diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 4af789de8..e71d3486d 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -41,13 +41,19 @@ False TRACE + + bin\NuGet-$(Configuration)\ + - + ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - + ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll + + ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll + 3.5 @@ -130,19 +136,26 @@ - - + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} Mono.Cecil + + {53DCA265-3C3C-42F9-B647-F72BA678122B} + ICSharpCode.NRefactory.CSharp + + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + - + diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 808290a49..8bb4934e7 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -49,12 +49,6 @@ ..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll - - ..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - - - ..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll - False .\nunit.framework.dll @@ -121,6 +115,14 @@ {D68133BD-1E63-496E-9EDE-4FBDBF77B486} Mono.Cecil + + {53DCA265-3C3C-42F9-B647-F72BA678122B} + ICSharpCode.NRefactory.CSharp + + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + {984CC812-9470-4A13-AFF9-CC44068D666C} ICSharpCode.Decompiler diff --git a/ICSharpCode.Decompiler/Tests/packages.config b/ICSharpCode.Decompiler/Tests/packages.config index b6522ccdb..4d992b424 100644 --- a/ICSharpCode.Decompiler/Tests/packages.config +++ b/ICSharpCode.Decompiler/Tests/packages.config @@ -1,6 +1,4 @@  - - \ No newline at end of file diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index 2e507061a..7c56424cb 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -57,12 +57,6 @@ False False - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll - False @@ -220,10 +214,18 @@ {63e6915c-7ea4-4d76-ab28-0d7191eea626} Mono.Cecil.Pdb + + {53dca265-3c3c-42f9-b647-f72ba678122b} + ICSharpCode.NRefactory.CSharp + {7b82b671-419f-45f4-b778-d9286f996efa} ICSharpCode.NRefactory.VB + + {3b2a5653-ec97-4001-bb9b-d90f1af2c371} + ICSharpCode.NRefactory + {dde2a481-8271-4eac-a330-8fa6a38d13d1} ICSharpCode.TreeView diff --git a/ILSpy.AddIn/packages.config b/ILSpy.AddIn/packages.config index 5f793afd7..14a098365 100644 --- a/ILSpy.AddIn/packages.config +++ b/ILSpy.AddIn/packages.config @@ -1,7 +1,5 @@  - - diff --git a/ILSpy.sln b/ILSpy.sln index 0d57ce0ae..7a69bb14f 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -1,7 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -# SharpDevelop 5.1 +# Visual Studio 2013 VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E72-4000-B5AD-3A7B485A0896}" @@ -19,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "A EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj", "{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler.Tests", "ICSharpCode.Decompiler\Tests\ICSharpCode.Decompiler.Tests.csproj", "{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{F32EBCC8-0E53-4421-867E-05B3D6E10C70}" @@ -31,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler", "ILS EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests", "ILSpy.BamlDecompiler\Tests\ILSpy.BamlDecompiler.Tests.csproj", "{1169E6D1-1899-43D4-A500-07CE4235B388}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory.CSharp", "NRefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj", "{53DCA265-3C3C-42F9-B647-F72BA678122B}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.AddIn", "ILSpy.AddIn\ILSpy.AddIn.csproj", "{9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0A344E19-D1FC-4F4C-8883-0844AC669113}" @@ -86,6 +89,14 @@ Global {984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.Build.0 = Release|Any CPU {984CC812-9470-4A13-AFF9-CC44068D666C}.Release|x86.ActiveCfg = Release|Any CPU {984CC812-9470-4A13-AFF9-CC44068D666C}.Release|x86.Build.0 = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|x86.ActiveCfg = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|x86.Build.0 = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.Build.0 = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|x86.ActiveCfg = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|x86.Build.0 = Release|Any CPU {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|Any CPU.Build.0 = Debug|Any CPU {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|x86.ActiveCfg = Debug|x86 @@ -134,6 +145,14 @@ Global {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.Build.0 = Release|Any CPU {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|x86.ActiveCfg = Release|x86 {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|x86.Build.0 = Release|x86 + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|x86.ActiveCfg = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|x86.Build.0 = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|Any CPU.Build.0 = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|x86.ActiveCfg = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|x86.Build.0 = Release|Any CPU {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Debug|x86.ActiveCfg = Debug|Any CPU diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 44c274807..46a29db40 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -55,15 +55,6 @@ TRACE - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.Cecil.dll - - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll - 3.0 @@ -262,7 +253,6 @@ - @@ -392,10 +382,18 @@ {63E6915C-7EA4-4D76-AB28-0D7191EEA626} Mono.Cecil.Pdb + + {53DCA265-3C3C-42F9-B647-F72BA678122B} + ICSharpCode.NRefactory.CSharp + {7B82B671-419F-45F4-B778-D9286F996EFA} ICSharpCode.NRefactory.VB + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + {DDE2A481-8271-4EAC-A330-8FA6A38D13D1} ICSharpCode.TreeView diff --git a/ILSpy/VB/ILSpyEnvironmentProvider.cs b/ILSpy/VB/ILSpyEnvironmentProvider.cs index 279000d78..78c46da12 100644 --- a/ILSpy/VB/ILSpyEnvironmentProvider.cs +++ b/ILSpy/VB/ILSpyEnvironmentProvider.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.ILSpy.VB } } - readonly CecilLoader loader = new CecilLoader(); + //readonly CecilLoader loader = new CecilLoader(); public string GetTypeNameForAttribute(ICSharpCode.NRefactory.CSharp.Attribute attribute) { diff --git a/ILSpy/packages.config b/ILSpy/packages.config deleted file mode 100644 index ade7de76c..000000000 --- a/ILSpy/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/NRefactory/.gitattributes b/NRefactory/.gitattributes index 1a1533024..fbfb41be8 100644 --- a/NRefactory/.gitattributes +++ b/NRefactory/.gitattributes @@ -1,2 +1,3 @@ -*.sln -crlf -*.csproj -crlf \ No newline at end of file +*.cs text diff=csharp +*.sln text eol=crlf +*.csproj text eol=crlf diff --git a/NRefactory/.gitignore b/NRefactory/.gitignore index 0031dcbe2..813e7d15c 100644 --- a/NRefactory/.gitignore +++ b/NRefactory/.gitignore @@ -1,5 +1,6 @@ bin obj +*.suo /lib/*.dll /ICSharpCode.NRefactory.Tests/PartCover/* _ReSharper*/* diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/AnnotationNames.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/AnnotationNames.cs new file mode 100644 index 000000000..94d19e4b6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/AnnotationNames.cs @@ -0,0 +1,48 @@ +// +// Annotations.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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 +{ + public static class AnnotationNames + { + //Used const instead of readonly to allow values to be used in switch cases. + + public const string AssertionMethodAttribute = "JetBrains.Annotations.AssertionMethodAttribute"; + public const string AssertionConditionAttribute = "JetBrains.Annotations.AssertionConditionAttribute"; + public const string AssertionConditionTypeAttribute = "JetBrains.Annotations.AssertionConditionType"; + + public const string AssertionConditionTypeIsTrue = "JetBrains.Annotations.AssertionConditionType.IS_TRUE"; + public const string AssertionConditionTypeIsFalse = "JetBrains.Annotations.AssertionConditionType.IS_FALSE"; + public const string AssertionConditionTypeIsNull = "JetBrains.Annotations.AssertionConditionType.IS_NULL"; + public const string AssertionConditionTypeIsNotNull = "JetBrains.Annotations.AssertionConditionType.IS_NOT_NULL"; + + public const string NotNullAttribute = "JetBrains.Annotations.NotNullAttribute"; + public const string CanBeNullAttribute = "JetBrains.Annotations.CanBeNullAttribute"; + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs new file mode 100644 index 000000000..2d3895b1e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs @@ -0,0 +1,803 @@ +// Copyright (c) 2010-2013 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.Diagnostics; +using System.Linq; +using System.Threading; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents a node in the control flow graph of a C# method. + /// + public class ControlFlowNode + { + public readonly Statement PreviousStatement; + public readonly Statement NextStatement; + + public readonly ControlFlowNodeType Type; + + public readonly List Outgoing = new List(); + public readonly List Incoming = new List(); + + public ControlFlowNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + if (previousStatement == null && nextStatement == null) + throw new ArgumentException("previousStatement and nextStatement must not be both null"); + this.PreviousStatement = previousStatement; + this.NextStatement = nextStatement; + this.Type = type; + } + } + + public enum ControlFlowNodeType + { + /// + /// Unknown node type + /// + None, + /// + /// Node in front of a statement + /// + StartNode, + /// + /// Node between two statements + /// + BetweenStatements, + /// + /// Node at the end of a statement list + /// + EndNode, + /// + /// Node representing the position before evaluating the condition of a loop. + /// + LoopCondition + } + + public class ControlFlowEdge + { + public readonly ControlFlowNode From; + public readonly ControlFlowNode To; + public readonly ControlFlowEdgeType Type; + + List jumpOutOfTryFinally; + + public ControlFlowEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type) + { + if (from == null) + throw new ArgumentNullException("from"); + if (to == null) + throw new ArgumentNullException("to"); + this.From = from; + this.To = to; + this.Type = type; + } + + internal void AddJumpOutOfTryFinally(TryCatchStatement tryFinally) + { + if (jumpOutOfTryFinally == null) + jumpOutOfTryFinally = new List(); + jumpOutOfTryFinally.Add(tryFinally); + } + + /// + /// Gets whether this control flow edge is leaving any try-finally statements. + /// + public bool IsLeavingTryFinally { + get { return jumpOutOfTryFinally != null; } + } + + /// + /// Gets the try-finally statements that this control flow edge is leaving. + /// + public IEnumerable TryFinallyStatements { + get { return jumpOutOfTryFinally ?? Enumerable.Empty(); } + } + } + + public enum ControlFlowEdgeType + { + /// + /// Regular control flow. + /// + Normal, + /// + /// Conditional control flow (edge taken if condition is true) + /// + ConditionTrue, + /// + /// Conditional control flow (edge taken if condition is false) + /// + ConditionFalse, + /// + /// A jump statement (goto, goto case, break or continue) + /// + Jump + } + + /// + /// Constructs the control flow graph for C# statements. + /// + public class ControlFlowGraphBuilder + { + // Written according to the reachability rules in the C# spec (§8.1 End points and reachability) + + protected virtual ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + cancellationToken.ThrowIfCancellationRequested(); + return new ControlFlowNode(previousStatement, nextStatement, type); + } + + protected virtual ControlFlowEdge CreateEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type) + { + cancellationToken.ThrowIfCancellationRequested(); + return new ControlFlowEdge(from, to, type); + } + + Statement rootStatement; + CSharpTypeResolveContext typeResolveContext; + Func resolver; + List nodes; + Dictionary labels; + List gotoStatements; + CancellationToken cancellationToken; + + public IList BuildControlFlowGraph(Statement statement, CancellationToken cancellationToken = default(CancellationToken)) + { + if (statement == null) + throw new ArgumentNullException("statement"); + CSharpResolver r = new CSharpResolver(MinimalCorlib.Instance.CreateCompilation()); + return BuildControlFlowGraph(statement, new CSharpAstResolver(r, statement), cancellationToken); + } + + public IList BuildControlFlowGraph(Statement statement, CSharpAstResolver resolver, CancellationToken cancellationToken = default(CancellationToken)) + { + if (statement == null) + throw new ArgumentNullException("statement"); + if (resolver == null) + throw new ArgumentNullException("resolver"); + return BuildControlFlowGraph(statement, resolver.Resolve, resolver.TypeResolveContext, cancellationToken); + } + + internal IList BuildControlFlowGraph(Statement statement, Func resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken) + { + NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor(); + nodeCreationVisitor.builder = this; + try { + this.nodes = new List(); + this.labels = new Dictionary(); + this.gotoStatements = new List(); + this.rootStatement = statement; + this.resolver = resolver; + this.typeResolveContext = typeResolveContext; + this.cancellationToken = cancellationToken; + + ControlFlowNode entryPoint = CreateStartNode(statement); + statement.AcceptVisitor(nodeCreationVisitor, entryPoint); + + // Resolve goto statements: + foreach (ControlFlowNode gotoStmt in gotoStatements) { + string label = ((GotoStatement)gotoStmt.NextStatement).Label; + ControlFlowNode labelNode; + if (labels.TryGetValue(label, out labelNode)) + nodeCreationVisitor.Connect(gotoStmt, labelNode, ControlFlowEdgeType.Jump); + } + + AnnotateLeaveEdgesWithTryFinallyBlocks(); + + return nodes; + } finally { + this.nodes = null; + this.labels = null; + this.gotoStatements = null; + this.rootStatement = null; + this.resolver = null; + this.typeResolveContext = null; + this.cancellationToken = CancellationToken.None; + } + } + + void AnnotateLeaveEdgesWithTryFinallyBlocks() + { + foreach (ControlFlowEdge edge in nodes.SelectMany(n => n.Outgoing)) { + if (edge.Type != ControlFlowEdgeType.Jump) { + // Only jumps are potential candidates for leaving try-finally blocks. + // Note that the regular edges leaving try or catch blocks are already annotated by the visitor. + continue; + } + Statement gotoStatement = edge.From.NextStatement; + Debug.Assert(gotoStatement is GotoStatement || gotoStatement is GotoDefaultStatement || gotoStatement is GotoCaseStatement || gotoStatement is BreakStatement || gotoStatement is ContinueStatement); + Statement targetStatement = edge.To.PreviousStatement ?? edge.To.NextStatement; + if (gotoStatement.Parent == targetStatement.Parent) + continue; + HashSet targetParentTryCatch = new HashSet(targetStatement.Ancestors.OfType()); + for (AstNode node = gotoStatement.Parent; node != null; node = node.Parent) { + TryCatchStatement leftTryCatch = node as TryCatchStatement; + if (leftTryCatch != null) { + if (targetParentTryCatch.Contains(leftTryCatch)) + break; + if (!leftTryCatch.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(leftTryCatch); + } + } + } + } + + #region Create*Node + ControlFlowNode CreateStartNode(Statement statement) + { + if (statement.IsNull) + return null; + ControlFlowNode node = CreateNode(null, statement, ControlFlowNodeType.StartNode); + nodes.Add(node); + return node; + } + + ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true) + { + ControlFlowNode node = CreateNode(null, statement, type); + if (addToNodeList) + nodes.Add(node); + return node; + } + + ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true) + { + Statement nextStatement; + if (statement == rootStatement) { + nextStatement = null; + } else { + // Find the next statement in the same role: + AstNode next = statement; + do { + next = next.NextSibling; + } while (next != null && next.Role != statement.Role); + nextStatement = next as Statement; + } + ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode; + ControlFlowNode node = CreateNode(statement, nextStatement, type); + if (addToNodeList) + nodes.Add(node); + return node; + } + #endregion + + #region Constant evaluation + /// + /// Gets/Sets whether to handle only primitive expressions as constants (no complex expressions like "a + b"). + /// + public bool EvaluateOnlyPrimitiveConstants { get; set; } + + /// + /// Evaluates an expression. + /// + /// The constant value of the expression; or null if the expression is not a constant. + ResolveResult EvaluateConstant(Expression expr) + { + if (expr.IsNull) + return null; + if (EvaluateOnlyPrimitiveConstants) { + if (!(expr is PrimitiveExpression || expr is NullReferenceExpression)) + return null; + } + return resolver(expr, cancellationToken); + } + + /// + /// Evaluates an expression. + /// + /// The value of the constant boolean expression; or null if the value is not a constant boolean expression. + bool? EvaluateCondition(Expression expr) + { + ResolveResult rr = EvaluateConstant(expr); + if (rr != null && rr.IsCompileTimeConstant) + return rr.ConstantValue as bool?; + else + return null; + } + + bool AreEqualConstants(ResolveResult c1, ResolveResult c2) + { + if (c1 == null || c2 == null || !c1.IsCompileTimeConstant || !c2.IsCompileTimeConstant) + return false; + CSharpResolver r = new CSharpResolver(typeResolveContext); + ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2); + return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true; + } + #endregion + + sealed class NodeCreationVisitor : DepthFirstAstVisitor + { + // 'data' parameter: input control flow node (start of statement being visited) + // Return value: result control flow node (end of statement being visited) + + internal ControlFlowGraphBuilder builder; + Stack breakTargets = new Stack(); + Stack continueTargets = new Stack(); + List gotoCaseOrDefault = new List(); + + internal ControlFlowEdge Connect(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type = ControlFlowEdgeType.Normal) + { + if (from == null || to == null) + return null; + ControlFlowEdge edge = builder.CreateEdge(from, to, type); + from.Outgoing.Add(edge); + to.Incoming.Add(edge); + return edge; + } + + /// + /// Creates an end node for stmt and connects from with the new node. + /// + ControlFlowNode CreateConnectedEndNode(Statement stmt, ControlFlowNode from) + { + ControlFlowNode newNode = builder.CreateEndNode(stmt); + Connect(from, newNode); + return newNode; + } + + protected override ControlFlowNode VisitChildren(AstNode node, ControlFlowNode data) + { + // We have overrides for all possible statements and should visit statements only. + throw new NotSupportedException(); + } + + public override ControlFlowNode VisitBlockStatement(BlockStatement blockStatement, ControlFlowNode data) + { + // C# 4.0 spec: §8.2 Blocks + ControlFlowNode childNode = HandleStatementList(blockStatement.Statements, data); + return CreateConnectedEndNode(blockStatement, childNode); + } + + ControlFlowNode HandleStatementList(AstNodeCollection statements, ControlFlowNode source) + { + ControlFlowNode childNode = null; + foreach (Statement stmt in statements) { + if (childNode == null) { + childNode = builder.CreateStartNode(stmt); + if (source != null) + Connect(source, childNode); + } + Debug.Assert(childNode.NextStatement == stmt); + childNode = stmt.AcceptVisitor(this, childNode); + Debug.Assert(childNode.PreviousStatement == stmt); + } + return childNode ?? source; + } + + public override ControlFlowNode VisitEmptyStatement(EmptyStatement emptyStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(emptyStatement, data); + } + + public override ControlFlowNode VisitLabelStatement(LabelStatement labelStatement, ControlFlowNode data) + { + ControlFlowNode end = CreateConnectedEndNode(labelStatement, data); + builder.labels[labelStatement.Label] = end; + return end; + } + + public override ControlFlowNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(variableDeclarationStatement, data); + } + + public override ControlFlowNode VisitExpressionStatement(ExpressionStatement expressionStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(expressionStatement, data); + } + + public override ControlFlowNode VisitIfElseStatement(IfElseStatement ifElseStatement, ControlFlowNode data) + { + bool? cond = builder.EvaluateCondition(ifElseStatement.Condition); + + ControlFlowNode trueBegin = builder.CreateStartNode(ifElseStatement.TrueStatement); + if (cond != false) + Connect(data, trueBegin, ControlFlowEdgeType.ConditionTrue); + ControlFlowNode trueEnd = ifElseStatement.TrueStatement.AcceptVisitor(this, trueBegin); + + ControlFlowNode falseBegin = builder.CreateStartNode(ifElseStatement.FalseStatement); + if (cond != true) + Connect(data, falseBegin, ControlFlowEdgeType.ConditionFalse); + ControlFlowNode falseEnd = ifElseStatement.FalseStatement.AcceptVisitor(this, falseBegin); + // (if no else statement exists, both falseBegin and falseEnd will be null) + + ControlFlowNode end = builder.CreateEndNode(ifElseStatement); + Connect(trueEnd, end); + if (falseEnd != null) { + Connect(falseEnd, end); + } else if (cond != true) { + Connect(data, end, ControlFlowEdgeType.ConditionFalse); + } + return end; + } + + public override ControlFlowNode VisitSwitchStatement(SwitchStatement switchStatement, ControlFlowNode data) + { + // First, figure out which switch section will get called (if the expression is constant): + ResolveResult constant = builder.EvaluateConstant(switchStatement.Expression); + SwitchSection defaultSection = null; + SwitchSection sectionMatchedByConstant = null; + foreach (SwitchSection section in switchStatement.SwitchSections) { + foreach (CaseLabel label in section.CaseLabels) { + if (label.Expression.IsNull) { + defaultSection = section; + } else if (constant != null && constant.IsCompileTimeConstant) { + ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); + if (builder.AreEqualConstants(constant, labelConstant)) + sectionMatchedByConstant = section; + } + } + } + if (constant != null && constant.IsCompileTimeConstant && sectionMatchedByConstant == null) + sectionMatchedByConstant = defaultSection; + + int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count; + List sectionStartNodes = new List(); + + ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false); + breakTargets.Push(end); + foreach (SwitchSection section in switchStatement.SwitchSections) { + int sectionStartNodeID = builder.nodes.Count; + if (constant == null || !constant.IsCompileTimeConstant || section == sectionMatchedByConstant) { + HandleStatementList(section.Statements, data); + } else { + // This section is unreachable: pass null to HandleStatementList. + HandleStatementList(section.Statements, null); + } + // Don't bother connecting the ends of the sections: the 'break' statement takes care of that. + + // Store the section start node for 'goto case' statements. + sectionStartNodes.Add(sectionStartNodeID < builder.nodes.Count ? builder.nodes[sectionStartNodeID] : null); + } + breakTargets.Pop(); + if (defaultSection == null && sectionMatchedByConstant == null) { + Connect(data, end); + } + + if (gotoCaseOrDefault.Count > gotoCaseOrDefaultInOuterScope) { + // Resolve 'goto case' statements: + for (int i = gotoCaseOrDefaultInOuterScope; i < gotoCaseOrDefault.Count; i++) { + ControlFlowNode gotoCaseNode = gotoCaseOrDefault[i]; + GotoCaseStatement gotoCaseStatement = gotoCaseNode.NextStatement as GotoCaseStatement; + ResolveResult gotoCaseConstant = null; + if (gotoCaseStatement != null) { + gotoCaseConstant = builder.EvaluateConstant(gotoCaseStatement.LabelExpression); + } + int targetSectionIndex = -1; + int currentSectionIndex = 0; + foreach (SwitchSection section in switchStatement.SwitchSections) { + foreach (CaseLabel label in section.CaseLabels) { + if (gotoCaseStatement != null) { + // goto case + if (!label.Expression.IsNull) { + ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); + if (builder.AreEqualConstants(gotoCaseConstant, labelConstant)) + targetSectionIndex = currentSectionIndex; + } + } else { + // goto default + if (label.Expression.IsNull) + targetSectionIndex = currentSectionIndex; + } + } + currentSectionIndex++; + } + if (targetSectionIndex >= 0 && sectionStartNodes[targetSectionIndex] != null) + Connect(gotoCaseNode, sectionStartNodes[targetSectionIndex], ControlFlowEdgeType.Jump); + else + Connect(gotoCaseNode, end, ControlFlowEdgeType.Jump); + } + gotoCaseOrDefault.RemoveRange(gotoCaseOrDefaultInOuterScope, gotoCaseOrDefault.Count - gotoCaseOrDefaultInOuterScope); + } + + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, ControlFlowNode data) + { + gotoCaseOrDefault.Add(data); + return builder.CreateEndNode(gotoCaseStatement); + } + + public override ControlFlowNode VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, ControlFlowNode data) + { + gotoCaseOrDefault.Add(data); + return builder.CreateEndNode(gotoDefaultStatement); + } + + public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data) + { + // while (cond) { embeddedStmt; } + ControlFlowNode end = builder.CreateEndNode(whileStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition); + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + Connect(data, conditionNode); + + bool? cond = builder.EvaluateCondition(whileStatement.Condition); + ControlFlowNode bodyStart = builder.CreateStartNode(whileStatement.EmbeddedStatement); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + ControlFlowNode bodyEnd = whileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, conditionNode); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + breakTargets.Pop(); + continueTargets.Pop(); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data) + { + // do { embeddedStmt; } while(cond); + ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false); + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + ControlFlowNode bodyStart = builder.CreateStartNode(doWhileStatement.EmbeddedStatement); + Connect(data, bodyStart); + ControlFlowNode bodyEnd = doWhileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, conditionNode); + + bool? cond = builder.EvaluateCondition(doWhileStatement.Condition); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + breakTargets.Pop(); + continueTargets.Pop(); + builder.nodes.Add(conditionNode); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitForStatement(ForStatement forStatement, ControlFlowNode data) + { + data = HandleStatementList(forStatement.Initializers, data); + // for (initializers ; cond; iterators) { embeddedStmt; } + ControlFlowNode end = builder.CreateEndNode(forStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition); + Connect(data, conditionNode); + + int iteratorStartNodeID = builder.nodes.Count; + ControlFlowNode iteratorEnd = HandleStatementList(forStatement.Iterators, null); + ControlFlowNode iteratorStart; + if (iteratorEnd != null) { + iteratorStart = builder.nodes[iteratorStartNodeID]; + Connect(iteratorEnd, conditionNode); + } else { + iteratorStart = conditionNode; + } + + breakTargets.Push(end); + continueTargets.Push(iteratorStart); + + ControlFlowNode bodyStart = builder.CreateStartNode(forStatement.EmbeddedStatement); + ControlFlowNode bodyEnd = forStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, iteratorStart); + + breakTargets.Pop(); + continueTargets.Pop(); + + bool? cond = forStatement.Condition.IsNull ? true : builder.EvaluateCondition(forStatement.Condition); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + builder.nodes.Add(end); + return end; + } + + ControlFlowNode HandleEmbeddedStatement(Statement embeddedStatement, ControlFlowNode source) + { + if (embeddedStatement == null || embeddedStatement.IsNull) + return source; + ControlFlowNode bodyStart = builder.CreateStartNode(embeddedStatement); + if (source != null) + Connect(source, bodyStart); + return embeddedStatement.AcceptVisitor(this, bodyStart); + } + + public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data) + { + // foreach (...) { embeddedStmt } + ControlFlowNode end = builder.CreateEndNode(foreachStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition); + Connect(data, conditionNode); + + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + ControlFlowNode bodyEnd = HandleEmbeddedStatement(foreachStatement.EmbeddedStatement, conditionNode); + Connect(bodyEnd, conditionNode); + + breakTargets.Pop(); + continueTargets.Pop(); + + Connect(conditionNode, end); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitBreakStatement(BreakStatement breakStatement, ControlFlowNode data) + { + if (breakTargets.Count > 0) + Connect(data, breakTargets.Peek(), ControlFlowEdgeType.Jump); + return builder.CreateEndNode(breakStatement); + } + + public override ControlFlowNode VisitContinueStatement(ContinueStatement continueStatement, ControlFlowNode data) + { + if (continueTargets.Count > 0) + Connect(data, continueTargets.Peek(), ControlFlowEdgeType.Jump); + return builder.CreateEndNode(continueStatement); + } + + public override ControlFlowNode VisitGotoStatement(GotoStatement gotoStatement, ControlFlowNode data) + { + builder.gotoStatements.Add(data); + return builder.CreateEndNode(gotoStatement); + } + + public override ControlFlowNode VisitReturnStatement(ReturnStatement returnStatement, ControlFlowNode data) + { + return builder.CreateEndNode(returnStatement); // end not connected with data + } + + public override ControlFlowNode VisitThrowStatement(ThrowStatement throwStatement, ControlFlowNode data) + { + return builder.CreateEndNode(throwStatement); // end not connected with data + } + + public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data) + { + ControlFlowNode end = builder.CreateEndNode(tryCatchStatement, addToNodeList: false); + var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end); + if (!tryCatchStatement.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(tryCatchStatement); + foreach (CatchClause cc in tryCatchStatement.CatchClauses) { + edge = Connect(HandleEmbeddedStatement(cc.Body, data), end); + if (!tryCatchStatement.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(tryCatchStatement); + } + if (!tryCatchStatement.FinallyBlock.IsNull) { + // Don't connect the end of the try-finally block to anything. + // Consumers of the CFG will have to special-case try-finally. + HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data); + } + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitCheckedStatement(CheckedStatement checkedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(checkedStatement.Body, data); + return CreateConnectedEndNode(checkedStatement, bodyEnd); + } + + public override ControlFlowNode VisitUncheckedStatement(UncheckedStatement uncheckedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(uncheckedStatement.Body, data); + return CreateConnectedEndNode(uncheckedStatement, bodyEnd); + } + + public override ControlFlowNode VisitLockStatement(LockStatement lockStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(lockStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(lockStatement, bodyEnd); + } + + public override ControlFlowNode VisitUsingStatement(UsingStatement usingStatement, ControlFlowNode data) + { + data = HandleEmbeddedStatement(usingStatement.ResourceAcquisition as Statement, data); + ControlFlowNode bodyEnd = HandleEmbeddedStatement(usingStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(usingStatement, bodyEnd); + } + + public override ControlFlowNode VisitYieldReturnStatement(YieldReturnStatement yieldStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(yieldStatement, data); + } + + public override ControlFlowNode VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, ControlFlowNode data) + { + return builder.CreateEndNode(yieldBreakStatement); // end not connected with data + } + + public override ControlFlowNode VisitUnsafeStatement(UnsafeStatement unsafeStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(unsafeStatement.Body, data); + return CreateConnectedEndNode(unsafeStatement, bodyEnd); + } + + public override ControlFlowNode VisitFixedStatement(FixedStatement fixedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(fixedStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(fixedStatement, bodyEnd); + } + } + + /// + /// Debugging helper that exports a control flow graph. + /// + public static GraphVizGraph ExportGraph(IList nodes) + { + GraphVizGraph g = new GraphVizGraph(); + GraphVizNode[] n = new GraphVizNode[nodes.Count]; + Dictionary dict = new Dictionary(); + for (int i = 0; i < n.Length; i++) { + dict.Add(nodes[i], i); + n[i] = new GraphVizNode(i); + string name = "#" + i + " = "; + switch (nodes[i].Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + name += nodes[i].NextStatement.DebugToString(); + break; + case ControlFlowNodeType.EndNode: + name += "End of " + nodes[i].PreviousStatement.DebugToString(); + break; + case ControlFlowNodeType.LoopCondition: + name += "Condition in " + nodes[i].NextStatement.DebugToString(); + break; + default: + name += "?"; + break; + } + n[i].label = name; + g.AddNode(n[i]); + } + for (int i = 0; i < n.Length; i++) { + foreach (ControlFlowEdge edge in nodes[i].Outgoing) { + GraphVizEdge ge = new GraphVizEdge(i, dict[edge.To]); + if (edge.IsLeavingTryFinally) + ge.style = "dashed"; + switch (edge.Type) { + case ControlFlowEdgeType.ConditionTrue: + ge.color = "green"; + break; + case ControlFlowEdgeType.ConditionFalse: + ge.color = "red"; + break; + case ControlFlowEdgeType.Jump: + ge.color = "blue"; + break; + } + g.AddEdge(ge); + } + } + return g; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs new file mode 100644 index 000000000..a412cba3a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs @@ -0,0 +1,157 @@ +// +// LovalVariableDeclarationSpace.cs +// +// Author: +// Simon Lindgren +// +// Copyright (c) 2013 Simon Lindgren +// +// 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 ICSharpCode.NRefactory.Utils; +using System.Collections.Generic; +using System.Linq; +using System; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents a declaration space. (§3.3) + /// + public class LocalDeclarationSpace + { + /// + /// Maps from variable name to the declarations in this declaration space. + /// + /// + /// This maps from variable name + /// + MultiDictionary declarations = new MultiDictionary (); + + public LocalDeclarationSpace() + { + Children = new List (); + } + + /// + /// The child declaration spaces. + /// + public IList Children { + get; + private set; + } + + /// + /// The parent declaration space. + /// + /// The parent. + public LocalDeclarationSpace Parent { + get; + private set; + } + + /// + /// The names declared in this declaration space, excluding child spaces. + /// + /// The declared names. + public ICollection DeclaredNames { + get { + return declarations.Keys; + } + } + + /// + /// Get all nodes declaring the name specified in . + /// + /// The declaring nodes. + /// The declaration name. + public IEnumerable GetNameDeclarations(string name) + { + return declarations [name].Concat(Children.SelectMany(child => child.GetNameDeclarations(name))); + } + + /// + /// Adds a child declaration space. + /// + /// The to add. + public void AddChildSpace(LocalDeclarationSpace child) + { + if (child == null) + throw new ArgumentNullException("child"); + if (Children.Contains(child)) + throw new InvalidOperationException("the child was already added"); + + Children.Add(child); + child.Parent = this; + } + + /// + /// Adds a new declaration to the declaration space. + /// + /// The name of the declared variable. + /// A node associated with the declaration. + public void AddDeclaration(string name, AstNode node) + { + if (name == null) + throw new ArgumentNullException("name"); + if (node == null) + throw new ArgumentNullException("node"); + declarations.Add(name, node); + } + + /// + /// Determines if the name exists in the this declaration space. + /// + /// true, if the name specified in is used in this variable declaration space, false otherwise. + /// The name to look for. + /// When true, child declaration spaces are included in the search. + public bool ContainsName(string name, bool includeChildren) + { + if (name == null) + throw new ArgumentNullException("name"); + + if (declarations.Keys.Contains(name)) + return true; + return includeChildren && Children.Any(child => child.ContainsName(name, true)); + } + + /// + /// Determines whether the name specified in is used in surrouding code. + /// + /// true if the name is used, false otherwise. + /// The name to check. + /// + /// Contrary to , this method also checks parent declaration spaces + /// for name conflicts. Typically, this will be the right method to use when determining if a name can be used. + /// + public bool IsNameUsed(string name) + { + if (name == null) + throw new ArgumentNullException("name"); + + return IsNameUsedBySelfOrParent(name) || Children.Any(child => child.ContainsName(name, true)); + } + + bool IsNameUsedBySelfOrParent(string name) + { + if (declarations.Keys.Contains(name)) + return true; + return Parent != null && Parent.IsNameUsedBySelfOrParent(name); + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs new file mode 100644 index 000000000..08389a413 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs @@ -0,0 +1,138 @@ +// +// LocalDeclarationSpaceVisitor.cs +// +// Author: +// Simon Lindgren +// +// Copyright (c) 2013 Simon Lindgren +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + public class LocalDeclarationSpaceVisitor : DepthFirstAstVisitor + { + LocalDeclarationSpace currentDeclarationSpace; + Dictionary nodeDeclarationSpaces = new Dictionary(); + + public LocalDeclarationSpace GetDeclarationSpace(AstNode node) + { + if (node == null) + throw new ArgumentNullException("node"); + while (node != null) { + LocalDeclarationSpace declarationSpace; + if (nodeDeclarationSpaces.TryGetValue(node, out declarationSpace)) + return declarationSpace; + node = node.Parent; + } + return null; + } + + #region Visitor + + void AddDeclaration(string name, AstNode node) + { + if (currentDeclarationSpace != null) + currentDeclarationSpace.AddDeclaration(name, node); + } + + public override void VisitVariableInitializer(VariableInitializer variableInitializer) + { + AddDeclaration(variableInitializer.Name, variableInitializer); + base.VisitVariableInitializer(variableInitializer); + } + + public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + AddDeclaration(parameterDeclaration.Name, parameterDeclaration); + base.VisitParameterDeclaration(parameterDeclaration); + } + + void VisitNewDeclarationSpace(AstNode node) + { + var oldDeclarationSpace = currentDeclarationSpace; + currentDeclarationSpace = new LocalDeclarationSpace(); + if (oldDeclarationSpace != null) + oldDeclarationSpace.AddChildSpace(currentDeclarationSpace); + + VisitChildren(node); + + nodeDeclarationSpaces.Add(node, currentDeclarationSpace); + currentDeclarationSpace = oldDeclarationSpace; + } + + #region Declaration space creating nodes + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + VisitNewDeclarationSpace(methodDeclaration); + } + + public override void VisitBlockStatement(BlockStatement blockStatement) + { + VisitNewDeclarationSpace(blockStatement); + } + + public override void VisitSwitchStatement(SwitchStatement switchStatement) + { + VisitNewDeclarationSpace(switchStatement); + } + + public override void VisitForeachStatement(ForeachStatement foreachStatement) + { + AddDeclaration(foreachStatement.VariableName, foreachStatement); + VisitNewDeclarationSpace(foreachStatement); + } + + public override void VisitForStatement(ForStatement forStatement) + { + VisitNewDeclarationSpace(forStatement); + } + + public override void VisitUsingStatement(UsingStatement usingStatement) + { + VisitNewDeclarationSpace(usingStatement); + } + + public override void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + VisitNewDeclarationSpace(lambdaExpression); + } + + public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + VisitNewDeclarationSpace(anonymousMethodExpression); + } + + public override void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + AddDeclaration(eventDeclaration.Name, eventDeclaration); + } + + public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + VisitNewDeclarationSpace(eventDeclaration); + } + + #endregion + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs new file mode 100644 index 000000000..9b33e74a7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs @@ -0,0 +1,759 @@ +// Copyright (c) 2010-2013 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.Diagnostics; +using System.Linq; +using System.Threading; + +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.Analysis +{ + /// + /// Represents the definite assignment status of a variable at a specific location. + /// + public enum DefiniteAssignmentStatus + { + /// + /// The variable might be assigned or unassigned. + /// + PotentiallyAssigned, + /// + /// The variable is definitely assigned. + /// + DefinitelyAssigned, + /// + /// The variable is definitely assigned iff the expression results in the value 'true'. + /// + AssignedAfterTrueExpression, + /// + /// The variable is definitely assigned iff the expression results in the value 'false'. + /// + AssignedAfterFalseExpression, + /// + /// The code is unreachable. + /// + CodeUnreachable + } + + /// + /// Implements the C# definite assignment analysis (C# 4.0 Spec: §5.3 Definite assignment) + /// + public class DefiniteAssignmentAnalysis + { + sealed class DefiniteAssignmentNode : ControlFlowNode + { + public int Index; + public DefiniteAssignmentStatus NodeStatus; + + public DefiniteAssignmentNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + : base(previousStatement, nextStatement, type) + { + } + } + + sealed class DerivedControlFlowGraphBuilder : ControlFlowGraphBuilder + { + protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + return new DefiniteAssignmentNode(previousStatement, nextStatement, type); + } + } + + readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor(); + readonly List allNodes = new List(); + readonly Dictionary beginNodeDict = new Dictionary(); + readonly Dictionary endNodeDict = new Dictionary(); + readonly Dictionary conditionNodeDict = new Dictionary(); + readonly CSharpAstResolver resolver; + Dictionary edgeStatus = new Dictionary(); + + string variableName; + List unassignedVariableUses = new List(); + int analyzedRangeStart, analyzedRangeEnd; + CancellationToken analysisCancellationToken; + + Queue nodesWithModifiedInput = new Queue(); + + public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken) + : this(rootStatement, + new CSharpAstResolver(new CSharpResolver(MinimalCorlib.Instance.CreateCompilation()), rootStatement), + cancellationToken) + { + } + + public DefiniteAssignmentAnalysis(Statement rootStatement, CSharpAstResolver resolver, CancellationToken cancellationToken) + { + if (rootStatement == null) + throw new ArgumentNullException("rootStatement"); + if (resolver == null) + throw new ArgumentNullException("resolver"); + this.resolver = resolver; + + visitor.analysis = this; + DerivedControlFlowGraphBuilder cfgBuilder = new DerivedControlFlowGraphBuilder(); + if (resolver.TypeResolveContext.Compilation.MainAssembly.UnresolvedAssembly is MinimalCorlib) { + cfgBuilder.EvaluateOnlyPrimitiveConstants = true; + } + allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolver, cancellationToken).Cast()); + for (int i = 0; i < allNodes.Count; i++) { + DefiniteAssignmentNode node = allNodes[i]; + node.Index = i; // assign numbers to the nodes + if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) { + // Anonymous methods have separate control flow graphs, but we also need to analyze those. + // Iterate backwards so that anonymous methods are inserted in the correct order + for (AstNode child = node.NextStatement.LastChild; child != null; child = child.PrevSibling) { + InsertAnonymousMethods(i + 1, child, cfgBuilder, cancellationToken); + } + } + // Now register the node in the dictionaries: + if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) + beginNodeDict.Add(node.NextStatement, node); + if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode) + endNodeDict.Add(node.PreviousStatement, node); + if (node.Type == ControlFlowNodeType.LoopCondition) + conditionNodeDict.Add(node.NextStatement, node); + } + // Verify that we created nodes for all statements: + Debug.Assert(!rootStatement.DescendantsAndSelf.OfType().Except(allNodes.Select(n => n.NextStatement)).Any()); + // Verify that we put all nodes into the dictionaries: + Debug.Assert(rootStatement.DescendantsAndSelf.OfType().All(stmt => beginNodeDict.ContainsKey(stmt))); + Debug.Assert(rootStatement.DescendantsAndSelf.OfType().All(stmt => endNodeDict.ContainsKey(stmt))); + + this.analyzedRangeStart = 0; + this.analyzedRangeEnd = allNodes.Count - 1; + } + + void InsertAnonymousMethods(int insertPos, AstNode node, ControlFlowGraphBuilder cfgBuilder, CancellationToken cancellationToken) + { + // Ignore any statements, as those have their own ControlFlowNode and get handled separately + if (node is Statement) + return; + AnonymousMethodExpression ame = node as AnonymousMethodExpression; + if (ame != null) { + allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph(ame.Body, resolver, cancellationToken).Cast()); + return; + } + LambdaExpression lambda = node as LambdaExpression; + if (lambda != null && lambda.Body is Statement) { + allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolver, cancellationToken).Cast()); + return; + } + // Descend into child expressions + // Iterate backwards so that anonymous methods are inserted in the correct order + for (AstNode child = node.LastChild; child != null; child = child.PrevSibling) { + InsertAnonymousMethods(insertPos, child, cfgBuilder, cancellationToken); + } + } + + /// + /// Gets the unassigned usages of the previously analyzed variable. + /// + public IList UnassignedVariableUses { + get { + return unassignedVariableUses.AsReadOnly(); + } + } + + /// + /// Sets the range of statements to be analyzed. + /// This method can be used to restrict the analysis to only a part of the method. + /// Only the control flow paths that are fully contained within the selected part will be analyzed. + /// + /// By default, both 'start' and 'end' are inclusive. + public void SetAnalyzedRange(Statement start, Statement end, bool startInclusive = true, bool endInclusive = true) + { + var dictForStart = startInclusive ? beginNodeDict : endNodeDict; + var dictForEnd = endInclusive ? endNodeDict : beginNodeDict; + Debug.Assert(dictForStart.ContainsKey(start) && dictForEnd.ContainsKey(end)); + int startIndex = dictForStart[start].Index; + int endIndex = dictForEnd[end].Index; + if (startIndex > endIndex) + throw new ArgumentException("The start statement must be lexically preceding the end statement"); + this.analyzedRangeStart = startIndex; + this.analyzedRangeEnd = endIndex; + } + + public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned, CancellationToken cancellationToken = default(CancellationToken)) + { + this.analysisCancellationToken = cancellationToken; + this.variableName = variable; + try { + // Reset the status: + unassignedVariableUses.Clear(); + foreach (DefiniteAssignmentNode node in allNodes) { + node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable; + foreach (ControlFlowEdge edge in node.Outgoing) + edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable; + } + + ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus); + // Iterate as long as the input status of some nodes is changing: + while (nodesWithModifiedInput.Count > 0) { + DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue(); + DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable; + foreach (ControlFlowEdge edge in node.Incoming) { + inputStatus = MergeStatus(inputStatus, edgeStatus[edge]); + } + ChangeNodeStatus(node, inputStatus); + } + } finally { + this.analysisCancellationToken = CancellationToken.None; + this.variableName = null; + } + } + + public DefiniteAssignmentStatus GetStatusBefore(Statement statement) + { + return beginNodeDict[statement].NodeStatus; + } + + public DefiniteAssignmentStatus GetStatusAfter(Statement statement) + { + return endNodeDict[statement].NodeStatus; + } + + public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement) + { + return conditionNodeDict[statement].NodeStatus; + } + + /// + /// Exports the CFG. This method is intended to help debugging issues related to definite assignment. + /// + public GraphVizGraph ExportGraph() + { + GraphVizGraph g = new GraphVizGraph(); + g.Title = "DefiniteAssignment - " + variableName; + for (int i = 0; i < allNodes.Count; i++) { + string name = "#" + i + " = " + allNodes[i].NodeStatus.ToString() + Environment.NewLine; + switch (allNodes[i].Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + name += allNodes[i].NextStatement.ToString(); + break; + case ControlFlowNodeType.EndNode: + name += "End of " + allNodes[i].PreviousStatement.ToString(); + break; + case ControlFlowNodeType.LoopCondition: + name += "Condition in " + allNodes[i].NextStatement.ToString(); + break; + default: + name += allNodes[i].Type.ToString(); + break; + } + g.AddNode(new GraphVizNode(i) { label = name }); + foreach (ControlFlowEdge edge in allNodes[i].Outgoing) { + GraphVizEdge ge = new GraphVizEdge(i, ((DefiniteAssignmentNode)edge.To).Index); + if (edgeStatus.Count > 0) + ge.label = edgeStatus[edge].ToString(); + if (edge.IsLeavingTryFinally) + ge.style = "dashed"; + switch (edge.Type) { + case ControlFlowEdgeType.ConditionTrue: + ge.color = "green"; + break; + case ControlFlowEdgeType.ConditionFalse: + ge.color = "red"; + break; + case ControlFlowEdgeType.Jump: + ge.color = "blue"; + break; + } + g.AddEdge(ge); + } + } + return g; + } + + static DefiniteAssignmentStatus MergeStatus(DefiniteAssignmentStatus a, DefiniteAssignmentStatus b) + { + // The result will be DefinitelyAssigned if at least one incoming edge is DefinitelyAssigned and all others are unreachable. + // The result will be DefinitelyUnassigned if at least one incoming edge is DefinitelyUnassigned and all others are unreachable. + // The result will be Unreachable if all incoming edges are unreachable. + // Otherwise, the result will be PotentiallyAssigned. + + if (a == b) + return a; + else if (a == DefiniteAssignmentStatus.CodeUnreachable) + return b; + else if (b == DefiniteAssignmentStatus.CodeUnreachable) + return a; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } + + void ChangeNodeStatus (DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus) + { + if (node.NodeStatus == inputStatus) + return; + node.NodeStatus = inputStatus; + DefiniteAssignmentStatus outputStatus; + switch (node.Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + if (node.NextStatement is IfElseStatement) { + // Handle if-else as a condition node + goto case ControlFlowNodeType.LoopCondition; + } + if (inputStatus == DefiniteAssignmentStatus.DefinitelyAssigned) { + // There isn't any way to un-assign variables, so we don't have to check the expression + // if the status already is definitely assigned. + outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + outputStatus = CleanSpecialValues (node.NextStatement.AcceptVisitor (visitor, inputStatus)); + } + break; + case ControlFlowNodeType.EndNode: + outputStatus = inputStatus; + if (node.PreviousStatement.Role == TryCatchStatement.FinallyBlockRole + && (outputStatus == DefiniteAssignmentStatus.DefinitelyAssigned || outputStatus == DefiniteAssignmentStatus.PotentiallyAssigned)) { + TryCatchStatement tryFinally = (TryCatchStatement)node.PreviousStatement.Parent; + // Changing the status on a finally block potentially changes the status of all edges leaving that finally block: + foreach (ControlFlowEdge edge in allNodes.SelectMany(n => n.Outgoing)) { + if (edge.IsLeavingTryFinally && edge.TryFinallyStatements.Contains (tryFinally)) { + DefiniteAssignmentStatus s = edgeStatus [edge]; + if (s == DefiniteAssignmentStatus.PotentiallyAssigned) { + ChangeEdgeStatus (edge, outputStatus); + } + } + } + } + break; + case ControlFlowNodeType.LoopCondition: + ForeachStatement foreachStmt = node.NextStatement as ForeachStatement; + if (foreachStmt != null) { + outputStatus = CleanSpecialValues (foreachStmt.InExpression.AcceptVisitor (visitor, inputStatus)); + if (foreachStmt.VariableName == this.variableName) + outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned; + break; + } else { + Debug.Assert (node.NextStatement is IfElseStatement || node.NextStatement is WhileStatement || node.NextStatement is ForStatement || node.NextStatement is DoWhileStatement); + Expression condition = node.NextStatement.GetChildByRole (Roles.Condition); + if (condition.IsNull) + outputStatus = inputStatus; + else + outputStatus = condition.AcceptVisitor(visitor, inputStatus); + foreach (ControlFlowEdge edge in node.Outgoing) { + if (edge.Type == ControlFlowEdgeType.ConditionTrue && outputStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { + ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned); + } else if (edge.Type == ControlFlowEdgeType.ConditionFalse && outputStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression) { + ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned); + } else { + ChangeEdgeStatus(edge, CleanSpecialValues(outputStatus)); + } + } + return; + } + default: + throw new InvalidOperationException(); + } + foreach (ControlFlowEdge edge in node.Outgoing) { + ChangeEdgeStatus(edge, outputStatus); + } + } + + void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus) + { + DefiniteAssignmentStatus oldStatus = edgeStatus[edge]; + if (oldStatus == newStatus) + return; + // Ensure that status can cannot change back to CodeUnreachable after it once was reachable. + // Also, don't ever use AssignedAfter... for statements. + if (newStatus == DefiniteAssignmentStatus.CodeUnreachable + || newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression + || newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + { + throw new InvalidOperationException(); + } + // Note that the status can change from DefinitelyAssigned + // back to PotentiallyAssigned as unreachable input edges are + // discovered to be reachable. + + edgeStatus[edge] = newStatus; + DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To; + if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd) { + // TODO: potential optimization: visit previously unreachable nodes with higher priority + // (e.g. use Deque and enqueue previously unreachable nodes at the front, but + // other nodes at the end) + nodesWithModifiedInput.Enqueue(targetNode); + } + } + + /// + /// Evaluates an expression. + /// + /// The constant value of the expression; or null if the expression is not a constant. + ResolveResult EvaluateConstant(Expression expr) + { + return resolver.Resolve(expr, analysisCancellationToken); + } + + /// + /// Evaluates an expression. + /// + /// The value of the constant boolean expression; or null if the value is not a constant boolean expression. + bool? EvaluateCondition(Expression expr) + { + ResolveResult rr = EvaluateConstant(expr); + if (rr != null && rr.IsCompileTimeConstant) + return rr.ConstantValue as bool?; + else + return null; + } + + static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status) + { + if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.PotentiallyAssigned; + else if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.PotentiallyAssigned; + else + return status; + } + + sealed class DefiniteAssignmentVisitor : DepthFirstAstVisitor + { + internal DefiniteAssignmentAnalysis analysis; + + // The general approach for unknown nodes is to pass the status through all child nodes in order + protected override DefiniteAssignmentStatus VisitChildren(AstNode node, DefiniteAssignmentStatus data) + { + // the special values are valid as output only, not as input + Debug.Assert(data == CleanSpecialValues(data)); + DefiniteAssignmentStatus status = data; + for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + analysis.analysisCancellationToken.ThrowIfCancellationRequested(); + + Debug.Assert(!(child is Statement)); // statements are visited with the CFG, not with the visitor pattern + status = child.AcceptVisitor(this, status); + status = CleanSpecialValues(status); + } + return status; + } + + #region Statements + // For statements, the visitor only describes the effect of the statement itself; + // we do not consider the effect of any nested statements. + // This is done because the nested statements will be reached using the control flow graph. + + // In fact, these methods are present so that the default logic in VisitChildren does not try to visit the nested statements. + + public override DefiniteAssignmentStatus VisitBlockStatement(BlockStatement blockStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitCheckedStatement(CheckedStatement checkedStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitUncheckedStatement(UncheckedStatement uncheckedStatement, DefiniteAssignmentStatus data) + { + return data; + } + + // ExpressionStatement handled by default logic + // VariableDeclarationStatement handled by default logic + + public override DefiniteAssignmentStatus VisitVariableInitializer(VariableInitializer variableInitializer, DefiniteAssignmentStatus data) + { + if (variableInitializer.Initializer.IsNull) { + return data; + } else { + DefiniteAssignmentStatus status = variableInitializer.Initializer.AcceptVisitor(this, data); + if (variableInitializer.Name == analysis.variableName) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else + return status; + } + } + + // IfStatement not handled by visitor, but special-cased in the code consuming the control flow graph + + public override DefiniteAssignmentStatus VisitSwitchStatement(SwitchStatement switchStatement, DefiniteAssignmentStatus data) + { + return switchStatement.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitWhileStatement(WhileStatement whileStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node + } + + public override DefiniteAssignmentStatus VisitDoWhileStatement(DoWhileStatement doWhileStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node + } + + public override DefiniteAssignmentStatus VisitForStatement(ForStatement forStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node; initializer and iterator statements are handled by CFG + } + + // Break/Continue/Goto: handled by default logic + + // ThrowStatement: handled by default logic (just visit the expression) + // ReturnStatement: handled by default logic (just visit the expression) + + public override DefiniteAssignmentStatus VisitTryCatchStatement(TryCatchStatement tryCatchStatement, DefiniteAssignmentStatus data) + { + return data; // no special logic when entering the try-catch-finally statement + // TODO: where to put the special logic when exiting the try-finally statement? + } + + public override DefiniteAssignmentStatus VisitForeachStatement(ForeachStatement foreachStatement, DefiniteAssignmentStatus data) + { + return data; // assignment of the foreach loop variable is done when handling the condition node + } + + public override DefiniteAssignmentStatus VisitUsingStatement(UsingStatement usingStatement, DefiniteAssignmentStatus data) + { + if (usingStatement.ResourceAcquisition is Expression) + return usingStatement.ResourceAcquisition.AcceptVisitor(this, data); + else + return data; // don't handle resource acquisition statements, as those are connected in the control flow graph + } + + public override DefiniteAssignmentStatus VisitLockStatement(LockStatement lockStatement, DefiniteAssignmentStatus data) + { + return lockStatement.Expression.AcceptVisitor(this, data); + } + + // Yield statements use the default logic + + public override DefiniteAssignmentStatus VisitUnsafeStatement(UnsafeStatement unsafeStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitFixedStatement(FixedStatement fixedStatement, DefiniteAssignmentStatus data) + { + DefiniteAssignmentStatus status = data; + foreach (var variable in fixedStatement.Variables) + status = variable.AcceptVisitor(this, status); + return status; + } + #endregion + + #region Expressions + public override DefiniteAssignmentStatus VisitDirectionExpression(DirectionExpression directionExpression, DefiniteAssignmentStatus data) + { + if (directionExpression.FieldDirection == FieldDirection.Out) { + return HandleAssignment(directionExpression.Expression, null, data); + } else { + // use default logic for 'ref' + return VisitChildren(directionExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitAssignmentExpression(AssignmentExpression assignmentExpression, DefiniteAssignmentStatus data) + { + if (assignmentExpression.Operator == AssignmentOperatorType.Assign) { + return HandleAssignment(assignmentExpression.Left, assignmentExpression.Right, data); + } else { + // use default logic for compound assignment operators + return VisitChildren(assignmentExpression, data); + } + } + + DefiniteAssignmentStatus HandleAssignment(Expression left, Expression right, DefiniteAssignmentStatus initialStatus) + { + IdentifierExpression ident = left as IdentifierExpression; + if (ident != null && ident.Identifier == analysis.variableName) { + // right==null is special case when handling 'out' expressions + if (right != null) + right.AcceptVisitor(this, initialStatus); + return DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus); + if (right != null) + status = right.AcceptVisitor(this, CleanSpecialValues(status)); + return CleanSpecialValues(status); + } + } + + public override DefiniteAssignmentStatus VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, DefiniteAssignmentStatus data) + { + // Don't use the default logic here because we don't want to clean up the special values. + return parenthesizedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitCheckedExpression(CheckedExpression checkedExpression, DefiniteAssignmentStatus data) + { + return checkedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitUncheckedExpression(UncheckedExpression uncheckedExpression, DefiniteAssignmentStatus data) + { + return uncheckedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, DefiniteAssignmentStatus data) + { + if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalAnd) { + // Handle constant left side of && expressions (not in the C# spec, but done by the MS compiler) + bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left); + if (cond == true) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally + else if (cond == false) + return data; // right operand never gets evaluated + // C# 4.0 spec: §5.3.3.24 Definite Assignment for && expressions + DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data); + DefiniteAssignmentStatus beforeRight; + if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned; + else + beforeRight = afterLeft; + DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight); + if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalOr) { + // C# 4.0 spec: §5.3.3.25 Definite Assignment for || expressions + bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left); + if (cond == false) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally + else if (cond == true) + return data; // right operand never gets evaluated + DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data); + DefiniteAssignmentStatus beforeRight; + if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned; + else + beforeRight = afterLeft; + DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight); + if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { + // C# 4.0 spec: §5.3.3.27 Definite assignment for ?? expressions + ResolveResult crr = analysis.EvaluateConstant(binaryOperatorExpression.Left); + if (crr != null && crr.IsCompileTimeConstant && crr.ConstantValue == null) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); + DefiniteAssignmentStatus status = CleanSpecialValues(binaryOperatorExpression.Left.AcceptVisitor(this, data)); + binaryOperatorExpression.Right.AcceptVisitor(this, status); + return status; + } else { + // use default logic for other operators + return VisitChildren(binaryOperatorExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, DefiniteAssignmentStatus data) + { + if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) { + // C# 4.0 spec: §5.3.3.26 Definite assignment for ! expressions + DefiniteAssignmentStatus status = unaryOperatorExpression.Expression.AcceptVisitor(this, data); + if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else + return status; + } else { + // use default logic for other operators + return VisitChildren(unaryOperatorExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitConditionalExpression(ConditionalExpression conditionalExpression, DefiniteAssignmentStatus data) + { + // C# 4.0 spec: §5.3.3.28 Definite assignment for ?: expressions + bool? cond = analysis.EvaluateCondition(conditionalExpression.Condition); + if (cond == true) { + return conditionalExpression.TrueExpression.AcceptVisitor(this, data); + } else if (cond == false) { + return conditionalExpression.FalseExpression.AcceptVisitor(this, data); + } else { + DefiniteAssignmentStatus afterCondition = conditionalExpression.Condition.AcceptVisitor(this, data); + + DefiniteAssignmentStatus beforeTrue, beforeFalse; + if (afterCondition == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { + beforeTrue = DefiniteAssignmentStatus.DefinitelyAssigned; + beforeFalse = DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (afterCondition == DefiniteAssignmentStatus.AssignedAfterFalseExpression) { + beforeTrue = DefiniteAssignmentStatus.PotentiallyAssigned; + beforeFalse = DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + beforeTrue = afterCondition; + beforeFalse = afterCondition; + } + + DefiniteAssignmentStatus afterTrue = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeTrue); + DefiniteAssignmentStatus afterFalse = conditionalExpression.FalseExpression.AcceptVisitor(this, beforeFalse); + return MergeStatus(CleanSpecialValues(afterTrue), CleanSpecialValues(afterFalse)); + } + } + + public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data) + { + BlockStatement body = anonymousMethodExpression.Body; + analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data); + return data; + } + + public override DefiniteAssignmentStatus VisitLambdaExpression(LambdaExpression lambdaExpression, DefiniteAssignmentStatus data) + { + Statement body = lambdaExpression.Body as Statement; + if (body != null) { + analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data); + } else { + lambdaExpression.Body.AcceptVisitor(this, data); + } + return data; + } + + public override DefiniteAssignmentStatus VisitIdentifierExpression(IdentifierExpression identifierExpression, DefiniteAssignmentStatus data) + { + if (data != DefiniteAssignmentStatus.DefinitelyAssigned + && identifierExpression.Identifier == analysis.variableName && identifierExpression.TypeArguments.Count == 0) + { + analysis.unassignedVariableUses.Add(identifierExpression); + } + return data; + } + #endregion + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueAnalysis.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueAnalysis.cs new file mode 100644 index 000000000..a20b4a0f0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueAnalysis.cs @@ -0,0 +1,2215 @@ +// +// NullValueAnalysis.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Text; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + public class NullValueAnalysis + { + sealed class VariableStatusInfo : IEquatable, IEnumerable> + { + readonly Dictionary VariableStatus = new Dictionary(); + + public NullValueStatus this[string name] + { + get { + NullValueStatus status; + if (VariableStatus.TryGetValue(name, out status)) { + return status; + } + return NullValueStatus.UnreachableOrInexistent; + } + set { + if (value == NullValueStatus.UnreachableOrInexistent) { + VariableStatus.Remove(name); + } else { + VariableStatus [name] = value; + } + } + } + + /// + /// Modifies the variable state to consider a new incoming path + /// + /// true, if the state has changed, false otherwise. + /// The variable state of the incoming path + public bool ReceiveIncoming(VariableStatusInfo incomingState) + { + bool changed = false; + var listOfVariables = VariableStatus.Keys.Concat(incomingState.VariableStatus.Keys).ToList(); + foreach (string variable in listOfVariables) + { + var newValue = CombineStatus(this [variable], incomingState [variable]); + if (this [variable] != newValue) { + this [variable] = newValue; + changed = true; + } + } + + return changed; + } + + public static NullValueStatus CombineStatus(NullValueStatus oldValue, NullValueStatus incomingValue) + { + if (oldValue == NullValueStatus.Error || incomingValue == NullValueStatus.Error) + return NullValueStatus.Error; + + if (oldValue == NullValueStatus.UnreachableOrInexistent || + oldValue == NullValueStatus.Unassigned) + return incomingValue; + + if (incomingValue == NullValueStatus.Unassigned) { + return NullValueStatus.Unassigned; + } + + if (oldValue == NullValueStatus.CapturedUnknown || incomingValue == NullValueStatus.CapturedUnknown) { + //TODO: Check if this is right + return NullValueStatus.CapturedUnknown; + } + + if (oldValue == NullValueStatus.Unknown) { + return NullValueStatus.Unknown; + } + + if (oldValue == NullValueStatus.DefinitelyNull) { + return incomingValue == NullValueStatus.DefinitelyNull ? + NullValueStatus.DefinitelyNull : NullValueStatus.PotentiallyNull; + } + + if (oldValue == NullValueStatus.DefinitelyNotNull) { + if (incomingValue == NullValueStatus.Unknown) + return NullValueStatus.Unknown; + if (incomingValue == NullValueStatus.DefinitelyNotNull) + return NullValueStatus.DefinitelyNotNull; + return NullValueStatus.PotentiallyNull; + } + + Debug.Assert(oldValue == NullValueStatus.PotentiallyNull); + return NullValueStatus.PotentiallyNull; + } + + public bool HasVariable(string variable) { + return VariableStatus.ContainsKey(variable); + } + + public VariableStatusInfo Clone() { + var clone = new VariableStatusInfo(); + foreach (var item in VariableStatus) { + clone.VariableStatus.Add(item.Key, item.Value); + } + return clone; + } + + public override bool Equals(object obj) + { + return Equals(obj as VariableStatusInfo); + } + + public bool Equals(VariableStatusInfo obj) + { + if (obj == null) { + return false; + } + + if (VariableStatus.Count != obj.VariableStatus.Count) + return false; + + return VariableStatus.All(item => item.Value == obj[item.Key]); + } + + public override int GetHashCode() + { + //STUB + return VariableStatus.Count.GetHashCode(); + } + + public static bool operator ==(VariableStatusInfo obj1, VariableStatusInfo obj2) { + return object.ReferenceEquals(obj1, null) ? + object.ReferenceEquals(obj2, null) : obj1.Equals(obj2); + } + + public static bool operator !=(VariableStatusInfo obj1, VariableStatusInfo obj2) { + return !(obj1 == obj2); + } + + public IEnumerator> GetEnumerator() + { + return VariableStatus.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override string ToString() + { + var builder = new StringBuilder("["); + foreach (var item in this) { + builder.Append(item.Key); + builder.Append("="); + builder.Append(item.Value); + } + builder.Append("]"); + return builder.ToString(); + } + } + + sealed class NullAnalysisNode : ControlFlowNode + { + public readonly VariableStatusInfo VariableState = new VariableStatusInfo(); + public bool Visited { get; private set; } + + public NullAnalysisNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + : base(previousStatement, nextStatement, type) + { + } + + public bool ReceiveIncoming(VariableStatusInfo incomingState) + { + bool changed = VariableState.ReceiveIncoming(incomingState); + if (!Visited) { + Visited = true; + return true; + } + return changed; + } + } + + sealed class NullAnalysisGraphBuilder : ControlFlowGraphBuilder + { + protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + return new NullAnalysisNode(previousStatement, nextStatement, type); + } + } + + class PendingNode : IEquatable { + internal readonly NullAnalysisNode nodeToVisit; + internal readonly VariableStatusInfo statusInfo; + internal readonly ComparableList pendingTryFinallyNodes; + internal readonly NullAnalysisNode nodeAfterFinally; + + internal PendingNode(NullAnalysisNode nodeToVisit, VariableStatusInfo statusInfo) + : this(nodeToVisit, statusInfo, new ComparableList(), null) + { + } + + public PendingNode(NullAnalysisNode nodeToVisit, VariableStatusInfo statusInfo, ComparableList pendingFinallyNodes, NullAnalysisNode nodeAfterFinally) + { + this.nodeToVisit = nodeToVisit; + this.statusInfo = statusInfo; + this.pendingTryFinallyNodes = pendingFinallyNodes; + this.nodeAfterFinally = nodeAfterFinally; + } + + public override bool Equals(object obj) + { + return Equals(obj as PendingNode); + } + + public bool Equals(PendingNode obj) { + if (obj == null) return false; + + if (nodeToVisit != obj.nodeToVisit) return false; + if (statusInfo != obj.statusInfo) return false; + if (pendingTryFinallyNodes != obj.pendingTryFinallyNodes) return false; + if (nodeAfterFinally != obj.nodeAfterFinally) return false; + + return true; + } + + public override int GetHashCode() + { + return nodeToVisit.GetHashCode() ^ + statusInfo.GetHashCode() ^ + pendingTryFinallyNodes.GetHashCode() ^ + (nodeAfterFinally == null ? 0 : nodeAfterFinally.GetHashCode()); + } + } + + readonly BaseRefactoringContext context; + readonly NullAnalysisVisitor visitor; + List allNodes; + readonly HashSet nodesToVisit = new HashSet(); + Dictionary nodeBeforeStatementDict; + Dictionary nodeAfterStatementDict; + readonly Dictionary expressionResult = new Dictionary(); + + public NullValueAnalysis(BaseRefactoringContext context, MethodDeclaration methodDeclaration, CancellationToken cancellationToken) + : this(context, methodDeclaration.Body, methodDeclaration.Parameters, cancellationToken) + { + } + + readonly IEnumerable parameters; + readonly Statement rootStatement; + + readonly CancellationToken cancellationToken; + + public NullValueAnalysis(BaseRefactoringContext context, Statement rootStatement, IEnumerable parameters, CancellationToken cancellationToken) + { + if (rootStatement == null) + throw new ArgumentNullException("rootStatement"); + if (context == null) + throw new ArgumentNullException("context"); + + this.context = context; + this.rootStatement = rootStatement; + this.parameters = parameters; + this.visitor = new NullAnalysisVisitor(this); + this.cancellationToken = cancellationToken; + } + + /// + /// Sets the local variable value. + /// This method does not change anything if identifier does not refer to a local variable. + /// Do not use this in variable declarations since resolving the variable won't work yet. + /// + /// true, if local variable value was set, false otherwise. + /// The variable status data to change. + /// The identifier to set. + /// The name of the identifier to set. + /// The value to set the identifier. + bool SetLocalVariableValue (VariableStatusInfo data, AstNode identifierNode, string identifierName, NullValueStatus value) { + var resolveResult = context.Resolve(identifierNode); + if (resolveResult is LocalResolveResult) { + if (data [identifierName] != NullValueStatus.CapturedUnknown) { + data [identifierName] = value; + + return true; + } + } + return false; + } + + bool SetLocalVariableValue (VariableStatusInfo data, IdentifierExpression identifierExpression, NullValueStatus value) { + return SetLocalVariableValue(data, identifierExpression, identifierExpression.Identifier, value); + } + + bool SetLocalVariableValue (VariableStatusInfo data, Identifier identifier, NullValueStatus value) { + return SetLocalVariableValue(data, identifier, identifier.Name, value); + } + + void SetupNode(NullAnalysisNode node) + { + foreach (var parameter in parameters) { + var resolveResult = context.Resolve(parameter.Type); + node.VariableState[parameter.Name] = GetInitialVariableStatus(resolveResult); + } + + nodesToVisit.Add(new PendingNode(node, node.VariableState)); + } + + static bool IsTypeNullable(IType type) + { + return type.IsReferenceType == true || type.FullName == "System.Nullable"; + } + + public bool IsParametersAreUninitialized { + get; + set; + } + + NullValueStatus GetInitialVariableStatus(ResolveResult resolveResult) + { + var typeResolveResult = resolveResult as TypeResolveResult; + if (typeResolveResult == null) { + return NullValueStatus.Error; + } + var type = typeResolveResult.Type; + if (type.IsReferenceType == null) { + return NullValueStatus.Error; + } + if (!IsParametersAreUninitialized) + return NullValueStatus.DefinitelyNotNull; + return IsTypeNullable(type) ? NullValueStatus.PotentiallyNull : NullValueStatus.DefinitelyNotNull; + } + + public void Analyze() + { + var cfgBuilder = new NullAnalysisGraphBuilder(); + allNodes = cfgBuilder.BuildControlFlowGraph(rootStatement, cancellationToken).Cast().ToList(); + nodeBeforeStatementDict = allNodes.Where(node => node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) + .ToDictionary(node => node.NextStatement); + nodeAfterStatementDict = allNodes.Where(node => node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode) + .ToDictionary(node => node.PreviousStatement); + + foreach (var node in allNodes) { + if (node.Type == ControlFlowNodeType.StartNode && node.NextStatement == rootStatement) { + Debug.Assert(!nodesToVisit.Any()); + + SetupNode(node); + } + } + + while (nodesToVisit.Any()) { + var nodeToVisit = nodesToVisit.First(); + nodesToVisit.Remove(nodeToVisit); + + Visit(nodeToVisit); + } + } + + int visits = 0; + + public int NodeVisits + { + get { + return visits; + } + } + + void Visit(PendingNode nodeInfo) + { + cancellationToken.ThrowIfCancellationRequested(); + + var node = nodeInfo.nodeToVisit; + var statusInfo = nodeInfo.statusInfo; + + visits++; + if (visits > 100) { + //Visiting way too often, let's enter fast mode + //Fast mode is slighly less accurate but visits each node less times + nodesToVisit.RemoveWhere(candidate => candidate.nodeToVisit == nodeInfo.nodeToVisit && + candidate.pendingTryFinallyNodes.Equals(nodeInfo.pendingTryFinallyNodes) && + candidate.nodeAfterFinally == nodeInfo.nodeAfterFinally); + statusInfo = node.VariableState; + } + + var nextStatement = node.NextStatement; + VariableStatusInfo outgoingStatusInfo = statusInfo; + VisitorResult result = null; + + if (nextStatement != null && (!(nextStatement is DoWhileStatement) || node.Type == ControlFlowNodeType.LoopCondition)) { + result = nextStatement.AcceptVisitor(visitor, statusInfo); + if (result == null) { + Console.WriteLine("Failure in {0}", nextStatement); + throw new InvalidOperationException(); + } + + outgoingStatusInfo = result.Variables; + } + + if ((result == null || !result.ThrowsException) && node.Outgoing.Any()) { + var tryFinallyStatement = nextStatement as TryCatchStatement; + + foreach (var outgoingEdge in node.Outgoing) { + VariableStatusInfo edgeInfo; + edgeInfo = outgoingStatusInfo.Clone(); + + if (node.Type == ControlFlowNodeType.EndNode) { + var previousBlock = node.PreviousStatement as BlockStatement; + if (previousBlock != null) { + //We're leaving a block statement. + //As such, we'll remove the variables that were declared *in* the loop + //This helps GetVariableStatusAfter/BeforeStatement be more accurate + //and prevents some redundant revisiting. + + foreach (var variableInitializer in previousBlock.Statements + .OfType() + .SelectMany(declaration => declaration.Variables)) { + + edgeInfo [variableInitializer.Name] = NullValueStatus.UnreachableOrInexistent; + } + } + } + + if (tryFinallyStatement != null) { + //With the exception of try statements, this needs special handling: + //we'll set all changed variables to Unknown or CapturedUnknown + if (outgoingEdge.To.NextStatement == tryFinallyStatement.FinallyBlock) { + foreach (var identifierExpression in tryFinallyStatement.TryBlock.Descendants.OfType()) { + //TODO: Investigate CaptureUnknown + SetLocalVariableValue(edgeInfo, identifierExpression, NullValueStatus.Unknown); + } + } else { + var clause = tryFinallyStatement.CatchClauses + .FirstOrDefault(candidateClause => candidateClause.Body == outgoingEdge.To.NextStatement); + + if (clause != null) { + SetLocalVariableValue(edgeInfo, clause.VariableNameToken, NullValueStatus.DefinitelyNotNull); + + foreach (var identifierExpression in tryFinallyStatement.TryBlock.Descendants.OfType()) { + //TODO: Investigate CaptureUnknown + SetLocalVariableValue(edgeInfo, identifierExpression, NullValueStatus.Unknown); + } + } + } + } + + if (result != null) { + switch (outgoingEdge.Type) { + case ControlFlowEdgeType.ConditionTrue: + if (result.KnownBoolResult == false) { + //No need to explore this path -- expression is known to be false + continue; + } + edgeInfo = result.TruePathVariables; + break; + case ControlFlowEdgeType.ConditionFalse: + if (result.KnownBoolResult == true) { + //No need to explore this path -- expression is known to be true + continue; + } + edgeInfo = result.FalsePathVariables; + break; + } + } + + if (outgoingEdge.IsLeavingTryFinally) { + var nodeAfterFinally = (NullAnalysisNode)outgoingEdge.To; + var finallyNodes = outgoingEdge.TryFinallyStatements.Select(tryFinally => nodeBeforeStatementDict [tryFinally.FinallyBlock]).ToList(); + var nextNode = finallyNodes.First(); + var remainingFinallyNodes = new ComparableList(finallyNodes.Skip(1)); + //We have to visit the node even if ReceiveIncoming returns false + //since the finallyNodes/nodeAfterFinally might be different even if the values of variables are the same -- and they need to be visited either way! + //TODO 1: Is there any point in visiting the finally statement here? + //TODO 2: Do we need the ReceiveIncoming at all? + nextNode.ReceiveIncoming(edgeInfo); + nodesToVisit.Add(new PendingNode(nextNode, edgeInfo, remainingFinallyNodes, nodeAfterFinally)); + } else { + var outgoingNode = (NullAnalysisNode)outgoingEdge.To; + if (outgoingNode.ReceiveIncoming(edgeInfo)) { + nodesToVisit.Add(new PendingNode(outgoingNode, edgeInfo)); + } + } + } + } else { + //We found a return/throw/yield break or some other termination node + var finallyBlockStarts = nodeInfo.pendingTryFinallyNodes; + var nodeAfterFinally = nodeInfo.nodeAfterFinally; + + if (finallyBlockStarts.Any()) { + var nextNode = finallyBlockStarts.First(); + if (nextNode.ReceiveIncoming(outgoingStatusInfo)) + nodesToVisit.Add(new PendingNode(nextNode, outgoingStatusInfo, new ComparableList(finallyBlockStarts.Skip(1)), nodeInfo.nodeAfterFinally)); + } else if (nodeAfterFinally != null && nodeAfterFinally.ReceiveIncoming(outgoingStatusInfo)) { + nodesToVisit.Add(new PendingNode(nodeAfterFinally, outgoingStatusInfo)); + } else { + //Maybe we finished a try/catch/finally statement the "normal" way (no direct jumps) + //so let's check that case + var statement = node.PreviousStatement ?? node.NextStatement; + Debug.Assert(statement != null); + var parent = statement.GetParent(); + var parentTryCatch = parent as TryCatchStatement; + if (parentTryCatch != null) { + var nextNode = nodeAfterStatementDict [parentTryCatch]; + if (nextNode.ReceiveIncoming(outgoingStatusInfo)) { + nodesToVisit.Add(new PendingNode(nextNode, outgoingStatusInfo)); + } + } + } + } + } + + public NullValueStatus GetExpressionResult(Expression expr) + { + if (expr == null) + throw new ArgumentNullException("expr"); + + NullValueStatus info; + if (expressionResult.TryGetValue(expr, out info)) { + return info; + } + + return NullValueStatus.UnreachableOrInexistent; + } + + public NullValueStatus GetVariableStatusBeforeStatement(Statement stmt, string variableName) + { + if (stmt == null) + throw new ArgumentNullException("stmt"); + if (variableName == null) + throw new ArgumentNullException("variableName"); + + NullAnalysisNode node; + if (nodeBeforeStatementDict.TryGetValue(stmt, out node)) { + return node.VariableState [variableName]; + } + + return NullValueStatus.UnreachableOrInexistent; + } + + public NullValueStatus GetVariableStatusAfterStatement(Statement stmt, string variableName) + { + if (stmt == null) + throw new ArgumentNullException("stmt"); + if (variableName == null) + throw new ArgumentNullException("variableName"); + + NullAnalysisNode node; + if (nodeAfterStatementDict.TryGetValue(stmt, out node)) { + return node.VariableState [variableName]; + } + + return NullValueStatus.UnreachableOrInexistent; + } + + class ConditionalBranchInfo + { + /// + /// True if the variable is null for the true path, false if it is false for the true path. + /// + public Dictionary TrueResultVariableNullStates = new Dictionary(); + /// + /// True if the variable is null for the false path, false if it is false for the false path. + /// + public Dictionary FalseResultVariableNullStates = new Dictionary(); + } + + class VisitorResult + { + /// + /// Indicates the return value of the expression. + /// + /// + /// Only applicable for expressions. + /// + public NullValueStatus NullableReturnResult; + + /// + /// Indicates the value of each item in an array or linq query. + /// + public NullValueStatus EnumeratedValueResult; + + /// + /// Information that indicates the restrictions to add + /// when branching. + /// + /// + /// Used in if/else statements, conditional expressions and + /// while statements. + /// + public ConditionalBranchInfo ConditionalBranchInfo; + + /// + /// The state of the variables after the expression is executed. + /// + public VariableStatusInfo Variables; + + /// + /// The expression is known to be invalid and trigger an error + /// (e.g. a NullReferenceException) + /// + public bool ThrowsException; + + /// + /// The known bool result of an expression. + /// + public bool? KnownBoolResult; + + public static VisitorResult ForEnumeratedValue(VariableStatusInfo variables, NullValueStatus itemValues) + { + var result = new VisitorResult(); + result.NullableReturnResult = NullValueStatus.DefinitelyNotNull; + result.EnumeratedValueResult = itemValues; + result.Variables = variables.Clone(); + return result; + } + + public static VisitorResult ForValue(VariableStatusInfo variables, NullValueStatus returnValue) + { + var result = new VisitorResult(); + result.NullableReturnResult = returnValue; + result.Variables = variables.Clone(); + return result; + } + + public static VisitorResult ForBoolValue(VariableStatusInfo variables, bool newValue) + { + var result = new VisitorResult(); + result.NullableReturnResult = NullValueStatus.DefinitelyNotNull; //Bool expressions are never null + result.KnownBoolResult = newValue; + result.Variables = variables.Clone(); + return result; + } + + public static VisitorResult ForException(VariableStatusInfo variables) { + var result = new VisitorResult(); + result.NullableReturnResult = NullValueStatus.UnreachableOrInexistent; + result.ThrowsException = true; + result.Variables = variables.Clone(); + return result; + } + + public VisitorResult Negated { + get { + var result = new VisitorResult(); + if (NullableReturnResult.IsDefiniteValue()) { + result.NullableReturnResult = NullableReturnResult == NullValueStatus.DefinitelyNull + ? NullValueStatus.DefinitelyNotNull : NullValueStatus.DefinitelyNull; + } else { + result.NullableReturnResult = NullableReturnResult; + } + result.Variables = Variables.Clone(); + result.KnownBoolResult = !KnownBoolResult; + if (ConditionalBranchInfo != null) { + result.ConditionalBranchInfo = new ConditionalBranchInfo(); + foreach (var item in ConditionalBranchInfo.TrueResultVariableNullStates) { + result.ConditionalBranchInfo.FalseResultVariableNullStates [item.Key] = item.Value; + } + foreach (var item in ConditionalBranchInfo.FalseResultVariableNullStates) { + result.ConditionalBranchInfo.TrueResultVariableNullStates [item.Key] = item.Value; + } + } + return result; + } + } + + public VariableStatusInfo TruePathVariables { + get { + var variables = Variables.Clone(); + if (ConditionalBranchInfo != null) { + foreach (var item in ConditionalBranchInfo.TrueResultVariableNullStates) { + variables [item.Key] = item.Value ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull; + } + } + return variables; + } + } + + public VariableStatusInfo FalsePathVariables { + get { + var variables = Variables.Clone(); + if (ConditionalBranchInfo != null) { + foreach (var item in ConditionalBranchInfo.FalseResultVariableNullStates) { + variables [item.Key] = item.Value ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull; + } + } + return variables; + } + } + + public static VisitorResult AndOperation(VisitorResult tentativeLeftResult, VisitorResult tentativeRightResult) + { + var result = new VisitorResult(); + result.KnownBoolResult = tentativeLeftResult.KnownBoolResult & tentativeRightResult.KnownBoolResult; + + var trueTruePath = tentativeRightResult.TruePathVariables; + var trueFalsePath = tentativeRightResult.FalsePathVariables; + var falsePath = tentativeLeftResult.FalsePathVariables; + + var trueVariables = trueTruePath; + + var falseVariables = trueFalsePath.Clone(); + falseVariables.ReceiveIncoming(falsePath); + result.Variables = trueVariables.Clone(); + result.Variables.ReceiveIncoming(falseVariables); + + result.ConditionalBranchInfo = new ConditionalBranchInfo(); + + foreach (var variable in trueVariables) { + if (!variable.Value.IsDefiniteValue()) + continue; + + string variableName = variable.Key; + + if (variable.Value != result.Variables[variableName]) { + bool isNull = variable.Value == NullValueStatus.DefinitelyNull; + result.ConditionalBranchInfo.TrueResultVariableNullStates.Add(variableName, isNull); + } + } + + foreach (var variable in falseVariables) { + if (!variable.Value.IsDefiniteValue()) + continue; + + string variableName = variable.Key; + + if (variable.Value != result.Variables [variableName]) { + bool isNull = variable.Value == NullValueStatus.DefinitelyNull; + result.ConditionalBranchInfo.FalseResultVariableNullStates.Add(variableName, isNull); + } + } + + return result; + } + + public static VisitorResult OrOperation(VisitorResult tentativeLeftResult, VisitorResult tentativeRightResult) + { + return VisitorResult.AndOperation(tentativeLeftResult.Negated, tentativeRightResult.Negated).Negated; + } + } + + class NullAnalysisVisitor : DepthFirstAstVisitor + { + NullValueAnalysis analysis; + + public NullAnalysisVisitor(NullValueAnalysis analysis) { + this.analysis = analysis; + } + + protected override VisitorResult VisitChildren(AstNode node, VariableStatusInfo data) + { + Debug.Fail("Missing override for " + node.GetType().Name); + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitNullNode(AstNode nullNode, VariableStatusInfo data) + { + // can occur due to syntax errors + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitEmptyStatement(EmptyStatement emptyStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitBlockStatement(BlockStatement blockStatement, VariableStatusInfo data) + { + //We'll visit the child statements later (we'll visit each one directly from the CFG) + //As such this is mostly a dummy node. + return new VisitorResult { Variables = data }; + } + + public override VisitorResult VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, VariableStatusInfo data) + { + foreach (var variable in variableDeclarationStatement.Variables) { + var result = variable.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables; + } + + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitVariableInitializer(VariableInitializer variableInitializer, VariableStatusInfo data) + { + if (variableInitializer.Initializer.IsNull) { + data = data.Clone(); + data[variableInitializer.Name] = NullValueStatus.Unassigned; + } else { + var result = variableInitializer.Initializer.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables.Clone(); + data[variableInitializer.Name] = result.NullableReturnResult; + } + + return VisitorResult.ForValue(data, data [variableInitializer.Name]); + } + + public override VisitorResult VisitIfElseStatement(IfElseStatement ifElseStatement, VariableStatusInfo data) + { + //We'll visit the true/false statements later (directly from the CFG) + return ifElseStatement.Condition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitWhileStatement(WhileStatement whileStatement, VariableStatusInfo data) + { + return whileStatement.Condition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitDoWhileStatement(DoWhileStatement doWhileStatement, VariableStatusInfo data) + { + return doWhileStatement.Condition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitForStatement(ForStatement forStatement, VariableStatusInfo data) + { + //The initializers, the embedded statement and the iterators aren't visited here + //because they have their own CFG nodes. + if (forStatement.Condition.IsNull) + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + return forStatement.Condition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitForeachStatement(ForeachStatement foreachStatement, VariableStatusInfo data) + { + var newVariable = foreachStatement.VariableNameToken; + var inExpressionResult = foreachStatement.InExpression.AcceptVisitor(this, data); + if (inExpressionResult.ThrowsException) + return inExpressionResult; + + var newData = inExpressionResult.Variables.Clone(); + + var resolveResult = analysis.context.Resolve(foreachStatement.VariableNameToken) as LocalResolveResult; + if (resolveResult != null) { + //C# 5.0 changed the meaning of foreach so that each iteration declares a new variable + //as such, the variable is "uncaptured" only for C# >= 5.0 + if (analysis.context.Supports(new Version(5, 0)) || data[newVariable.Name] != NullValueStatus.CapturedUnknown) { + newData[newVariable.Name] = NullValueAnalysis.IsTypeNullable(resolveResult.Type) ? inExpressionResult.EnumeratedValueResult : NullValueStatus.DefinitelyNotNull; + } + } + + return VisitorResult.ForValue(newData, NullValueStatus.Unknown); + } + + public override VisitorResult VisitUsingStatement(UsingStatement usingStatement, VariableStatusInfo data) + { + return usingStatement.ResourceAcquisition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitFixedStatement(FixedStatement fixedStatement, VariableStatusInfo data) + { + foreach (var variable in fixedStatement.Variables) { + var result = variable.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables; + } + + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitSwitchStatement(SwitchStatement switchStatement, VariableStatusInfo data) + { + //We could do better than this, but it would require special handling outside the visitor + //so for now, for simplicity, we'll just take the easy way + + var tentativeResult = switchStatement.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) { + return tentativeResult; + } + + foreach (var section in switchStatement.SwitchSections) { + //No need to check for ThrowsException, since it will always be false (see VisitSwitchSection) + section.AcceptVisitor(this, tentativeResult.Variables); + } + + return VisitorResult.ForValue(tentativeResult.Variables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitSwitchSection(SwitchSection switchSection, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitExpressionStatement(ExpressionStatement expressionStatement, VariableStatusInfo data) + { + return expressionStatement.Expression.AcceptVisitor(this, data); + } + + public override VisitorResult VisitReturnStatement(ReturnStatement returnStatement, VariableStatusInfo data) + { + if (returnStatement.Expression.IsNull) + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + return returnStatement.Expression.AcceptVisitor(this, data); + } + + public override VisitorResult VisitTryCatchStatement(TryCatchStatement tryCatchStatement, VariableStatusInfo data) + { + //The needs special treatment in the analyser itself + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitBreakStatement(BreakStatement breakStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitContinueStatement(ContinueStatement continueStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitGotoStatement(GotoStatement gotoStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitLabelStatement(LabelStatement labelStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitUnsafeStatement(UnsafeStatement unsafeStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitLockStatement(LockStatement lockStatement, VariableStatusInfo data) + { + var expressionResult = lockStatement.Expression.AcceptVisitor(this, data); + if (expressionResult.ThrowsException) + return expressionResult; + + if (expressionResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return VisitorResult.ForException(expressionResult.Variables); + } + + var identifier = CSharpUtil.GetInnerMostExpression(lockStatement.Expression) as IdentifierExpression; + if (identifier != null) { + var identifierValue = expressionResult.Variables [identifier.Identifier]; + if (identifierValue != NullValueStatus.CapturedUnknown) { + var newVariables = expressionResult.Variables.Clone(); + analysis.SetLocalVariableValue(newVariables, identifier, NullValueStatus.DefinitelyNotNull); + + return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown); + } + } + + return VisitorResult.ForValue(expressionResult.Variables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitThrowStatement(ThrowStatement throwStatement, VariableStatusInfo data) + { + if (throwStatement.Expression.IsNull) + return VisitorResult.ForValue(data, NullValueStatus.DefinitelyNotNull); + return throwStatement.Expression.AcceptVisitor(this, data); + } + + public override VisitorResult VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, VariableStatusInfo data) + { + return yieldReturnStatement.Expression.AcceptVisitor(this, data); + } + + public override VisitorResult VisitCheckedStatement(CheckedStatement checkedStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitUncheckedStatement(UncheckedStatement uncheckedStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + void RegisterExpressionResult(Expression expression, NullValueStatus expressionResult) + { + NullValueStatus oldStatus; + if (analysis.expressionResult.TryGetValue(expression, out oldStatus)) { + analysis.expressionResult[expression] = VariableStatusInfo.CombineStatus(analysis.expressionResult[expression], expressionResult); + } + else { + analysis.expressionResult[expression] = expressionResult; + } + } + + VisitorResult HandleExpressionResult(Expression expression, VariableStatusInfo dataAfterExpression, NullValueStatus expressionResult) { + RegisterExpressionResult(expression, expressionResult); + + return VisitorResult.ForValue(dataAfterExpression, expressionResult); + } + + VisitorResult HandleExpressionResult(Expression expression, VariableStatusInfo dataAfterExpression, bool expressionResult) { + RegisterExpressionResult(expression, NullValueStatus.DefinitelyNotNull); + + return VisitorResult.ForBoolValue(dataAfterExpression, expressionResult); + } + + VisitorResult HandleExpressionResult(Expression expression, VisitorResult result) { + RegisterExpressionResult(expression, result.NullableReturnResult); + + return result; + } + + public override VisitorResult VisitAssignmentExpression(AssignmentExpression assignmentExpression, VariableStatusInfo data) + { + var tentativeResult = assignmentExpression.Left.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return HandleExpressionResult(assignmentExpression, tentativeResult); + tentativeResult = assignmentExpression.Right.AcceptVisitor(this, tentativeResult.Variables); + if (tentativeResult.ThrowsException) + return HandleExpressionResult(assignmentExpression, tentativeResult); + + var leftIdentifier = assignmentExpression.Left as IdentifierExpression; + if (leftIdentifier != null) { + var resolveResult = analysis.context.Resolve(leftIdentifier); + if (resolveResult.IsError) { + return HandleExpressionResult(assignmentExpression, data, NullValueStatus.Error); + } + + if (resolveResult is LocalResolveResult) { + var result = new VisitorResult(); + result.NullableReturnResult = tentativeResult.NullableReturnResult; + result.Variables = tentativeResult.Variables.Clone(); + var oldValue = result.Variables [leftIdentifier.Identifier]; + + if (assignmentExpression.Operator == AssignmentOperatorType.Assign || + oldValue == NullValueStatus.Unassigned || + oldValue == NullValueStatus.DefinitelyNotNull || + tentativeResult.NullableReturnResult == NullValueStatus.Error || + tentativeResult.NullableReturnResult == NullValueStatus.Unknown) { + analysis.SetLocalVariableValue(result.Variables, leftIdentifier, tentativeResult.NullableReturnResult); + } else { + if (oldValue == NullValueStatus.DefinitelyNull) { + //Do nothing --it'll remain null + } else { + analysis.SetLocalVariableValue(result.Variables, leftIdentifier, NullValueStatus.PotentiallyNull); + } + } + + return HandleExpressionResult(assignmentExpression, result); + } + } + + return HandleExpressionResult(assignmentExpression, tentativeResult); + } + + public override VisitorResult VisitIdentifierExpression(IdentifierExpression identifierExpression, VariableStatusInfo data) + { + var resolveResult = analysis.context.Resolve(identifierExpression); + if (resolveResult.IsError) { + return HandleExpressionResult(identifierExpression, data, NullValueStatus.Error); + } + var local = resolveResult as LocalResolveResult; + if (local != null) { + var value = data [local.Variable.Name]; + if (value == NullValueStatus.CapturedUnknown) + value = NullValueStatus.Unknown; + return HandleExpressionResult(identifierExpression, data, value); + } + if (resolveResult.IsCompileTimeConstant) { + object value = resolveResult.ConstantValue; + if (value == null) { + return HandleExpressionResult(identifierExpression, data, NullValueStatus.DefinitelyNull); + } + var boolValue = value as bool?; + if (boolValue != null) { + return VisitorResult.ForBoolValue(data, (bool)boolValue); + } + return HandleExpressionResult(identifierExpression, data, NullValueStatus.DefinitelyNotNull); + } + + var memberResolveResult = resolveResult as MemberResolveResult; + + var returnValue = GetFieldReturnValue(memberResolveResult, data); + + return HandleExpressionResult(identifierExpression, data, returnValue); + } + + public override VisitorResult VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, VariableStatusInfo data) + { + var resolveResult = analysis.context.Resolve(defaultValueExpression); + if (resolveResult.IsError) { + return HandleExpressionResult(defaultValueExpression, data, NullValueStatus.Unknown); + } + + Debug.Assert(resolveResult.IsCompileTimeConstant); + + var status = resolveResult.ConstantValue == null && resolveResult.Type.IsReferenceType != false ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull; + return HandleExpressionResult(defaultValueExpression, data, status); + } + + public override VisitorResult VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, VariableStatusInfo data) + { + return HandleExpressionResult(nullReferenceExpression, data, NullValueStatus.DefinitelyNull); + } + + public override VisitorResult VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, VariableStatusInfo data) + { + return HandleExpressionResult(primitiveExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, VariableStatusInfo data) + { + return HandleExpressionResult(parenthesizedExpression, parenthesizedExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitConditionalExpression(ConditionalExpression conditionalExpression, VariableStatusInfo data) + { + var tentativeBaseResult = conditionalExpression.Condition.AcceptVisitor(this, data); + if (tentativeBaseResult.ThrowsException) + return HandleExpressionResult(conditionalExpression, tentativeBaseResult); + + var conditionResolveResult = analysis.context.Resolve(conditionalExpression.Condition); + + if (tentativeBaseResult.KnownBoolResult == true || true.Equals(conditionResolveResult.ConstantValue)) { + return HandleExpressionResult(conditionalExpression, conditionalExpression.TrueExpression.AcceptVisitor(this, tentativeBaseResult.TruePathVariables)); + } + if (tentativeBaseResult.KnownBoolResult == false || false.Equals(conditionResolveResult.ConstantValue)) { + return HandleExpressionResult(conditionalExpression, conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables)); + } + + //No known bool result + var trueCaseResult = conditionalExpression.TrueExpression.AcceptVisitor(this, tentativeBaseResult.TruePathVariables); + if (trueCaseResult.ThrowsException) { + //We know that the true case will never be completed, then the right case is the only possible route. + return HandleExpressionResult(conditionalExpression, conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables)); + } + var falseCaseResult = conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables); + if (falseCaseResult.ThrowsException) { + return HandleExpressionResult(conditionalExpression, trueCaseResult.Variables, true); + } + + return HandleExpressionResult(conditionalExpression, VisitorResult.OrOperation(trueCaseResult, falseCaseResult)); + } + + public override VisitorResult VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + //Let's not evaluate the sides just yet because of ??, && and || + + //We'll register the results here (with HandleExpressionResult) + //so each Visit*Expression won't have to do it itself + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.ConditionalAnd: + return HandleExpressionResult(binaryOperatorExpression, VisitConditionalAndExpression(binaryOperatorExpression, data)); + case BinaryOperatorType.ConditionalOr: + return HandleExpressionResult(binaryOperatorExpression, VisitConditionalOrExpression(binaryOperatorExpression, data)); + case BinaryOperatorType.NullCoalescing: + return HandleExpressionResult(binaryOperatorExpression, VisitNullCoalescing(binaryOperatorExpression, data)); + case BinaryOperatorType.Equality: + return HandleExpressionResult(binaryOperatorExpression, VisitEquality(binaryOperatorExpression, data)); + case BinaryOperatorType.InEquality: + return HandleExpressionResult(binaryOperatorExpression, VisitEquality(binaryOperatorExpression, data).Negated); + default: + return HandleExpressionResult(binaryOperatorExpression, VisitOtherBinaryExpression(binaryOperatorExpression, data)); + } + } + + VisitorResult VisitOtherBinaryExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + var leftTentativeResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (leftTentativeResult.ThrowsException) + return leftTentativeResult; + var rightTentativeResult = binaryOperatorExpression.Right.AcceptVisitor(this, leftTentativeResult.Variables); + if (rightTentativeResult.ThrowsException) + return rightTentativeResult; + + //TODO: Assuming operators are not overloaded by users + // (or, if they are, that they retain similar behavior to the default ones) + + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.LessThan: + case BinaryOperatorType.GreaterThan: + //Operations < and > with nulls always return false + //Those same operations will other values may or may not return false + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull && + rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false); + } + //We don't know what the value is, but we know that both true and false are != null. + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNotNull); + case BinaryOperatorType.LessThanOrEqual: + case BinaryOperatorType.GreaterThanOrEqual: + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) + return VisitorResult.ForBoolValue(rightTentativeResult.Variables, true); + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) + return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false); + } else if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) { + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) + return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false); + } + + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.Unknown); + default: + //Anything else: null + anything == anything + null == null. + //not null + not null = not null + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNull); + } + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) { + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNull); + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.Unknown); + } + } + + VisitorResult WithVariableValue(VisitorResult result, IdentifierExpression identifier, bool isNull) + { + var localVariableResult = analysis.context.Resolve(identifier) as LocalResolveResult; + if (localVariableResult != null) { + result.ConditionalBranchInfo.TrueResultVariableNullStates[identifier.Identifier] = isNull; + if (isNull) { + result.ConditionalBranchInfo.FalseResultVariableNullStates[identifier.Identifier] = false; + } + } + return result; + } + + VisitorResult VisitEquality(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + //TODO: Should this check for user operators? + + var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (tentativeLeftResult.ThrowsException) + return tentativeLeftResult; + var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, tentativeLeftResult.Variables); + if (tentativeRightResult.ThrowsException) + return tentativeRightResult; + + if (tentativeLeftResult.KnownBoolResult != null && tentativeLeftResult.KnownBoolResult == tentativeRightResult.KnownBoolResult) { + return VisitorResult.ForBoolValue(tentativeRightResult.Variables, true); + } + + if (tentativeLeftResult.KnownBoolResult != null && tentativeLeftResult.KnownBoolResult == !tentativeRightResult.KnownBoolResult) { + return VisitorResult.ForBoolValue(tentativeRightResult.Variables, false); + } + + if (tentativeLeftResult.NullableReturnResult.IsDefiniteValue()) { + if (tentativeRightResult.NullableReturnResult.IsDefiniteValue()) { + if (tentativeLeftResult.NullableReturnResult == NullValueStatus.DefinitelyNull || tentativeRightResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return VisitorResult.ForBoolValue(tentativeRightResult.Variables, tentativeLeftResult.NullableReturnResult == tentativeRightResult.NullableReturnResult); + } + } + } + + var result = new VisitorResult(); + result.Variables = tentativeRightResult.Variables; + result.NullableReturnResult = NullValueStatus.Unknown; + result.ConditionalBranchInfo = new ConditionalBranchInfo(); + + if (tentativeRightResult.NullableReturnResult.IsDefiniteValue()) { + var identifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Left) as IdentifierExpression; + + if (identifier != null) { + bool isNull = (tentativeRightResult.NullableReturnResult == NullValueStatus.DefinitelyNull); + + WithVariableValue(result, identifier, isNull); + } + } + + if (tentativeLeftResult.NullableReturnResult.IsDefiniteValue()) { + var identifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Right) as IdentifierExpression; + + if (identifier != null) { + bool isNull = (tentativeLeftResult.NullableReturnResult == NullValueStatus.DefinitelyNull); + + WithVariableValue(result, identifier, isNull); + } + } + + return result; + } + + VisitorResult VisitConditionalAndExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (tentativeLeftResult.KnownBoolResult == false || tentativeLeftResult.ThrowsException) { + return tentativeLeftResult; + } + + var truePath = tentativeLeftResult.TruePathVariables; + var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, truePath); + if (tentativeRightResult.ThrowsException) { + //If the true path throws an exception, then the only way for the expression to complete + //successfully is if the left expression is false + return VisitorResult.ForBoolValue(tentativeLeftResult.FalsePathVariables, false); + } + + return VisitorResult.AndOperation(tentativeLeftResult, tentativeRightResult); + } + + VisitorResult VisitConditionalOrExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (tentativeLeftResult.KnownBoolResult == true || tentativeLeftResult.ThrowsException) { + return tentativeLeftResult; + } + + var falsePath = tentativeLeftResult.FalsePathVariables; + var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, falsePath); + if (tentativeRightResult.ThrowsException) { + //If the false path throws an exception, then the only way for the expression to complete + //successfully is if the left expression is true + return VisitorResult.ForBoolValue(tentativeLeftResult.TruePathVariables, true); + } + + return VisitorResult.OrOperation(tentativeLeftResult, tentativeRightResult); + } + + VisitorResult VisitNullCoalescing(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + var leftTentativeResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull || leftTentativeResult.ThrowsException) { + return leftTentativeResult; + } + + //If the right side is found, then the left side is known to be null + var newData = leftTentativeResult.Variables; + var leftIdentifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Left) as IdentifierExpression; + if (leftIdentifier != null) { + newData = newData.Clone(); + analysis.SetLocalVariableValue(newData, leftIdentifier, NullValueStatus.DefinitelyNull); + } + + var rightTentativeResult = binaryOperatorExpression.Right.AcceptVisitor(this, newData); + if (rightTentativeResult.ThrowsException) { + //This means the left expression was not null all along (or else the expression will throw an exception) + + if (leftIdentifier != null) { + newData = newData.Clone(); + analysis.SetLocalVariableValue(newData, leftIdentifier, NullValueStatus.DefinitelyNotNull); + return VisitorResult.ForValue(newData, NullValueStatus.DefinitelyNotNull); + } + + return VisitorResult.ForValue(leftTentativeResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + var mergedVariables = rightTentativeResult.Variables; + var nullValue = rightTentativeResult.NullableReturnResult; + + if (leftTentativeResult.NullableReturnResult != NullValueStatus.DefinitelyNull) { + mergedVariables = mergedVariables.Clone(); + mergedVariables.ReceiveIncoming(leftTentativeResult.Variables); + if (nullValue == NullValueStatus.DefinitelyNull) { + nullValue = NullValueStatus.PotentiallyNull; + } + } + + return VisitorResult.ForValue(mergedVariables, nullValue); + } + + public override VisitorResult VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, VariableStatusInfo data) + { + //TODO: Again, what to do when overloaded operators are found? + + var tentativeResult = unaryOperatorExpression.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return HandleExpressionResult(unaryOperatorExpression, tentativeResult); + + if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) { + return HandleExpressionResult(unaryOperatorExpression, tentativeResult.Negated); + } + return HandleExpressionResult(unaryOperatorExpression, tentativeResult); + } + + public override VisitorResult VisitInvocationExpression(InvocationExpression invocationExpression, VariableStatusInfo data) + { + //TODO: Handle some common methods such as string.IsNullOrEmpty + + var targetResult = invocationExpression.Target.AcceptVisitor(this, data); + if (targetResult.ThrowsException) + return HandleExpressionResult(invocationExpression, targetResult); + + data = targetResult.Variables; + + var methodResolveResult = analysis.context.Resolve(invocationExpression) as CSharpInvocationResolveResult; + + List parameterResults = new List(); + + foreach (var argumentToHandle in invocationExpression.Arguments.Select((argument, parameterIndex) => new { argument, parameterIndex })) { + var argument = argumentToHandle.argument; + var parameterIndex = argumentToHandle.parameterIndex; + + var result = argument.AcceptVisitor(this, data); + if (result.ThrowsException) + return HandleExpressionResult(invocationExpression, result); + parameterResults.Add(result); + + var namedArgument = argument as NamedArgumentExpression; + + var directionExpression = (namedArgument == null ? argument : namedArgument.Expression) as DirectionExpression; + if (directionExpression != null && methodResolveResult != null) { + var identifier = directionExpression.Expression as IdentifierExpression; + if (identifier != null) { + //out and ref parameters do *NOT* capture the variable (since they must stop changing it by the time they return) + var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult; + if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) { + data = data.Clone(); + + FixParameter(argument, methodResolveResult.Member.Parameters, parameterIndex, identifier, data); + } + } + + + continue; + } + + data = result.Variables; + } + + var identifierExpression = CSharpUtil.GetInnerMostExpression(invocationExpression.Target) as IdentifierExpression; + if (identifierExpression != null) { + if (targetResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return HandleExpressionResult(invocationExpression, VisitorResult.ForException(data)); + } + + var descendentIdentifiers = invocationExpression.Arguments.SelectMany(argument => argument.DescendantsAndSelf).OfType(); + if (!descendentIdentifiers.Any(identifier => identifier.Identifier == identifierExpression.Identifier)) { + //TODO: We can make this check better (see VisitIndexerExpression for more details) + data = data.Clone(); + analysis.SetLocalVariableValue(data, identifierExpression, NullValueStatus.DefinitelyNotNull); + } + } + + return HandleExpressionResult(invocationExpression, GetMethodVisitorResult(methodResolveResult, data, parameterResults)); + } + + static VisitorResult GetMethodVisitorResult(CSharpInvocationResolveResult methodResolveResult, VariableStatusInfo data, List parameterResults) + { + if (methodResolveResult == null) + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + + var method = methodResolveResult.Member as IMethod; + if (method != null) { + if (method.GetAttribute(new FullTypeName(AnnotationNames.AssertionMethodAttribute)) != null) { + var assertionParameters = method.Parameters.Select((parameter, index) => new { index, parameter }) + .Select(parameter => new { parameter.index, parameter.parameter, attributes = parameter.parameter.Attributes.Where(attribute => attribute.AttributeType.FullName == AnnotationNames.AssertionConditionAttribute).ToList() }) + .Where(parameter => parameter.attributes.Count() == 1) + .Select(parameter => new { parameter.index, parameter.parameter, attribute = parameter.attributes[0] }) + .ToList(); + + //Unclear what should be done if there are multiple assertion conditions + if (assertionParameters.Count() == 1) { + Debug.Assert(methodResolveResult.Arguments.Count == parameterResults.Count); + + var assertionParameter = assertionParameters [0]; + VisitorResult assertionParameterResult = null; + + object intendedResult = true; + var positionalArgument = assertionParameter.attribute.PositionalArguments.FirstOrDefault() as MemberResolveResult; + if (positionalArgument != null && positionalArgument.Type.FullName == AnnotationNames.AssertionConditionTypeAttribute) { + switch (positionalArgument.Member.FullName) { + case AnnotationNames.AssertionConditionTypeIsTrue: + intendedResult = true; + break; + case AnnotationNames.AssertionConditionTypeIsFalse: + intendedResult = false; + break; + case AnnotationNames.AssertionConditionTypeIsNull: + intendedResult = null; + break; + case AnnotationNames.AssertionConditionTypeIsNotNull: + intendedResult = ""; + break; + } + } + + int parameterIndex = assertionParameter.index; + if (assertionParameter.index < methodResolveResult.Arguments.Count && !(methodResolveResult.Arguments [assertionParameter.index] is NamedArgumentResolveResult)) { + //Use index + assertionParameterResult = parameterResults [assertionParameter.index]; + } else { + //Use named argument + int? nameIndex = methodResolveResult.Arguments.Select((argument, index) => new { argument, index}) + .Where(argument => { + var namedArgument = argument.argument as NamedArgumentResolveResult; + return namedArgument != null && namedArgument.ParameterName == assertionParameter.parameter.Name; + }).Select(argument => (int?)argument.index).FirstOrDefault(); + + if (nameIndex != null) { + parameterIndex = nameIndex.Value; + assertionParameterResult = parameterResults [nameIndex.Value]; + } else if (assertionParameter.parameter.IsOptional) { + //Try to use default value + + if (intendedResult is string) { + if (assertionParameter.parameter.ConstantValue == null) { + return VisitorResult.ForException(data); + } + } else { + if (!object.Equals(assertionParameter.parameter.ConstantValue, intendedResult)) { + return VisitorResult.ForException(data); + } + } + } else { + //The parameter was not specified, yet it is not optional? + return VisitorResult.ForException(data); + } + } + + //Now check assertion + if (assertionParameterResult != null) { + if (intendedResult is bool) { + if (assertionParameterResult.KnownBoolResult == !(bool)intendedResult) { + return VisitorResult.ForException(data); + } + + data = (bool)intendedResult ? assertionParameterResult.TruePathVariables : assertionParameterResult.FalsePathVariables; + } else { + bool shouldBeNull = intendedResult == null; + + if (assertionParameterResult.NullableReturnResult == (shouldBeNull ? NullValueStatus.DefinitelyNotNull : NullValueStatus.DefinitelyNull)) { + return VisitorResult.ForException(data); + } + + var parameterResolveResult = methodResolveResult.Arguments [parameterIndex]; + + LocalResolveResult localVariableResult = null; + + var conversionResolveResult = parameterResolveResult as ConversionResolveResult; + if (conversionResolveResult != null) { + if (!IsTypeNullable(conversionResolveResult.Type)) { + if (intendedResult == null) { + return VisitorResult.ForException(data); + } + } else { + localVariableResult = conversionResolveResult.Input as LocalResolveResult; + } + } else { + localVariableResult = parameterResolveResult as LocalResolveResult; + } + + if (localVariableResult != null && data[localVariableResult.Variable.Name] != NullValueStatus.CapturedUnknown) { + data = data.Clone(); + data [localVariableResult.Variable.Name] = shouldBeNull ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull; + } + } + } + } + } + } + + bool isNullable = IsTypeNullable(methodResolveResult.Type); + if (!isNullable) { + return VisitorResult.ForValue(data, NullValueStatus.DefinitelyNotNull); + } + + if (method != null) + return VisitorResult.ForValue(data, GetNullableStatus(method)); + + return VisitorResult.ForValue(data, GetNullableStatus(methodResolveResult.TargetResult.Type.GetDefinition())); + } + + static NullValueStatus GetNullableStatus(IEntity entity) + { + if (entity.DeclaringType != null && entity.DeclaringType.Kind == TypeKind.Delegate) { + //Handle Delegate.Invoke method + return GetNullableStatus(entity.DeclaringTypeDefinition); + } + + return GetNullableStatus(fullTypeName => entity.GetAttribute(new FullTypeName(fullTypeName))); + } + + static NullValueStatus GetNullableStatus(IParameter parameter) + { + return GetNullableStatus(fullTypeName => parameter.Attributes.FirstOrDefault(attribute => attribute.AttributeType.FullName == fullTypeName)); + } + + static NullValueStatus GetNullableStatus(Func attributeGetter) + { + if (attributeGetter(AnnotationNames.NotNullAttribute) != null) { + return NullValueStatus.DefinitelyNotNull; + } + if (attributeGetter(AnnotationNames.CanBeNullAttribute) != null) { + return NullValueStatus.PotentiallyNull; + } + return NullValueStatus.Unknown; + } + + public override VisitorResult VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, VariableStatusInfo data) + { + var targetResult = memberReferenceExpression.Target.AcceptVisitor(this, data); + if (targetResult.ThrowsException) + return HandleExpressionResult(memberReferenceExpression, targetResult); + + var variables = targetResult.Variables; + + var memberResolveResult = analysis.context.Resolve(memberReferenceExpression) as MemberResolveResult; + + var targetIdentifier = CSharpUtil.GetInnerMostExpression(memberReferenceExpression.Target) as IdentifierExpression; + if (targetIdentifier != null) { + if (memberResolveResult == null) { + var invocation = memberReferenceExpression.Parent as InvocationExpression; + if (invocation != null) { + memberResolveResult = analysis.context.Resolve(invocation) as MemberResolveResult; + } + } + + if (memberResolveResult != null && memberResolveResult.Member.FullName != "System.Nullable.HasValue") { + var method = memberResolveResult.Member as IMethod; + if (method == null || !method.IsExtensionMethod) { + if (targetResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return HandleExpressionResult(memberReferenceExpression, VisitorResult.ForException(variables)); + } + if (variables [targetIdentifier.Identifier] != NullValueStatus.CapturedUnknown) { + variables = variables.Clone(); + analysis.SetLocalVariableValue(variables, targetIdentifier, NullValueStatus.DefinitelyNotNull); + } + } + } + } + + var returnValue = GetFieldReturnValue(memberResolveResult, data); + return HandleExpressionResult(memberReferenceExpression, variables, returnValue); + } + + static NullValueStatus GetFieldReturnValue(MemberResolveResult memberResolveResult, VariableStatusInfo data) + { + bool isNullable = memberResolveResult == null || IsTypeNullable(memberResolveResult.Type); + if (!isNullable) { + return NullValueStatus.DefinitelyNotNull; + } + + if (memberResolveResult != null) { + return GetNullableStatus(memberResolveResult.Member); + } + + return NullValueStatus.Unknown; + } + + public override VisitorResult VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, VariableStatusInfo data) + { + return HandleExpressionResult(typeReferenceExpression, data, NullValueStatus.Unknown); + + } + + void FixParameter(Expression argument, IList parameters, int parameterIndex, IdentifierExpression identifier, VariableStatusInfo data) + { + NullValueStatus newValue = NullValueStatus.Unknown; + if (argument is NamedArgumentExpression) { + var namedResolveResult = analysis.context.Resolve(argument) as NamedArgumentResolveResult; + if (namedResolveResult != null) { + newValue = GetNullableStatus(namedResolveResult.Parameter); + } + } + else { + var parameter = parameters[parameterIndex]; + newValue = GetNullableStatus(parameter); + } + analysis.SetLocalVariableValue(data, identifier, newValue); + } + + public override VisitorResult VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, VariableStatusInfo data) + { + foreach (var argumentToHandle in objectCreateExpression.Arguments.Select((argument, parameterIndex) => new { argument, parameterIndex })) { + var argument = argumentToHandle.argument; + var parameterIndex = argumentToHandle.parameterIndex; + + var namedArgument = argument as NamedArgumentExpression; + + var directionExpression = (namedArgument == null ? argument : namedArgument.Expression) as DirectionExpression; + if (directionExpression != null) { + var identifier = directionExpression.Expression as IdentifierExpression; + if (identifier != null && data [identifier.Identifier] != NullValueStatus.CapturedUnknown) { + //out and ref parameters do *NOT* capture the variable (since they must stop changing it by the time they return) + data = data.Clone(); + + var constructorResolveResult = analysis.context.Resolve(objectCreateExpression) as CSharpInvocationResolveResult; + if (constructorResolveResult != null) + FixParameter(argument, constructorResolveResult.Member.Parameters, parameterIndex, identifier, data); + } + continue; + } + + var argumentResult = argument.AcceptVisitor(this, data); + if (argumentResult.ThrowsException) + return argumentResult; + + data = argumentResult.Variables; + } + + //Constructors never return null + return HandleExpressionResult(objectCreateExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, VariableStatusInfo data) + { + foreach (var argument in arrayCreateExpression.Arguments) { + var result = argument.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables.Clone(); + } + + if (arrayCreateExpression.Initializer.IsNull) { + return HandleExpressionResult(arrayCreateExpression, data, NullValueStatus.DefinitelyNotNull); + } + + return HandleExpressionResult(arrayCreateExpression, arrayCreateExpression.Initializer.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, VariableStatusInfo data) + { + if (arrayInitializerExpression.IsSingleElement) { + return HandleExpressionResult(arrayInitializerExpression, arrayInitializerExpression.Elements.Single().AcceptVisitor(this, data)); + } + if (!arrayInitializerExpression.Elements.Any()) { + //Empty array + return HandleExpressionResult(arrayInitializerExpression, VisitorResult.ForValue(data, NullValueStatus.Unknown)); + } + + NullValueStatus enumeratedValue = NullValueStatus.UnreachableOrInexistent; + foreach (var element in arrayInitializerExpression.Elements) { + var result = element.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables.Clone(); + enumeratedValue = VariableStatusInfo.CombineStatus(enumeratedValue, result.NullableReturnResult); + + } + return HandleExpressionResult(arrayInitializerExpression, VisitorResult.ForEnumeratedValue(data, enumeratedValue)); + } + + public override VisitorResult VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, VariableStatusInfo data) + { + foreach (var initializer in anonymousTypeCreateExpression.Initializers) { + var result = initializer.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables; + } + + return HandleExpressionResult(anonymousTypeCreateExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitLambdaExpression(LambdaExpression lambdaExpression, VariableStatusInfo data) + { + var newData = data.Clone(); + + var identifiers = lambdaExpression.Descendants.OfType(); + foreach (var identifier in identifiers) { + //Check if it is in a "change-null-state" context + //For instance, x++ does not change the null state + //but `x = y` does. + if (identifier.Parent is AssignmentExpression && identifier.Role == AssignmentExpression.LeftRole) { + var parent = (AssignmentExpression)identifier.Parent; + if (parent.Operator != AssignmentOperatorType.Assign) { + continue; + } + } else { + //No other context matters + //Captured variables are never passed by reference (out/ref) + continue; + } + + //At this point, we know there's a good chance the variable has been changed + var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult; + if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) { + analysis.SetLocalVariableValue(newData, identifier, NullValueStatus.CapturedUnknown); + } + } + + //The lambda itself is known not to be null + return HandleExpressionResult(lambdaExpression, newData, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, VariableStatusInfo data) + { + var newData = data.Clone(); + + var identifiers = anonymousMethodExpression.Descendants.OfType(); + foreach (var identifier in identifiers) { + //Check if it is in a "change-null-state" context + //For instance, x++ does not change the null state + //but `x = y` does. + if (identifier.Parent is AssignmentExpression && identifier.Role == AssignmentExpression.LeftRole) { + var parent = (AssignmentExpression)identifier.Parent; + if (parent.Operator != AssignmentOperatorType.Assign) { + continue; + } + } else { + //No other context matters + //Captured variables are never passed by reference (out/ref) + continue; + } + + //At this point, we know there's a good chance the variable has been changed + var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult; + if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) { + analysis.SetLocalVariableValue(newData, identifier, NullValueStatus.CapturedUnknown); + } + } + + //The anonymous method itself is known not to be null + return HandleExpressionResult(anonymousMethodExpression, newData, NullValueStatus.DefinitelyNotNull); + } + + + public override VisitorResult VisitNamedExpression(NamedExpression namedExpression, VariableStatusInfo data) + { + return HandleExpressionResult(namedExpression, namedExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitAsExpression(AsExpression asExpression, VariableStatusInfo data) + { + var tentativeResult = asExpression.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return tentativeResult; + + NullValueStatus result; + if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + result = NullValueStatus.DefinitelyNull; + } else { + var asResolveResult = analysis.context.Resolve(asExpression) as CastResolveResult; + if (asResolveResult == null || + asResolveResult.IsError || + asResolveResult.Input.Type.Kind == TypeKind.Unknown || + asResolveResult.Type.Kind == TypeKind.Unknown) { + + result = NullValueStatus.Unknown; + } else { + var conversion = new CSharpConversions(analysis.context.Compilation); + var foundConversion = conversion.ExplicitConversion(asResolveResult.Input.Type, asResolveResult.Type); + + if (foundConversion == Conversion.None) { + result = NullValueStatus.DefinitelyNull; + } else if (foundConversion == Conversion.IdentityConversion) { + result = tentativeResult.NullableReturnResult; + } else { + result = NullValueStatus.PotentiallyNull; + } + } + } + return HandleExpressionResult(asExpression, tentativeResult.Variables, result); + } + + public override VisitorResult VisitCastExpression(CastExpression castExpression, VariableStatusInfo data) + { + var tentativeResult = castExpression.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return tentativeResult; + + NullValueStatus result; + if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + result = NullValueStatus.DefinitelyNull; + } else { + result = NullValueStatus.Unknown; + } + + VariableStatusInfo variables = tentativeResult.Variables; + + var resolveResult = analysis.context.Resolve(castExpression) as CastResolveResult; + if (resolveResult != null && !IsTypeNullable(resolveResult.Type)) { + if (result == NullValueStatus.DefinitelyNull) { + return HandleExpressionResult(castExpression, VisitorResult.ForException(tentativeResult.Variables)); + } + + var identifierExpression = CSharpUtil.GetInnerMostExpression(castExpression.Expression) as IdentifierExpression; + if (identifierExpression != null) { + var currentValue = variables [identifierExpression.Identifier]; + if (currentValue != NullValueStatus.CapturedUnknown && + currentValue != NullValueStatus.UnreachableOrInexistent && + currentValue != NullValueStatus.DefinitelyNotNull) { + //DefinitelyNotNull is included in this list because if that's the status + // then we don't need to change anything + + variables = variables.Clone(); + variables [identifierExpression.Identifier] = NullValueStatus.DefinitelyNotNull; + } + } + + result = NullValueStatus.DefinitelyNotNull; + } + + return HandleExpressionResult(castExpression, variables, result); + } + + public override VisitorResult VisitIsExpression(IsExpression isExpression, VariableStatusInfo data) + { + var tentativeResult = isExpression.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return tentativeResult; + + //TODO: Consider, for instance: new X() is X. The result is known to be true, so we can use KnownBoolValue + return HandleExpressionResult(isExpression, tentativeResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitDirectionExpression(DirectionExpression directionExpression, VariableStatusInfo data) + { + return HandleExpressionResult(directionExpression, directionExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitCheckedExpression(CheckedExpression checkedExpression, VariableStatusInfo data) + { + return HandleExpressionResult(checkedExpression, checkedExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitUncheckedExpression(UncheckedExpression uncheckedExpression, VariableStatusInfo data) + { + return HandleExpressionResult(uncheckedExpression, uncheckedExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, VariableStatusInfo data) + { + return HandleExpressionResult(thisReferenceExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitIndexerExpression(IndexerExpression indexerExpression, VariableStatusInfo data) + { + var tentativeResult = indexerExpression.Target.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return tentativeResult; + + data = tentativeResult.Variables; + + foreach (var argument in indexerExpression.Arguments) { + var result = argument.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables.Clone(); + } + + IdentifierExpression targetAsIdentifier = CSharpUtil.GetInnerMostExpression(indexerExpression.Target) as IdentifierExpression; + if (targetAsIdentifier != null) { + if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) + return HandleExpressionResult(indexerExpression, VisitorResult.ForException(data)); + + //If this doesn't cause an exception, then the target is not null + //But we won't set it if it has been changed + var descendentIdentifiers = indexerExpression.Arguments + .SelectMany(argument => argument.DescendantsAndSelf).OfType(); + if (!descendentIdentifiers.Any(identifier => identifier.Identifier == targetAsIdentifier.Identifier)) { + //TODO: this check might be improved to include more legitimate cases + //A good check will necessarily have to consider captured variables + data = data.Clone(); + analysis.SetLocalVariableValue(data, targetAsIdentifier, NullValueStatus.DefinitelyNotNull); + } + } + + var indexerResolveResult = analysis.context.Resolve(indexerExpression) as CSharpInvocationResolveResult; + bool isNullable = indexerResolveResult == null || IsTypeNullable(indexerResolveResult.Type); + + var returnValue = isNullable ? NullValueStatus.Unknown : NullValueStatus.DefinitelyNotNull; + return HandleExpressionResult(indexerExpression, data, returnValue); + } + + public override VisitorResult VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, VariableStatusInfo data) + { + return HandleExpressionResult(baseReferenceExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitTypeOfExpression(TypeOfExpression typeOfExpression, VariableStatusInfo data) + { + return HandleExpressionResult(typeOfExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitSizeOfExpression(SizeOfExpression sizeOfExpression, VariableStatusInfo data) + { + return HandleExpressionResult(sizeOfExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, VariableStatusInfo data) + { + var targetResult = pointerReferenceExpression.Target.AcceptVisitor(this, data); + if (targetResult.ThrowsException) + return targetResult; + return HandleExpressionResult(pointerReferenceExpression, targetResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitStackAllocExpression(StackAllocExpression stackAllocExpression, VariableStatusInfo data) + { + var countResult = stackAllocExpression.CountExpression.AcceptVisitor(this, data); + if (countResult.ThrowsException) + return countResult; + return HandleExpressionResult(stackAllocExpression, countResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, VariableStatusInfo data) + { + return HandleExpressionResult(namedArgumentExpression, namedArgumentExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, VariableStatusInfo data) + { + throw new NotImplementedException(); + } + + public override VisitorResult VisitQueryExpression(QueryExpression queryExpression, VariableStatusInfo data) + { + VariableStatusInfo outgoingData = data.Clone(); + NullValueStatus? outgoingEnumeratedValue = null; + var clauses = queryExpression.Clauses.ToList(); + + var backtracingClauses = (from item in clauses.Select((clause, i) => new { clause, i }) + where item.clause is QueryFromClause || item.clause is QueryJoinClause || item.clause is QueryContinuationClause + select item.i).ToList(); + + var beforeClauseVariableStates = Enumerable.Range(0, clauses.Count).ToDictionary(clauseIndex => clauseIndex, + clauseIndex => new VariableStatusInfo()); + var afterClauseVariableStates = Enumerable.Range(0, clauses.Count).ToDictionary(clauseIndex => clauseIndex, + clauseIndex => new VariableStatusInfo()); + + VisitorResult lastValidResult = null; + int currentClauseIndex = 0; + for (;;) { + VisitorResult result = null; + QueryClause clause = null; + bool backtrack = false; + + if (currentClauseIndex >= clauses.Count) { + backtrack = true; + } else { + clause = clauses [currentClauseIndex]; + beforeClauseVariableStates [currentClauseIndex].ReceiveIncoming(data); + result = clause.AcceptVisitor(this, data); + data = result.Variables; + lastValidResult = result; + if (result.KnownBoolResult == false) { + backtrack = true; + } + if (result.ThrowsException) { + //Don't backtrack. Exceptions completely stop the query. + break; + } + else { + afterClauseVariableStates [currentClauseIndex].ReceiveIncoming(data); + } + } + + if (backtrack) { + int? newIndex; + for (;;) { + newIndex = backtracingClauses.LastOrDefault(index => index < currentClauseIndex); + if (newIndex == null) { + //We've reached the end + break; + } + + currentClauseIndex = (int)newIndex + 1; + + if (!beforeClauseVariableStates[currentClauseIndex].ReceiveIncoming(lastValidResult.Variables)) { + newIndex = null; + break; + } + } + + if (newIndex == null) { + break; + } + + } else { + if (clause is QuerySelectClause) { + outgoingData.ReceiveIncoming(data); + if (outgoingEnumeratedValue == null) + outgoingEnumeratedValue = result.EnumeratedValueResult; + else + outgoingEnumeratedValue = VariableStatusInfo.CombineStatus(outgoingEnumeratedValue.Value, result.EnumeratedValueResult); + } + + ++currentClauseIndex; + } + } + + var finalData = new VariableStatusInfo(); + var endingClauseIndices = from item in clauses.Select((clause, i) => new { clause, i }) + let clause = item.clause + where clause is QueryFromClause || + clause is QueryContinuationClause || + clause is QueryJoinClause || + clause is QuerySelectClause || + clause is QueryWhereClause + select item.i; + foreach (var clauseIndex in endingClauseIndices) { + finalData.ReceiveIncoming(afterClauseVariableStates [clauseIndex]); + } + + return VisitorResult.ForEnumeratedValue(finalData, outgoingEnumeratedValue ?? NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, VariableStatusInfo data) + { + return IntroduceVariableFromEnumeratedValue(queryContinuationClause.Identifier, queryContinuationClause.PrecedingQuery, data); + } + + VisitorResult IntroduceVariableFromEnumeratedValue(string newVariable, Expression expression, VariableStatusInfo data) + { + var result = expression.AcceptVisitor(this, data); + var newVariables = result.Variables.Clone(); + newVariables[newVariable] = result.EnumeratedValueResult; + return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryFromClause(QueryFromClause queryFromClause, VariableStatusInfo data) + { + return IntroduceVariableFromEnumeratedValue(queryFromClause.Identifier, queryFromClause.Expression, data); + } + + public override VisitorResult VisitQueryJoinClause(QueryJoinClause queryJoinClause, VariableStatusInfo data) + { + //TODO: Check if this really works in weird edge-cases. + var tentativeResult = IntroduceVariableFromEnumeratedValue(queryJoinClause.JoinIdentifier, queryJoinClause.InExpression, data); + tentativeResult = queryJoinClause.OnExpression.AcceptVisitor(this, tentativeResult.Variables); + tentativeResult = queryJoinClause.EqualsExpression.AcceptVisitor(this, tentativeResult.Variables); + + if (queryJoinClause.IsGroupJoin) { + var newVariables = tentativeResult.Variables.Clone(); + analysis.SetLocalVariableValue(newVariables, queryJoinClause.IntoIdentifierToken, NullValueStatus.DefinitelyNotNull); + return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown); + } + + return tentativeResult; + } + + public override VisitorResult VisitQueryLetClause(QueryLetClause queryLetClause, VariableStatusInfo data) + { + var result = queryLetClause.Expression.AcceptVisitor(this, data); + + string newVariable = queryLetClause.Identifier; + var newVariables = result.Variables.Clone(); + newVariables [newVariable] = result.NullableReturnResult; + + return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQuerySelectClause(QuerySelectClause querySelectClause, VariableStatusInfo data) + { + var result = querySelectClause.Expression.AcceptVisitor(this, data); + + //The value of the expression in select becomes the "enumerated" value + return VisitorResult.ForEnumeratedValue(result.Variables, result.NullableReturnResult); + } + + public override VisitorResult VisitQueryWhereClause(QueryWhereClause queryWhereClause, VariableStatusInfo data) + { + var result = queryWhereClause.Condition.AcceptVisitor(this, data); + + return VisitorResult.ForEnumeratedValue(result.TruePathVariables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryOrderClause(QueryOrderClause queryOrderClause, VariableStatusInfo data) + { + foreach (var ordering in queryOrderClause.Orderings) { + data = ordering.AcceptVisitor(this, data).Variables; + } + + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryOrdering(QueryOrdering queryOrdering, VariableStatusInfo data) + { + return VisitorResult.ForValue(queryOrdering.Expression.AcceptVisitor(this, data).Variables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryGroupClause(QueryGroupClause queryGroupClause, VariableStatusInfo data) + { + var projectionResult = queryGroupClause.Projection.AcceptVisitor(this, data); + data = projectionResult.Variables; + data = queryGroupClause.Key.AcceptVisitor(this, data).Variables; + + return VisitorResult.ForEnumeratedValue(data, projectionResult.NullableReturnResult); + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueStatus.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueStatus.cs new file mode 100644 index 000000000..f6816dfd5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueStatus.cs @@ -0,0 +1,84 @@ +// +// NullValueAnalysis.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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.Analysis +{ + /// + /// Represents the null value status of a variable at a specific location. + /// + public enum NullValueStatus + { + /// + /// The value of the variable is unknown, possibly due to limitations + /// of the null value analysis. + /// + Unknown = 0, + /// + /// The value of the variable is unknown and even assigning it to a + /// value won't change its state, since it has been captured by a lambda + /// that may change it at any time (potentially even from a different thread). + /// Only going out of scope and creating a new variable may change the value + /// of this variable. + /// + CapturedUnknown, + /// + /// This variable is potentially unassigned. + /// + Unassigned, + /// + /// The value of the variable is provably null. + /// + DefinitelyNull, + /// + /// The value of the variable might or might not be null + /// + PotentiallyNull, + /// + /// The value of the variable is provably not null + /// + DefinitelyNotNull, + /// + /// The position of this node is unreachable, therefore the value + /// of the variable is not meaningful. + /// Alternatively, it might mean no local variable exists with the requested name. + /// + UnreachableOrInexistent, + /// + /// The analyser has encountered an error when attempting to find the value + /// of this variable. + /// + Error + } + + public static class NullValueStatusExtensions + { + public static bool IsDefiniteValue (this NullValueStatus self) { + return self == NullValueStatus.DefinitelyNull || self == NullValueStatus.DefinitelyNotNull; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs new file mode 100644 index 000000000..ce64f5f3b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs @@ -0,0 +1,209 @@ +// Copyright (c) 2010-2013 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.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Statement reachability analysis. + /// + public sealed class ReachabilityAnalysis + { + HashSet reachableStatements = new HashSet(); + HashSet reachableEndPoints = new HashSet(); + HashSet visitedNodes = new HashSet(); + Stack stack = new Stack(); + RecursiveDetectorVisitor recursiveDetectorVisitor = null; + + private ReachabilityAnalysis() {} + + public static ReachabilityAnalysis Create(Statement statement, CSharpAstResolver resolver = null, RecursiveDetectorVisitor recursiveDetectorVisitor = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var cfgBuilder = new ControlFlowGraphBuilder(); + var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, cancellationToken); + return Create(cfg, recursiveDetectorVisitor, cancellationToken); + } + + internal static ReachabilityAnalysis Create(Statement statement, Func resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken) + { + var cfgBuilder = new ControlFlowGraphBuilder(); + var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, typeResolveContext, cancellationToken); + return Create(cfg, null, cancellationToken); + } + + public static ReachabilityAnalysis Create(IList controlFlowGraph, RecursiveDetectorVisitor recursiveDetectorVisitor = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (controlFlowGraph == null) + throw new ArgumentNullException("controlFlowGraph"); + ReachabilityAnalysis ra = new ReachabilityAnalysis(); + ra.recursiveDetectorVisitor = recursiveDetectorVisitor; + // Analysing a null node can result in an empty control flow graph + if (controlFlowGraph.Count > 0) { + ra.stack.Push(controlFlowGraph[0]); + while (ra.stack.Count > 0) { + cancellationToken.ThrowIfCancellationRequested(); + ra.MarkReachable(ra.stack.Pop()); + } + } + ra.stack = null; + ra.visitedNodes = null; + return ra; + } + + void MarkReachable(ControlFlowNode node) + { + if (node.PreviousStatement != null) { + if (node.PreviousStatement is LabelStatement) { + reachableStatements.Add(node.PreviousStatement); + } + reachableEndPoints.Add(node.PreviousStatement); + } + if (node.NextStatement != null) { + reachableStatements.Add(node.NextStatement); + if (IsRecursive(node.NextStatement)) { + return; + } + } + foreach (var edge in node.Outgoing) { + if (visitedNodes.Add(edge.To)) + stack.Push(edge.To); + } + } + + bool IsRecursive(Statement statement) + { + return recursiveDetectorVisitor != null && statement.AcceptVisitor(recursiveDetectorVisitor); + } + + public IEnumerable ReachableStatements { + get { return reachableStatements; } + } + + public bool IsReachable(Statement statement) + { + return reachableStatements.Contains(statement); + } + + public bool IsEndpointReachable(Statement statement) + { + return reachableEndPoints.Contains(statement); + } + + public class RecursiveDetectorVisitor : DepthFirstAstVisitor + { + public override bool VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + if (conditionalExpression.Condition.AcceptVisitor(this)) + return true; + + if (!conditionalExpression.TrueExpression.AcceptVisitor(this)) + return false; + + return conditionalExpression.FalseExpression.AcceptVisitor(this); + } + + public override bool VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { + return binaryOperatorExpression.Left.AcceptVisitor(this); + } + return base.VisitBinaryOperatorExpression(binaryOperatorExpression); + } + + public override bool VisitIfElseStatement(IfElseStatement ifElseStatement) + { + if (ifElseStatement.Condition.AcceptVisitor(this)) + return true; + + if (!ifElseStatement.TrueStatement.AcceptVisitor(this)) + return false; + + //No need to worry about null ast nodes, since AcceptVisitor will just + //return false in those cases + return ifElseStatement.FalseStatement.AcceptVisitor(this); + } + + public override bool VisitForeachStatement(ForeachStatement foreachStatement) + { + //Even if the body is always recursive, the function may stop if the collection + // is empty. + return foreachStatement.InExpression.AcceptVisitor(this); + } + + public override bool VisitForStatement(ForStatement forStatement) + { + if (forStatement.Initializers.Any(initializer => initializer.AcceptVisitor(this))) + return true; + + return forStatement.Condition.AcceptVisitor(this); + } + + public override bool VisitSwitchStatement(SwitchStatement switchStatement) + { + if (switchStatement.Expression.AcceptVisitor(this)) { + return true; + } + + bool foundDefault = false; + foreach (var section in switchStatement.SwitchSections) { + foundDefault = foundDefault || section.CaseLabels.Any(label => label.Expression.IsNull); + if (!section.AcceptVisitor(this)) + return false; + } + + return foundDefault; + } + + public override bool VisitBlockStatement(BlockStatement blockStatement) + { + //If the block has a recursive statement, then that statement will be visited + //individually by the CFG construction algorithm later. + return false; + } + + protected override bool VisitChildren(AstNode node) + { + return VisitNodeList(node.Children); + } + + bool VisitNodeList(IEnumerable nodes) { + return nodes.Any(node => node.AcceptVisitor(this)); + } + + public override bool VisitQueryExpression(QueryExpression queryExpression) + { + //We only care about the first from clause because: + //in "from x in Method() select x", Method() might be recursive + //but in "from x in Bar() from y in Method() select x + y", even if Method() is recursive + //Bar might still be empty. + var queryFromClause = queryExpression.Clauses.OfType().FirstOrDefault(); + if (queryFromClause == null) + return true; + return queryFromClause.AcceptVisitor(this); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs new file mode 100644 index 000000000..0528d4885 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs @@ -0,0 +1,685 @@ +// Copyright (c) 2010-2013 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.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using System.Threading; +using ICSharpCode.NRefactory.CSharp.Completion; +using System.Collections.ObjectModel; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// C# Semantic highlighter. + /// + public abstract class SemanticHighlightingVisitor : DepthFirstAstVisitor + { + protected CancellationToken cancellationToken = default (CancellationToken); + + protected TColor defaultTextColor; + protected TColor referenceTypeColor; + protected TColor valueTypeColor; + protected TColor interfaceTypeColor; + protected TColor enumerationTypeColor; + protected TColor typeParameterTypeColor; + protected TColor delegateTypeColor; + + protected TColor methodCallColor; + protected TColor methodDeclarationColor; + + protected TColor eventDeclarationColor; + protected TColor eventAccessColor; + + protected TColor propertyDeclarationColor; + protected TColor propertyAccessColor; + + protected TColor fieldDeclarationColor; + protected TColor fieldAccessColor; + + protected TColor variableDeclarationColor; + protected TColor variableAccessColor; + + protected TColor parameterDeclarationColor; + protected TColor parameterAccessColor; + + protected TColor valueKeywordColor; + protected TColor externAliasKeywordColor; + protected TColor varKeywordTypeColor; + + /// + /// Used for 'in' modifiers on type parameters. + /// + /// + /// 'in' may have a different color when used with 'foreach'. + /// 'out' is not colored by semantic highlighting, as syntax highlighting can already detect it as a parameter modifier. + /// + protected TColor parameterModifierColor; + + /// + /// Used for inactive code (excluded by preprocessor or ConditionalAttribute) + /// + protected TColor inactiveCodeColor; + + protected TColor stringFormatItemColor; + + + protected TColor syntaxErrorColor; + + protected TextLocation regionStart; + protected TextLocation regionEnd; + + protected CSharpAstResolver resolver; + protected bool isInAccessorContainingValueParameter; + + protected abstract void Colorize(TextLocation start, TextLocation end, TColor color); + + #region Colorize helper methods + protected void Colorize(Identifier identifier, ResolveResult rr) + { + if (identifier.IsNull) + return; + if (rr.IsError) { + Colorize(identifier, syntaxErrorColor); + return; + } + if (rr is TypeResolveResult) { + if (blockDepth > 0 && identifier.Name == "var" && rr.Type.Kind != TypeKind.Null && rr.Type.Name != "var" ) { + Colorize(identifier, varKeywordTypeColor); + return; + } + + TColor color; + if (TryGetTypeHighlighting (rr.Type.Kind, out color)) { + Colorize(identifier, color); + } + return; + } + var mrr = rr as MemberResolveResult; + if (mrr != null) { + TColor color; + if (TryGetMemberColor (mrr.Member, out color)) { + Colorize(identifier, color); + return; + } + } + + if (rr is MethodGroupResolveResult) { + Colorize (identifier, methodCallColor); + return; + } + + var localResult = rr as LocalResolveResult; + if (localResult != null) { + if (localResult.Variable is IParameter) { + Colorize (identifier, parameterAccessColor); + } else { + Colorize (identifier, variableAccessColor); + } + } + + + VisitIdentifier(identifier); // un-colorize contextual keywords + } + + protected void Colorize(AstNode node, TColor color) + { + if (node.IsNull) + return; + Colorize(node.StartLocation, node.EndLocation, color); + } + #endregion + + protected override void VisitChildren(AstNode node) + { + for (var child = node.FirstChild; child != null; child = child.NextSibling) { + if (child.StartLocation < regionEnd && child.EndLocation > regionStart) + child.AcceptVisitor(this); + } + } + + /// + /// Visit all children of node until (but excluding) end. + /// If end is a null node, nothing will be visited. + /// + protected void VisitChildrenUntil(AstNode node, AstNode end) + { + if (end.IsNull) + return; + Debug.Assert(node == end.Parent); + for (var child = node.FirstChild; child != end; child = child.NextSibling) { + cancellationToken.ThrowIfCancellationRequested(); + if (child.StartLocation < regionEnd && child.EndLocation > regionStart) + child.AcceptVisitor(this); + } + } + + /// + /// Visit all children of node after (excluding) start. + /// If start is a null node, all children will be visited. + /// + protected void VisitChildrenAfter(AstNode node, AstNode start) + { + Debug.Assert(start.IsNull || start.Parent == node); + for (var child = (start.IsNull ? node.FirstChild : start.NextSibling); child != null; child = child.NextSibling) { + cancellationToken.ThrowIfCancellationRequested(); + if (child.StartLocation < regionEnd && child.EndLocation > regionStart) + child.AcceptVisitor(this); + } + } + + public override void VisitIdentifier(Identifier identifier) + { + switch (identifier.Name) { + case "add": + case "async": + case "await": + case "get": + case "partial": + case "remove": + case "set": + case "where": + case "yield": + case "from": + case "select": + case "group": + case "into": + case "orderby": + case "join": + case "let": + case "on": + case "equals": + case "by": + case "ascending": + case "descending": + case "dynamic": + case "var": + // Reset color of contextual keyword to default if it's used as an identifier. + // Note that this method does not get called when 'var' or 'dynamic' is used as a type, + // because types get highlighted with valueTypeColor/referenceTypeColor instead. + Colorize(identifier, defaultTextColor); + break; + case "global": + // Reset color of 'global' keyword to default unless its used as part of 'global::'. + MemberType parentMemberType = identifier.Parent as MemberType; + if (parentMemberType == null || !parentMemberType.IsDoubleColon) + Colorize(identifier, defaultTextColor); + break; + } + // "value" is handled in VisitIdentifierExpression() + // "alias" is handled in VisitExternAliasDeclaration() + } + + public override void VisitSimpleType(SimpleType simpleType) + { + var identifierToken = simpleType.IdentifierToken; + VisitChildrenUntil(simpleType, identifierToken); + Colorize(identifierToken, resolver.Resolve(simpleType, cancellationToken)); + VisitChildrenAfter(simpleType, identifierToken); + } + + public override void VisitMemberType(MemberType memberType) + { + var memberNameToken = memberType.MemberNameToken; + VisitChildrenUntil(memberType, memberNameToken); + Colorize(memberNameToken, resolver.Resolve(memberType, cancellationToken)); + VisitChildrenAfter(memberType, memberNameToken); + } + + public override void VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + var identifier = identifierExpression.IdentifierToken; + VisitChildrenUntil(identifierExpression, identifier); + if (isInAccessorContainingValueParameter && identifierExpression.Identifier == "value") { + Colorize(identifier, valueKeywordColor); + } else { + Colorize(identifier, resolver.Resolve(identifierExpression, cancellationToken)); + } + VisitChildrenAfter(identifierExpression, identifier); + } + + public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + var memberNameToken = memberReferenceExpression.MemberNameToken; + VisitChildrenUntil(memberReferenceExpression, memberNameToken); + ResolveResult rr = resolver.Resolve(memberReferenceExpression, cancellationToken); + Colorize(memberNameToken, rr); + VisitChildrenAfter(memberReferenceExpression, memberNameToken); + } + + void HighlightStringFormatItems(PrimitiveExpression expr) + { + if (!(expr.Value is string)) + return; + int line = expr.StartLocation.Line; + int col = expr.StartLocation.Column; + TextLocation start = TextLocation.Empty; + for (int i = 0; i < expr.LiteralValue.Length; i++) { + char ch = expr.LiteralValue [i]; + + if (NewLine.GetDelimiterType(ch, i + 1 < expr.LiteralValue.Length ? expr.LiteralValue [i + 1] : '\0') != UnicodeNewline.Unknown) { + line++; + col = 1; + continue; + } + + + if (ch == '{' && start.IsEmpty) { + char next = i + 1 < expr.LiteralValue.Length ? expr.LiteralValue [i + 1] : '\0'; + if (next == '{') { + i++; + col += 2; + continue; + } + start = new TextLocation(line, col); + } + if (ch == '}' &&!start.IsEmpty) { + Colorize(start, new TextLocation(line, col + 1), stringFormatItemColor); + start = TextLocation.Empty; + } + col++; + } + + } + + public override void VisitInvocationExpression(InvocationExpression invocationExpression) + { + Expression target = invocationExpression.Target; + if (target is IdentifierExpression || target is MemberReferenceExpression || target is PointerReferenceExpression) { + var invocationRR = resolver.Resolve(invocationExpression, cancellationToken) as CSharpInvocationResolveResult; + if (invocationRR != null) { + if (invocationExpression.Parent is ExpressionStatement && (IsInactiveConditionalMethod(invocationRR.Member) || IsEmptyPartialMethod(invocationRR.Member))) { + // mark the whole invocation statement as inactive code + Colorize(invocationExpression.Parent, inactiveCodeColor); + return; + } + + Expression fmtArgumets; + IList args; + if (invocationRR.Arguments.Count > 1 && FormatStringHelper.TryGetFormattingParameters(invocationRR, invocationExpression, out fmtArgumets, out args, null)) { + var expr = invocationExpression.Arguments.First() as PrimitiveExpression; + if (expr != null) + HighlightStringFormatItems(expr); + } + } + + VisitChildrenUntil(invocationExpression, target); + + // highlight the method call + var identifier = target.GetChildByRole(Roles.Identifier); + VisitChildrenUntil(target, identifier); + if (invocationRR != null && !invocationRR.IsDelegateInvocation) { + Colorize(identifier, methodCallColor); + } else { + ResolveResult targetRR = resolver.Resolve(target, cancellationToken); + Colorize(identifier, targetRR); + } + VisitChildrenAfter(target, identifier); + VisitChildrenAfter(invocationExpression, target); + } else { + VisitChildren(invocationExpression); + } + } + + #region IsInactiveConditional helper methods + bool IsInactiveConditionalMethod(IParameterizedMember member) + { + if (member.SymbolKind != SymbolKind.Method || member.ReturnType.Kind != TypeKind.Void) + return false; + foreach (var baseMember in InheritanceHelper.GetBaseMembers(member, false)) { + if (IsInactiveConditional (baseMember.Attributes)) + return true; + } + return IsInactiveConditional(member.Attributes); + } + + static bool IsEmptyPartialMethod(IParameterizedMember member) + { + if (member.SymbolKind != SymbolKind.Method || member.ReturnType.Kind != TypeKind.Void) + return false; + var method = (IMethod)member; + return method.IsPartial && !method.HasBody; + } + + bool IsInactiveConditional(IList attributes) + { + bool hasConditionalAttribute = false; + foreach (var attr in attributes) { + if (attr.AttributeType.Name == "ConditionalAttribute" && attr.AttributeType.Namespace == "System.Diagnostics" && attr.PositionalArguments.Count == 1) { + string symbol = attr.PositionalArguments[0].ConstantValue as string; + if (symbol != null) { + hasConditionalAttribute = true; + var cu = this.resolver.RootNode as SyntaxTree; + if (cu != null) { + if (cu.ConditionalSymbols.Contains(symbol)) + return false; // conditional is active + } + } + } + } + + return hasConditionalAttribute; + } + #endregion + + public override void VisitExternAliasDeclaration (ExternAliasDeclaration externAliasDeclaration) + { + var aliasToken = externAliasDeclaration.AliasToken; + VisitChildrenUntil(externAliasDeclaration, aliasToken); + Colorize (aliasToken, externAliasKeywordColor); + VisitChildrenAfter(externAliasDeclaration, aliasToken); + } + + public override void VisitAccessor(Accessor accessor) + { + isInAccessorContainingValueParameter = accessor.Role != PropertyDeclaration.GetterRole; + try { + VisitChildren(accessor); + } finally { + isInAccessorContainingValueParameter = false; + } + } + + bool CheckInterfaceImplementation (EntityDeclaration entityDeclaration) + { + var result = resolver.Resolve (entityDeclaration, cancellationToken) as MemberResolveResult; + if (result == null) + return false; + if (result.Member.ImplementedInterfaceMembers.Count == 0) { + Colorize (entityDeclaration.NameToken, syntaxErrorColor); + return false; + } + return true; + } + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + var nameToken = methodDeclaration.NameToken; + VisitChildrenUntil(methodDeclaration, nameToken); + if (!methodDeclaration.PrivateImplementationType.IsNull) { + if (!CheckInterfaceImplementation (methodDeclaration)) { + VisitChildrenAfter(methodDeclaration, nameToken); + return; + } + } + Colorize(nameToken, methodDeclarationColor); + VisitChildrenAfter(methodDeclaration, nameToken); + } + + public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + var nameToken = parameterDeclaration.NameToken; + VisitChildrenUntil(parameterDeclaration, nameToken); + Colorize(nameToken, parameterDeclarationColor); + VisitChildrenAfter(parameterDeclaration, nameToken); + } + + public override void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + var nameToken = eventDeclaration.NameToken; + VisitChildrenUntil(eventDeclaration, nameToken); + Colorize(nameToken, eventDeclarationColor); + VisitChildrenAfter(eventDeclaration, nameToken); + } + + public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + var nameToken = eventDeclaration.NameToken; + VisitChildrenUntil(eventDeclaration, nameToken); + if (!eventDeclaration.PrivateImplementationType.IsNull) { + if (!CheckInterfaceImplementation (eventDeclaration)) { + VisitChildrenAfter(eventDeclaration, nameToken); + return; + } + } + Colorize(nameToken, eventDeclarationColor); + VisitChildrenAfter(eventDeclaration, nameToken); + } + + public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + var nameToken = propertyDeclaration.NameToken; + VisitChildrenUntil(propertyDeclaration, nameToken); + if (!propertyDeclaration.PrivateImplementationType.IsNull) { + if (!CheckInterfaceImplementation (propertyDeclaration)) { + VisitChildrenAfter(propertyDeclaration, nameToken); + return; + } + } + Colorize(nameToken, propertyDeclarationColor); + VisitChildrenAfter(propertyDeclaration, nameToken); + } + + public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + base.VisitIndexerDeclaration(indexerDeclaration); + if (!indexerDeclaration.PrivateImplementationType.IsNull) { + CheckInterfaceImplementation (indexerDeclaration); + } + } + + public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + fieldDeclaration.ReturnType.AcceptVisitor (this); + foreach (var init in fieldDeclaration.Variables) { + Colorize (init.NameToken, fieldDeclarationColor); + init.Initializer.AcceptVisitor (this); + } + } + + public override void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + fixedFieldDeclaration.ReturnType.AcceptVisitor (this); + foreach (var init in fixedFieldDeclaration.Variables) { + Colorize (init.NameToken, fieldDeclarationColor); + init.CountExpression.AcceptVisitor (this); + } + } + + public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + HandleConstructorOrDestructor(constructorDeclaration); + } + + public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + HandleConstructorOrDestructor(destructorDeclaration); + } + + void HandleConstructorOrDestructor(AstNode constructorDeclaration) + { + Identifier nameToken = constructorDeclaration.GetChildByRole(Roles.Identifier); + VisitChildrenUntil(constructorDeclaration, nameToken); + var currentTypeDef = resolver.GetResolverStateBefore(constructorDeclaration).CurrentTypeDefinition; + if (currentTypeDef != null && nameToken.Name == currentTypeDef.Name) { + TColor color; + if (TryGetTypeHighlighting (currentTypeDef.Kind, out color)) + Colorize(nameToken, color); + } + VisitChildrenAfter(constructorDeclaration, nameToken); + } + + bool TryGetMemberColor(IMember member, out TColor color) + { + switch (member.SymbolKind) { + case SymbolKind.Field: + color = fieldAccessColor; + return true; + case SymbolKind.Property: + color = propertyAccessColor; + return true; + case SymbolKind.Event: + color = eventAccessColor; + return true; + case SymbolKind.Method: + color = methodCallColor; + return true; + case SymbolKind.Constructor: + case SymbolKind.Destructor: + return TryGetTypeHighlighting (member.DeclaringType.Kind, out color); + default: + color = default (TColor); + return false; + } + } + + TColor GetTypeHighlighting (ClassType classType) + { + switch (classType) { + case ClassType.Class: + return referenceTypeColor; + case ClassType.Struct: + return valueTypeColor; + case ClassType.Interface: + return interfaceTypeColor; + case ClassType.Enum: + return enumerationTypeColor; + default: + throw new InvalidOperationException ("Unknown class type :" + classType); + } + } + + bool TryGetTypeHighlighting (TypeKind kind, out TColor color) + { + switch (kind) { + case TypeKind.Class: + color = referenceTypeColor; + return true; + case TypeKind.Struct: + color = valueTypeColor; + return true; + case TypeKind.Interface: + color = interfaceTypeColor; + return true; + case TypeKind.Enum: + color = enumerationTypeColor; + return true; + case TypeKind.TypeParameter: + color = typeParameterTypeColor; + return true; + case TypeKind.Delegate: + color = delegateTypeColor; + return true; + case TypeKind.Unknown: + case TypeKind.Null: + color = syntaxErrorColor; + return true; + default: + color = default (TColor); + return false; + } + } + + public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + var nameToken = typeDeclaration.NameToken; + VisitChildrenUntil(typeDeclaration, nameToken); + Colorize(nameToken, GetTypeHighlighting (typeDeclaration.ClassType)); + VisitChildrenAfter(typeDeclaration, nameToken); + } + + public override void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + if (typeParameterDeclaration.Variance == VarianceModifier.Contravariant) + Colorize(typeParameterDeclaration.VarianceToken, parameterModifierColor); + + // bool isValueType = false; + // if (typeParameterDeclaration.Parent != null) { + // foreach (var constraint in typeParameterDeclaration.Parent.GetChildrenByRole(Roles.Constraint)) { + // if (constraint.TypeParameter.Identifier == typeParameterDeclaration.Name) { + // isValueType = constraint.BaseTypes.OfType().Any(p => p.Keyword == "struct"); + // } + // } + // } + var nameToken = typeParameterDeclaration.NameToken; + VisitChildrenUntil(typeParameterDeclaration, nameToken); + Colorize(nameToken, typeParameterTypeColor); /*isValueType ? valueTypeColor : referenceTypeColor*/ + VisitChildrenAfter(typeParameterDeclaration, nameToken); + } + + public override void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + var nameToken = delegateDeclaration.NameToken; + VisitChildrenUntil(delegateDeclaration, nameToken); + Colorize(nameToken, delegateTypeColor); + VisitChildrenAfter(delegateDeclaration, nameToken); + } + + public override void VisitVariableInitializer(VariableInitializer variableInitializer) + { + var nameToken = variableInitializer.NameToken; + VisitChildrenUntil(variableInitializer, nameToken); + if (variableInitializer.Parent is FieldDeclaration) { + Colorize(nameToken, fieldDeclarationColor); + } else if (variableInitializer.Parent is EventDeclaration) { + Colorize(nameToken, eventDeclarationColor); + } else { + Colorize(nameToken, variableDeclarationColor); + } + VisitChildrenAfter(variableInitializer, nameToken); + } + + public override void VisitComment(Comment comment) + { + if (comment.CommentType == CommentType.InactiveCode) { + Colorize(comment, inactiveCodeColor); + } + } + + public override void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + } + + public override void VisitAttribute(ICSharpCode.NRefactory.CSharp.Attribute attribute) + { + ITypeDefinition attrDef = resolver.Resolve(attribute.Type, cancellationToken).Type.GetDefinition(); + if (attrDef != null && IsInactiveConditional(attrDef.Attributes)) { + Colorize(attribute, inactiveCodeColor); + } else { + VisitChildren(attribute); + } + } + + public override void VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) + { + foreach (var a in arrayInitializerExpression.Elements) { + var namedElement = a as NamedExpression; + if (namedElement != null) { + var result = resolver.Resolve (namedElement, cancellationToken); + if (result.IsError) + Colorize (namedElement.NameToken, syntaxErrorColor); + namedElement.Expression.AcceptVisitor (this); + } else { + a.AcceptVisitor (this); + } + } + } + + int blockDepth; + public override void VisitBlockStatement(BlockStatement blockStatement) + { + blockDepth++; + base.VisitBlockStatement(blockStatement); + blockDepth--; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs new file mode 100644 index 000000000..8676beb67 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs @@ -0,0 +1,1042 @@ +// +// AstNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class AstNode : AbstractAnnotatable, ICSharpCode.NRefactory.TypeSystem.IFreezable, PatternMatching.INode, ICloneable + { + // 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 (); + + sealed class NullAstNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator AstNode (PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder (pattern) : null; + } + + sealed class PatternPlaceholder : AstNode, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder (PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder (this, child, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + return child.DoMatch (other, match); + } + + bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection (role, pos, match, backtrackingInfo); + } + } + #endregion + + AstNode parent; + AstNode prevSibling; + AstNode nextSibling; + AstNode firstChild; + AstNode lastChild; + + // 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; + } + + public virtual bool IsNull { + get { + return false; + } + } + + public virtual TextLocation StartLocation { + get { + var child = firstChild; + if (child == null) + return TextLocation.Empty; + return child.StartLocation; + } + } + + public virtual TextLocation EndLocation { + get { + var child = lastChild; + if (child == null) + return TextLocation.Empty; + return child.EndLocation; + } + } + + public DomRegion Region { + get { + return new DomRegion (StartLocation, EndLocation); + } + } + + /// + /// Gets the region from StartLocation to EndLocation for this node. + /// The file name of the region is set based on the parent SyntaxTree's file name. + /// If this node is not connected to a whole compilation, the file name will be null. + /// + public ICSharpCode.NRefactory.TypeSystem.DomRegion GetRegion() + { + var syntaxTree = (this.Ancestors.LastOrDefault() ?? this) as SyntaxTree; + string fileName = (syntaxTree != null ? syntaxTree.FileName : null); + return new ICSharpCode.NRefactory.TypeSystem.DomRegion(fileName, this.StartLocation, this.EndLocation); + } + + public AstNode Parent { + get { return parent; } + } + + public Role 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."); + ThrowIfFrozen(); + SetRole(value); + } + } + + internal uint RoleIndex { + get { return flags & roleIndexMask; } + } + + void SetRole(Role role) + { + flags = (flags & ~roleIndexMask) | role.Index; + } + + public AstNode NextSibling { + get { return nextSibling; } + } + + public AstNode PrevSibling { + get { return prevSibling; } + } + + public AstNode FirstChild { + get { return firstChild; } + } + + public AstNode LastChild { + get { return lastChild; } + } + + public bool HasChildren { + get { + return firstChild != null; + } + } + + public IEnumerable Children { + get { + AstNode next; + for (AstNode cur = firstChild; cur != null; cur = next) { + Debug.Assert (cur.parent == this); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.nextSibling; + yield return cur; + } + } + } + + /// + /// Gets the ancestors of this node (excluding this node itself) + /// + public IEnumerable Ancestors { + get { + for (AstNode cur = parent; cur != null; cur = cur.parent) { + yield return cur; + } + } + } + + /// + /// Gets the ancestors of this node (including this node itself) + /// + public IEnumerable AncestorsAndSelf { + get { + for (AstNode cur = this; cur != null; cur = cur.parent) { + yield return cur; + } + } + } + + /// + /// Gets all descendants of this node (excluding this node itself) in pre-order. + /// + public IEnumerable Descendants { + get { return GetDescendantsImpl(false); } + } + + /// + /// Gets all descendants of this node (including this node itself) in pre-order. + /// + public IEnumerable DescendantsAndSelf { + get { return GetDescendantsImpl(true); } + } + + static bool IsInsideRegion(DomRegion region, AstNode pos) + { + if (region.IsEmpty) + return true; + var nodeRegion = pos.Region; + return region.IntersectsWith(nodeRegion) || region.OverlapsWith(nodeRegion); + } + + public IEnumerable DescendantNodes (Func descendIntoChildren = null) + { + return GetDescendantsImpl(false, new DomRegion (), descendIntoChildren); + } + + public IEnumerable DescendantNodes (DomRegion region, Func descendIntoChildren = null) + { + return GetDescendantsImpl(false, region, descendIntoChildren); + } + + public IEnumerable DescendantNodesAndSelf (Func descendIntoChildren = null) + { + return GetDescendantsImpl(true, new DomRegion (), descendIntoChildren); + } + + public IEnumerable DescendantNodesAndSelf (DomRegion region, Func descendIntoChildren = null) + { + return GetDescendantsImpl(true, region, descendIntoChildren); + } + + IEnumerable GetDescendantsImpl(bool includeSelf, DomRegion region = new DomRegion (), Func descendIntoChildren = null) + { + if (includeSelf) { + if (IsInsideRegion (region, this)) + yield return this; + if (descendIntoChildren != null && !descendIntoChildren(this)) + yield break; + } + + Stack nextStack = new Stack(); + nextStack.Push(null); + AstNode pos = firstChild; + while (pos != null) { + // Remember next before yielding pos. + // This allows removing/replacing nodes while iterating through the list. + if (pos.nextSibling != null) + nextStack.Push(pos.nextSibling); + if (IsInsideRegion(region, pos)) + yield return pos; + if (pos.firstChild != null && (descendIntoChildren == null || descendIntoChildren(pos))) + pos = pos.firstChild; + else + pos = nextStack.Pop(); + } + } + + /// + /// Gets the first child with the specified role. + /// Returns the role's null object if the child is not found. + /// + public T GetChildByRole(Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + uint roleIndex = role.Index; + for (var cur = firstChild; cur != null; cur = cur.nextSibling) { + if ((cur.flags & roleIndexMask) == roleIndex) + return (T)cur; + } + return role.NullObject; + } + + public T GetParent() where T : AstNode + { + return Ancestors.OfType().FirstOrDefault(); + } + + public AstNode GetParent(Func pred) + { + return Ancestors.FirstOrDefault(pred); + } + + public AstNodeCollection GetChildrenByRole (Role role) where T : AstNode + { + return new AstNodeCollection (this, role); + } + + protected void SetChildByRole (Role role, T newChild) where T : AstNode + { + AstNode oldChild = GetChildByRole (role); + if (oldChild.IsNull) + AddChild (newChild, role); + else + oldChild.ReplaceWith (newChild); + } + + public void AddChild (T child, Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + if (child == null || child.IsNull) + return; + ThrowIfFrozen(); + if (child == this) + throw new ArgumentException ("Cannot add a node to itself as a child.", "child"); + 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); + } + + public void AddChildWithExistingRole (AstNode child) + { + if (child == null || child.IsNull) + return; + ThrowIfFrozen(); + if (child == this) + throw new ArgumentException ("Cannot add a node to itself as a child.", "child"); + 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, child.Role); + } + + /// + /// Adds a child without performing any safety checks. + /// + internal void AddChildUnsafe (AstNode child, Role role) + { + child.parent = this; + child.SetRole(role); + if (firstChild == null) { + lastChild = firstChild = child; + } else { + lastChild.nextSibling = child; + child.prevSibling = lastChild; + lastChild = child; + } + } + + public void InsertChildBefore (AstNode nextSibling, T child, Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + if (nextSibling == null || nextSibling.IsNull) { + AddChild (child, role); + return; + } + + 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", + // as there isn't any valid nextSibling in null nodes. + InsertChildBeforeUnsafe (nextSibling, child, role); + } + + internal void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role) + { + child.parent = this; + child.SetRole(role); + child.nextSibling = nextSibling; + child.prevSibling = nextSibling.prevSibling; + + if (nextSibling.prevSibling != null) { + Debug.Assert (nextSibling.prevSibling.nextSibling == nextSibling); + nextSibling.prevSibling.nextSibling = child; + } else { + Debug.Assert (firstChild == nextSibling); + firstChild = child; + } + nextSibling.prevSibling = child; + } + + public void InsertChildAfter (AstNode prevSibling, T child, Role role) where T : AstNode + { + InsertChildBefore ((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role); + } + + /// + /// Removes this node from its parent. + /// + public void Remove () + { + if (parent != null) { + ThrowIfFrozen(); + if (prevSibling != null) { + Debug.Assert (prevSibling.nextSibling == this); + prevSibling.nextSibling = nextSibling; + } else { + Debug.Assert (parent.firstChild == this); + parent.firstChild = nextSibling; + } + if (nextSibling != null) { + Debug.Assert (nextSibling.prevSibling == this); + nextSibling.prevSibling = prevSibling; + } else { + Debug.Assert (parent.lastChild == this); + parent.lastChild = prevSibling; + } + parent = null; + prevSibling = null; + nextSibling = null; + } + } + + /// + /// Replaces this node with the new node. + /// + public void ReplaceWith (AstNode newNode) + { + if (newNode == null || newNode.IsNull) { + Remove (); + return; + } + if (newNode == this) + return; // nothing to do... + 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 (!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? + if (newNode.Ancestors.Contains (this)) { + // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);" + // enable automatic removal + newNode.Remove (); + } else { + 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.SetRole(this.Role); + newNode.prevSibling = prevSibling; + newNode.nextSibling = nextSibling; + + if (prevSibling != null) { + Debug.Assert (prevSibling.nextSibling == this); + prevSibling.nextSibling = newNode; + } else { + Debug.Assert (parent.firstChild == this); + parent.firstChild = newNode; + } + if (nextSibling != null) { + Debug.Assert (nextSibling.prevSibling == this); + nextSibling.prevSibling = newNode; + } else { + Debug.Assert (parent.lastChild == this); + parent.lastChild = newNode; + } + parent = null; + prevSibling = null; + nextSibling = null; + } + + public AstNode ReplaceWith (Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException ("replaceFunction"); + if (parent == null) { + throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); + } + AstNode oldParent = parent; + AstNode oldSuccessor = nextSibling; + Role oldRole = this.Role; + Remove (); + AstNode replacement = replaceFunction (this); + if (oldSuccessor != null && oldSuccessor.parent != oldParent) + throw new InvalidOperationException ("replace function changed nextSibling of node being replaced?"); + if (!(replacement == null || replacement.IsNull)) { + if (replacement.parent != null) + throw new InvalidOperationException ("replace function must return the root of a tree"); + if (!oldRole.IsValid (replacement)) { + throw new InvalidOperationException (string.Format ("The new node '{0}' is not valid in the role {1}", replacement.GetType ().Name, oldRole.ToString ())); + } + + if (oldSuccessor != null) + oldParent.InsertChildBeforeUnsafe (oldSuccessor, replacement, oldRole); + else + oldParent.AddChildUnsafe (replacement, oldRole); + } + return replacement; + } + + /// + /// Clones the whole subtree starting at this AST node. + /// + /// Annotations are copied over to the new nodes; and any annotations implementing ICloneable will be cloned. + public AstNode Clone () + { + AstNode copy = (AstNode)MemberwiseClone (); + // First, reset the shallow pointer copies + copy.parent = null; + copy.firstChild = null; + 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); + } + + // Finally, clone the annotation, if necessary + copy.CloneAnnotations(); + + return copy; + } + + object ICloneable.Clone() + { + return Clone(); + } + + public abstract void AcceptVisitor (IAstVisitor visitor); + + public abstract T AcceptVisitor (IAstVisitor visitor); + + public abstract S AcceptVisitor (IAstVisitor visitor, T data); + + #region Pattern Matching + protected static bool MatchString (string pattern, string text) + { + return PatternMatching.Pattern.MatchString(pattern, text); + } + + protected internal abstract bool DoMatch (AstNode other, PatternMatching.Match match); + + bool PatternMatching.INode.DoMatch (PatternMatching.INode other, PatternMatching.Match match) + { + AstNode o = other as AstNode; + // try matching if other is null, or if other is an AstNode + return (other == null || o != null) && DoMatch (o, match); + } + + bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + AstNode o = pos as AstNode; + return (pos == null || o != null) && DoMatch (o, match); + } + + PatternMatching.INode PatternMatching.INode.NextSibling { + get { return nextSibling; } + } + + PatternMatching.INode PatternMatching.INode.FirstChild { + get { return firstChild; } + } + + #endregion + + public AstNode GetNextNode () + { + if (NextSibling != null) + return NextSibling; + if (Parent != null) + return Parent.GetNextNode (); + return null; + } + + /// + /// Gets the next node which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetNextNode (Func pred) + { + var next = GetNextNode(); + while (next != null && !pred (next)) + next = next.GetNextNode(); + return next; + } + + public AstNode GetPrevNode () + { + if (PrevSibling != null) + return PrevSibling; + if (Parent != null) + return Parent.GetPrevNode (); + return null; + } + + /// + /// Gets the previous node which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetPrevNode (Func pred) + { + var prev = GetPrevNode(); + while (prev != null && !pred (prev)) + prev = prev.GetPrevNode(); + return prev; + } + // filters all non c# nodes (comments, white spaces or pre processor directives) + public AstNode GetCSharpNodeBefore (AstNode node) + { + var n = node.PrevSibling; + while (n != null) { + if (n.Role != Roles.Comment) + return n; + n = n.GetPrevNode (); + } + return null; + } + + /// + /// Gets the next sibling which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetNextSibling (Func pred) + { + var next = NextSibling; + while (next != null && !pred (next)) + next = next.NextSibling; + return next; + } + + /// + /// Gets the next sibling which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetPrevSibling (Func pred) + { + var prev = PrevSibling; + while (prev != null && !pred (prev)) + prev = prev.PrevSibling; + return prev; + } + + #region GetNodeAt + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public AstNode GetNodeAt (int line, int column, Predicate pred = null) + { + return GetNodeAt (new TextLocation (line, column), pred); + } + + /// + /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public AstNode GetNodeAt (TextLocation location, Predicate pred = null) + { + AstNode result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location < child.EndLocation) { + if (pred == null || pred (child)) + result = child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public T GetNodeAt (int line, int column) where T : AstNode + { + return GetNodeAt (new TextLocation (line, column)); + } + + /// + /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public T GetNodeAt (TextLocation location) where T : AstNode + { + T result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location < child.EndLocation) { + if (child is T) + result = (T)child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + #endregion + + #region GetAdjacentNodeAt + /// + /// Gets the node specified by pred at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public AstNode GetAdjacentNodeAt(int line, int column, Predicate pred = null) + { + return GetAdjacentNodeAt (new TextLocation (line, column), pred); + } + + /// + /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public AstNode GetAdjacentNodeAt (TextLocation location, Predicate pred = null) + { + AstNode result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location <= child.EndLocation) { + if (pred == null || pred (child)) + result = child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public T GetAdjacentNodeAt(int line, int column) where T : AstNode + { + return GetAdjacentNodeAt (new TextLocation (line, column)); + } + + /// + /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public T GetAdjacentNodeAt (TextLocation location) where T : AstNode + { + T result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location <= child.EndLocation) { + if (child is T) + result = (T)child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + #endregion + + + /// + /// Gets the node that fully contains the range from startLocation to endLocation. + /// + public AstNode GetNodeContaining(TextLocation startLocation, TextLocation endLocation) + { + for (AstNode child = firstChild; child != null; child = child.nextSibling) { + if (child.StartLocation <= startLocation && endLocation <= child.EndLocation) + return child.GetNodeContaining(startLocation, endLocation); + } + return this; + } + + /// + /// Returns the root nodes of all subtrees that are fully contained in the specified region. + /// + public IEnumerable GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn) + { + return GetNodesBetween (new TextLocation (startLine, startColumn), new TextLocation (endLine, endColumn)); + } + + /// + /// Returns the root nodes of all subtrees that are fully contained between and (inclusive). + /// + public IEnumerable GetNodesBetween (TextLocation start, TextLocation end) + { + AstNode node = this; + while (node != null) { + AstNode next; + if (start <= node.StartLocation && node.EndLocation <= end) { + // Remember next before yielding node. + // This allows iteration to continue when the caller removes/replaces the node. + next = node.GetNextNode(); + yield return node; + } else { + if (node.EndLocation <= start) { + next = node.GetNextNode(); + } else { + next = node.FirstChild; + } + } + + if (next != null && next.StartLocation > end) + yield break; + node = next; + } + } + [Obsolete("Use ToString(options).")] + public string GetText (CSharpFormattingOptions formattingOptions = null) + { + return ToString(formattingOptions); + } + + /// + /// Gets the node as formatted C# output. + /// + /// + /// Formatting options. + /// + public virtual string ToString (CSharpFormattingOptions formattingOptions) + { + if (IsNull) + return ""; + var w = new StringWriter (); + AcceptVisitor (new CSharpOutputVisitor (w, formattingOptions ?? FormattingOptionsFactory.CreateMono ())); + return w.ToString (); + } + + public sealed override string ToString() + { + return ToString(null); + } + + /// + /// Returns true, if the given coordinates (line, column) are in the node. + /// + /// + /// True, if the given coordinates are between StartLocation and EndLocation (exclusive); otherwise, false. + /// + public bool Contains (int line, int column) + { + return Contains (new TextLocation (line, column)); + } + + /// + /// Returns true, if the given coordinates are in the node. + /// + /// + /// True, if location is between StartLocation and EndLocation (exclusive); otherwise, false. + /// + public bool Contains (TextLocation location) + { + return this.StartLocation <= location && location < this.EndLocation; + } + + /// + /// Returns true, if the given coordinates (line, column) are in the node. + /// + /// + /// True, if the given coordinates are between StartLocation and EndLocation (inclusive); otherwise, false. + /// + public bool IsInside (int line, int column) + { + return IsInside (new TextLocation (line, column)); + } + + /// + /// Returns true, if the given coordinates are in the node. + /// + /// + /// True, if location is between StartLocation and EndLocation (inclusive); otherwise, false. + /// + public bool IsInside (TextLocation location) + { + return this.StartLocation <= location && location <= this.EndLocation; + } + + public override void AddAnnotation (object annotation) + { + if (this.IsNull) + throw new InvalidOperationException ("Cannot add annotations to the null node"); + base.AddAnnotation (annotation); + } + + internal string DebugToString() + { + if (IsNull) + return "Null"; + string text = ToString(); + text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " "); + if (text.Length > 100) + return text.Substring(0, 97) + "..."; + else + return text; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs new file mode 100644 index 000000000..32d08b2e4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs @@ -0,0 +1,231 @@ +// Copyright (c) 2010-2013 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents the children of an AstNode that have a specific role. + /// + public class AstNodeCollection : ICollection + #if NET_4_5 + , IReadOnlyCollection + #endif + where T : AstNode + { + readonly AstNode node; + readonly Role role; + + public AstNodeCollection(AstNode node, Role role) + { + if (node == null) + throw new ArgumentNullException("node"); + if (role == null) + throw new ArgumentNullException("role"); + this.node = node; + this.role = role; + } + + public int Count { + get { + int count = 0; + uint roleIndex = role.Index; + for (AstNode cur = node.FirstChild; cur != null; cur = cur.NextSibling) { + if (cur.RoleIndex == roleIndex) + count++; + } + return count; + } + } + + public void Add(T element) + { + node.AddChild(element, role); + } + + public void AddRange(IEnumerable nodes) + { + // Evaluate 'nodes' first, since it might change when we add the new children + // Example: collection.AddRange(collection); + if (nodes != null) { + foreach (T node in nodes.ToList()) + Add(node); + } + } + + public void AddRange(T[] nodes) + { + // Fast overload for arrays - we don't need to create a copy + if (nodes != null) { + foreach (T node in nodes) + Add(node); + } + } + + public void ReplaceWith(IEnumerable nodes) + { + // Evaluate 'nodes' first, since it might change when we call Clear() + // Example: collection.ReplaceWith(collection); + if (nodes != null) + nodes = nodes.ToList(); + Clear(); + if (nodes != null) { + foreach (T node in nodes) + Add(node); + } + } + + public void MoveTo(ICollection targetCollection) + { + if (targetCollection == null) + throw new ArgumentNullException("targetCollection"); + foreach (T node in this) { + node.Remove(); + targetCollection.Add(node); + } + } + + public bool Contains(T element) + { + return element != null && element.Parent == node && element.RoleIndex == role.Index; + } + + public bool Remove(T element) + { + if (Contains(element)) { + element.Remove(); + return true; + } else { + return false; + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + foreach (T item in this) + array[arrayIndex++] = item; + } + + public void Clear() + { + foreach (T item in this) + item.Remove(); + } + + /// + /// Returns the first element for which the predicate returns true, + /// or the null node (AstNode with IsNull=true) if no such object is found. + /// + public T FirstOrNullObject(Func predicate = null) + { + foreach (T item in this) + if (predicate == null || predicate(item)) + return item; + return role.NullObject; + } + + /// + /// Returns the last element for which the predicate returns true, + /// or the null node (AstNode with IsNull=true) if no such object is found. + /// + public T LastOrNullObject(Func predicate = null) + { + T result = role.NullObject; + foreach (T item in this) + if (predicate == null || predicate(item)) + result = item; + return result; + } + + bool ICollection.IsReadOnly { + get { return false; } + } + + public IEnumerator GetEnumerator() + { + uint roleIndex = role.Index; + AstNode next; + for (AstNode cur = node.FirstChild; cur != null; cur = next) { + Debug.Assert(cur.Parent == node); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.NextSibling; + if (cur.RoleIndex == roleIndex) + yield return (T)cur; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #region Equals and GetHashCode implementation + public override int GetHashCode() + { + return node.GetHashCode() ^ role.GetHashCode(); + } + + public override bool Equals(object obj) + { + AstNodeCollection other = obj as AstNodeCollection; + if (other == null) + return false; + return this.node == other.node && this.role == other.role; + } + #endregion + + internal bool DoMatch(AstNodeCollection other, Match match) + { + return Pattern.DoMatchCollection(role, node.FirstChild, other.node.FirstChild, match); + } + + public void InsertAfter(T existingItem, T newItem) + { + node.InsertChildAfter(existingItem, newItem, role); + } + + public void InsertBefore(T existingItem, T newItem) + { + node.InsertChildBefore(existingItem, newItem, role); + } + + /// + /// Applies the to all nodes in this collection. + /// + public void AcceptVisitor(IAstVisitor visitor) + { + uint roleIndex = role.Index; + AstNode next; + for (AstNode cur = node.FirstChild; cur != null; cur = next) { + Debug.Assert(cur.Parent == node); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.NextSibling; + if (cur.RoleIndex == roleIndex) + cur.AcceptVisitor(visitor); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstType.cs new file mode 100644 index 000000000..2f13f0fdd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstType.cs @@ -0,0 +1,280 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A type reference in the C# AST. + /// + public abstract class AstType : AstNode + { + #region Null + public new static readonly AstType Null = new NullAstType (); + + sealed class NullAstType : AstType + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + return SpecialType.UnknownType; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator AstType(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : AstType, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder (this, child, data); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + throw new NotSupportedException(); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { return NodeType.TypeReference; } + } + + public new AstType Clone() + { + return (AstType)base.Clone(); + } + + /// + /// Gets whether this type is a SimpleType "var". + /// + public bool IsVar() + { + SimpleType st = this as SimpleType; + return st != null && st.Identifier == "var" && st.TypeArguments.Count == 0; + } + + /// + /// Create an ITypeReference for this AstType. + /// Uses the context (ancestors of this node) to determine the correct . + /// + /// + /// The resulting type reference will read the context information from the + /// : + /// For resolving type parameters, the CurrentTypeDefinition/CurrentMember is used. + /// For resolving simple names, the current namespace and usings from the CurrentUsingScope + /// (on CSharpTypeResolveContext only) is used. + /// + public ITypeReference ToTypeReference(InterningProvider interningProvider = null) + { + return ToTypeReference(GetNameLookupMode(), interningProvider); + } + + /// + /// Create an ITypeReference for this AstType. + /// + /// + /// The resulting type reference will read the context information from the + /// : + /// For resolving type parameters, the CurrentTypeDefinition/CurrentMember is used. + /// For resolving simple names, the current namespace and usings from the CurrentUsingScope + /// (on CSharpTypeResolveContext only) is used. + /// + public abstract ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null); + + /// + /// Gets the name lookup mode from the context (looking at the ancestors of this ). + /// + public NameLookupMode GetNameLookupMode() + { + AstType outermostType = this; + while (outermostType.Parent is AstType) + outermostType = (AstType)outermostType.Parent; + + if (outermostType.Parent is UsingDeclaration || outermostType.Parent is UsingAliasDeclaration) { + return NameLookupMode.TypeInUsingDeclaration; + } else if (outermostType.Role == Roles.BaseType) { + // Use BaseTypeReference for a type's base type, and for a constraint on a type. + // Do not use it for a constraint on a method. + if (outermostType.Parent is TypeDeclaration || (outermostType.Parent is Constraint && outermostType.Parent.Parent is TypeDeclaration)) + return NameLookupMode.BaseTypeReference; + } + return NameLookupMode.Type; + } + + /// + /// Creates a pointer type from this type by nesting it in a . + /// If this type already is a pointer type, this method just increases the PointerRank of the existing pointer type. + /// + public virtual AstType MakePointerType() + { + return new ComposedType { BaseType = this }.MakePointerType(); + } + + /// + /// Creates an array type from this type by nesting it in a . + /// If this type already is an array type, the additional rank is prepended to the existing array specifier list. + /// Thus, new SimpleType("T").MakeArrayType(1).MakeArrayType(2) will result in "T[,][]". + /// + public virtual AstType MakeArrayType(int rank = 1) + { + return new ComposedType { BaseType = this }.MakeArrayType(rank); + } + + /// + /// Creates a nullable type from this type by nesting it in a . + /// + public AstType MakeNullableType() + { + return new ComposedType { BaseType = this, HasNullableSpecifier = true }; + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberReferenceExpression Member(string memberName) + { + return new TypeReferenceExpression { Type = this }.Member(memberName); + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberType MemberType(string memberName, params AstType[] typeArguments) + { + var memberType = new MemberType(this, memberName); + memberType.TypeArguments.AddRange(typeArguments); + return memberType; + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberType MemberType(string memberName, IEnumerable typeArguments) + { + var memberType = new MemberType(this, memberName); + memberType.TypeArguments.AddRange(typeArguments); + return memberType; + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, params Expression[] arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, typeArguments, arguments); + } + + /// + /// Creates a simple AstType from a dotted name. + /// Does not support generics, arrays, etc. - just simple dotted names, + /// e.g. namespace names. + /// + public static AstType Create(string dottedName) + { + string[] parts = dottedName.Split('.'); + AstType type = new SimpleType(parts[0]); + for (int i = 1; i < parts.Length; i++) { + type = new MemberType(type, parts[i]); + } + return type; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs new file mode 100644 index 000000000..1a46006f2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs @@ -0,0 +1,218 @@ +// +// CSharpModifierToken.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class CSharpModifierToken : CSharpTokenNode + { + Modifiers modifier; + + public Modifiers Modifier { + get { return modifier; } + set { + ThrowIfFrozen(); + this.modifier = value; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + GetModifierLength (Modifier)); + } + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return GetModifierName (Modifier); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CSharpModifierToken o = other as CSharpModifierToken; + return o != null && this.modifier == o.modifier; + } + + // 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.New, + Modifiers.Unsafe, + Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Static, Modifiers.Override, + Modifiers.Readonly, Modifiers.Volatile, + Modifiers.Extern, Modifiers.Partial, Modifiers.Const, + Modifiers.Async, + Modifiers.Any + }; + + public static IEnumerable AllModifiers { + get { return allModifiers; } + } + + public CSharpModifierToken (TextLocation location, Modifiers modifier) : base (location, null) + { + this.Modifier = modifier; + } + + public static string GetModifierName(Modifiers modifier) + { + switch (modifier) { + case Modifiers.Private: + return "private"; + case Modifiers.Internal: + return "internal"; + case Modifiers.Protected: + return "protected"; + case Modifiers.Public: + return "public"; + case Modifiers.Abstract: + return "abstract"; + case Modifiers.Virtual: + return "virtual"; + case Modifiers.Sealed: + return "sealed"; + case Modifiers.Static: + return "static"; + case Modifiers.Override: + return "override"; + case Modifiers.Readonly: + return "readonly"; + case Modifiers.Const: + return "const"; + case Modifiers.New: + return "new"; + case Modifiers.Partial: + return "partial"; + case Modifiers.Extern: + return "extern"; + case Modifiers.Volatile: + return "volatile"; + case Modifiers.Unsafe: + return "unsafe"; + case Modifiers.Async: + return "async"; + case Modifiers.Any: + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return "any"; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + + public static int GetModifierLength(Modifiers modifier) + { + switch (modifier) { + case Modifiers.Private: + return "private".Length; + case Modifiers.Internal: + return "internal".Length; + case Modifiers.Protected: + return "protected".Length; + case Modifiers.Public: + return "public".Length; + case Modifiers.Abstract: + return "abstract".Length; + case Modifiers.Virtual: + return "virtual".Length; + case Modifiers.Sealed: + return "sealed".Length; + case Modifiers.Static: + return "static".Length; + case Modifiers.Override: + return "override".Length; + case Modifiers.Readonly: + return "readonly".Length; + case Modifiers.Const: + return "const".Length; + case Modifiers.New: + return "new".Length; + case Modifiers.Partial: + return "partial".Length; + case Modifiers.Extern: + return "extern".Length; + case Modifiers.Volatile: + return "volatile".Length; + case Modifiers.Unsafe: + return "unsafe".Length; + case Modifiers.Async: + return "async".Length; + case Modifiers.Any: + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return "any".Length; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + + public static Modifiers GetModifierValue(string modifier) + { + switch (modifier) { + case "private": + return Modifiers.Private; + case "internal": + return Modifiers.Internal; + case "protected": + return Modifiers.Protected; + case "public": + return Modifiers.Public; + case "abstract": + return Modifiers.Abstract; + case "virtual": + return Modifiers.Virtual; + case "sealed": + return Modifiers.Sealed; + case "static": + return Modifiers.Static; + case "override": + return Modifiers.Override; + case "readonly": + return Modifiers.Readonly; + case "const": + return Modifiers.Const; + case "new": + return Modifiers.New; + case "partial": + return Modifiers.Partial; + case "extern": + return Modifiers.Extern; + case "volatile": + return Modifiers.Volatile; + case "unsafe": + return Modifiers.Unsafe; + case "async": + return Modifiers.Async; + case "any": + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return Modifiers.Any; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs new file mode 100644 index 000000000..713f664b3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs @@ -0,0 +1,131 @@ +// +// TokenNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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 +{ + /// + /// Represents a token in C#. Note that the type of the token is defined through the TokenRole. + /// + /// + /// In all non null c# token nodes the Role of a CSharpToken must be a TokenRole. + /// + public class CSharpTokenNode : AstNode + { + public static new readonly CSharpTokenNode Null = new NullCSharpTokenNode (); + class NullCSharpTokenNode : CSharpTokenNode + { + public override bool IsNull { + get { + return true; + } + } + + public NullCSharpTokenNode () : base (TextLocation.Empty, null) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Token; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + int TokenLength { + get { + return TokenRole.TokenLengths [(int)(this.flags >> AstNodeFlagsUsedBits)]; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + TokenLength); + } + } + + public CSharpTokenNode (TextLocation location, TokenRole role) + { + this.startLocation = location; + if (role != null) + this.flags |= role.TokenIndex << AstNodeFlagsUsedBits; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return TokenRole.Tokens [(int)(this.flags >> AstNodeFlagsUsedBits)]; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCSharpTokenNode (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCSharpTokenNode (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCSharpTokenNode (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CSharpTokenNode o = other as CSharpTokenNode; + return o != null && !o.IsNull && !(o is CSharpModifierToken); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpUtil.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpUtil.cs new file mode 100644 index 000000000..a2a07ad6e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpUtil.cs @@ -0,0 +1,180 @@ +// +// CSharpUtil.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + public static class CSharpUtil + { + /// + /// Inverts a boolean condition. Note: The condition object can be frozen (from AST) it's cloned internally. + /// + /// The condition to invert. + public static Expression InvertCondition(Expression condition) + { + return InvertConditionInternal(condition); + } + + static Expression InvertConditionInternal(Expression condition) + { + if (condition is ParenthesizedExpression) { + return new ParenthesizedExpression(InvertCondition(((ParenthesizedExpression)condition).Expression)); + } + + if (condition is UnaryOperatorExpression) { + var uOp = (UnaryOperatorExpression)condition; + if (uOp.Operator == UnaryOperatorType.Not) { + if (!(uOp.Parent is Expression)) + return GetInnerMostExpression(uOp.Expression).Clone(); + return uOp.Expression.Clone(); + } + return new UnaryOperatorExpression(UnaryOperatorType.Not, uOp.Clone()); + } + + if (condition is BinaryOperatorExpression) { + var bOp = (BinaryOperatorExpression)condition; + + if ((bOp.Operator == BinaryOperatorType.ConditionalAnd) || (bOp.Operator == BinaryOperatorType.ConditionalOr)) { + return new BinaryOperatorExpression(InvertCondition(bOp.Left), NegateConditionOperator(bOp.Operator), InvertCondition(bOp.Right)); + } else if ((bOp.Operator == BinaryOperatorType.Equality) || (bOp.Operator == BinaryOperatorType.InEquality) || (bOp.Operator == BinaryOperatorType.GreaterThan) + || (bOp.Operator == BinaryOperatorType.GreaterThanOrEqual) || (bOp.Operator == BinaryOperatorType.LessThan) || + (bOp.Operator == BinaryOperatorType.LessThanOrEqual)) { + return new BinaryOperatorExpression(bOp.Left.Clone(), NegateRelationalOperator(bOp.Operator), bOp.Right.Clone()); + } else { + var negatedOp = NegateRelationalOperator(bOp.Operator); + if (negatedOp == BinaryOperatorType.Any) + return new UnaryOperatorExpression(UnaryOperatorType.Not, new ParenthesizedExpression(condition.Clone())); + bOp = (BinaryOperatorExpression)bOp.Clone(); + bOp.Operator = negatedOp; + return bOp; + } + } + if (condition is ConditionalExpression) { + var cEx = condition.Clone() as ConditionalExpression; + cEx.Condition = InvertCondition(cEx.Condition); + return cEx; + } + if (condition is PrimitiveExpression) { + var pex = condition as PrimitiveExpression; + if (pex.Value is bool) { + return new PrimitiveExpression(!((bool)pex.Value)); + } + } + + return new UnaryOperatorExpression(UnaryOperatorType.Not, AddParensForUnaryExpressionIfRequired(condition.Clone())); + } + + /// + /// When negating an expression this is required, otherwise you would end up with + /// a or b -> !a or b + /// + internal static Expression AddParensForUnaryExpressionIfRequired(Expression expression) + { + if ((expression is BinaryOperatorExpression) || + (expression is AssignmentExpression) || + (expression is CastExpression) || + (expression is AsExpression) || + (expression is IsExpression) || + (expression is LambdaExpression) || + (expression is ConditionalExpression)) { + return new ParenthesizedExpression(expression); + } + + return expression; + } + + /// + /// Get negation of the specified relational operator + /// + /// + /// negation of the specified relational operator, or BinaryOperatorType.Any if it's not a relational operator + /// + public static BinaryOperatorType NegateRelationalOperator(BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.GreaterThan: + return BinaryOperatorType.LessThanOrEqual; + case BinaryOperatorType.GreaterThanOrEqual: + return BinaryOperatorType.LessThan; + case BinaryOperatorType.Equality: + return BinaryOperatorType.InEquality; + case BinaryOperatorType.InEquality: + return BinaryOperatorType.Equality; + case BinaryOperatorType.LessThan: + return BinaryOperatorType.GreaterThanOrEqual; + case BinaryOperatorType.LessThanOrEqual: + return BinaryOperatorType.GreaterThan; + case BinaryOperatorType.ConditionalOr: + return BinaryOperatorType.ConditionalAnd; + case BinaryOperatorType.ConditionalAnd: + return BinaryOperatorType.ConditionalOr; + } + return BinaryOperatorType.Any; + } + + /// + /// Returns true, if the specified operator is a relational operator + /// + public static bool IsRelationalOperator(BinaryOperatorType op) + { + return NegateRelationalOperator(op) != BinaryOperatorType.Any; + } + + /// + /// Get negation of the condition operator + /// + /// + /// negation of the specified condition operator, or BinaryOperatorType.Any if it's not a condition operator + /// + public static BinaryOperatorType NegateConditionOperator(BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.ConditionalOr: + return BinaryOperatorType.ConditionalAnd; + case BinaryOperatorType.ConditionalAnd: + return BinaryOperatorType.ConditionalOr; + } + return BinaryOperatorType.Any; + } + + public static bool AreConditionsEqual(Expression cond1, Expression cond2) + { + if (cond1 == null || cond2 == null) + return false; + return GetInnerMostExpression(cond1).IsMatch(GetInnerMostExpression(cond2)); + } + + public static Expression GetInnerMostExpression(Expression target) + { + while (target is ParenthesizedExpression) + target = ((ParenthesizedExpression)target).Expression; + return target; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ComposedType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ComposedType.cs new file mode 100644 index 000000000..0c0f96c62 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ComposedType.cs @@ -0,0 +1,230 @@ +// +// ComposedType.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Text; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class ComposedType : AstType + { + public static readonly TokenRole NullableRole = new TokenRole("?"); + public static readonly TokenRole PointerRole = new TokenRole("*"); + public static readonly Role ArraySpecifierRole = new Role("ArraySpecifier"); + + public AstType BaseType { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public bool HasNullableSpecifier { + get { + return !GetChildByRole(NullableRole).IsNull; + } + set { + SetChildByRole(NullableRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); + } + } + + public CSharpTokenNode NullableSpecifierToken { + get { + return GetChildByRole(NullableRole); + } + } + + public int PointerRank { + get { + return GetChildrenByRole(PointerRole).Count; + } + set { + if (value < 0) + throw new ArgumentOutOfRangeException(); + int d = this.PointerRank; + while (d > value) { + GetChildByRole(PointerRole).Remove(); + d--; + } + while (d < value) { + InsertChildBefore(GetChildByRole(PointerRole), new CSharpTokenNode(TextLocation.Empty, PointerRole), PointerRole); + d++; + } + } + } + + public AstNodeCollection ArraySpecifiers { + get { return GetChildrenByRole (ArraySpecifierRole); } + } + + public AstNodeCollection PointerTokens { + get { return GetChildrenByRole (PointerRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitComposedType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitComposedType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitComposedType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ComposedType o = other as ComposedType; + return o != null && this.HasNullableSpecifier == o.HasNullableSpecifier && this.PointerRank == o.PointerRank + && this.BaseType.DoMatch(o.BaseType, match) + && this.ArraySpecifiers.DoMatch(o.ArraySpecifiers, match); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + StringBuilder b = new StringBuilder(); + b.Append(this.BaseType.ToString()); + if (this.HasNullableSpecifier) + b.Append('?'); + b.Append('*', this.PointerRank); + foreach (var arraySpecifier in this.ArraySpecifiers) { + b.Append('['); + b.Append(',', arraySpecifier.Dimensions - 1); + b.Append(']'); + } + return b.ToString(); + } + + public override AstType MakePointerType() + { + if (ArraySpecifiers.Any()) { + return base.MakePointerType(); + } else { + this.PointerRank++; + return this; + } + } + + public override AstType MakeArrayType(int dimensions) + { + InsertChildBefore(this.ArraySpecifiers.FirstOrDefault(), new ArraySpecifier(dimensions), ArraySpecifierRole); + return this; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + ITypeReference t = this.BaseType.ToTypeReference(lookupMode, interningProvider); + if (this.HasNullableSpecifier) { + t = interningProvider.Intern(NullableType.Create(t)); + } + int pointerRank = this.PointerRank; + for (int i = 0; i < pointerRank; i++) { + t = interningProvider.Intern(new PointerTypeReference(t)); + } + foreach (var a in this.ArraySpecifiers.Reverse()) { + t = interningProvider.Intern(new ArrayTypeReference(t, a.Dimensions)); + } + return t; + } + } + + /// + /// [,,,] + /// + public class ArraySpecifier : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public ArraySpecifier() + { + } + + public ArraySpecifier(int dimensions) + { + this.Dimensions = dimensions; + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public int Dimensions { + get { return 1 + GetChildrenByRole(Roles.Comma).Count; } + set { + int d = this.Dimensions; + while (d > value) { + GetChildByRole(Roles.Comma).Remove(); + d--; + } + while (d < value) { + InsertChildBefore(GetChildByRole(Roles.Comma), new CSharpTokenNode(TextLocation.Empty, Roles.Comma), Roles.Comma); + d++; + } + } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArraySpecifier (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArraySpecifier (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArraySpecifier(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArraySpecifier o = other as ArraySpecifier; + return o != null && this.Dimensions == o.Dimensions; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return "[" + new string(',', this.Dimensions - 1) + "]"; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DepthFirstAstVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DepthFirstAstVisitor.cs new file mode 100644 index 000000000..d8c678e2b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DepthFirstAstVisitor.cs @@ -0,0 +1,1849 @@ +// +// IAstVisitor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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 +{ + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual void VisitChildren (AstNode node) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this); + } + } + + public virtual void VisitNullNode(AstNode nullNode) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + } + + public virtual void VisitSyntaxTree (SyntaxTree syntaxTree) + { + VisitChildren (syntaxTree); + } + + public virtual void VisitComment(Comment comment) + { + VisitChildren(comment); + } + + public virtual void VisitNewLine(NewLineNode newLineNode) + { + VisitChildren(newLineNode); + } + + public virtual void VisitWhitespace(WhitespaceNode whitespaceNode) + { + VisitChildren(whitespaceNode); + } + + public virtual void VisitText(TextNode textNode) + { + VisitChildren(textNode); + } + + public virtual void VisitDocumentationReference (DocumentationReference documentationReference) + { + VisitChildren (documentationReference); + } + + public virtual void VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) + { + VisitChildren (preProcessorDirective); + } + + public virtual void VisitIdentifier (Identifier identifier) + { + VisitChildren (identifier); + } + + public virtual void VisitCSharpTokenNode (CSharpTokenNode token) + { + VisitChildren (token); + } + + public virtual void VisitPrimitiveType (PrimitiveType primitiveType) + { + VisitChildren (primitiveType); + } + + public virtual void VisitComposedType (ComposedType composedType) + { + VisitChildren (composedType); + } + + public virtual void VisitSimpleType(SimpleType simpleType) + { + VisitChildren (simpleType); + } + + public virtual void VisitMemberType(MemberType memberType) + { + VisitChildren (memberType); + } + + public virtual void VisitAttribute (Attribute attribute) + { + VisitChildren (attribute); + } + + public virtual void VisitAttributeSection (AttributeSection attributeSection) + { + VisitChildren (attributeSection); + } + + public virtual void VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration) + { + VisitChildren (delegateDeclaration); + } + + public virtual void VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration) + { + VisitChildren (namespaceDeclaration); + } + + public virtual void VisitTypeDeclaration (TypeDeclaration typeDeclaration) + { + VisitChildren (typeDeclaration); + } + + public virtual void VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration) + { + VisitChildren (typeParameterDeclaration); + } + + public virtual void VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration) + { + VisitChildren (enumMemberDeclaration); + } + + public virtual void VisitUsingDeclaration (UsingDeclaration usingDeclaration) + { + VisitChildren (usingDeclaration); + } + + public virtual void VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration) + { + VisitChildren (usingDeclaration); + } + + public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + VisitChildren (externAliasDeclaration); + } + + public virtual void VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration) + { + VisitChildren (constructorDeclaration); + } + + public virtual void VisitConstructorInitializer (ConstructorInitializer constructorInitializer) + { + VisitChildren (constructorInitializer); + } + + public virtual void VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration) + { + VisitChildren (destructorDeclaration); + } + + public virtual void VisitEventDeclaration (EventDeclaration eventDeclaration) + { + VisitChildren (eventDeclaration); + } + + public virtual void VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration) + { + VisitChildren (eventDeclaration); + } + + public virtual void VisitFieldDeclaration (FieldDeclaration fieldDeclaration) + { + VisitChildren (fieldDeclaration); + } + + public virtual void VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration) + { + VisitChildren (fixedFieldDeclaration); + } + + public virtual void VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer) + { + VisitChildren (fixedVariableInitializer); + } + + public virtual void VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration) + { + VisitChildren (indexerDeclaration); + } + + public virtual void VisitMethodDeclaration (MethodDeclaration methodDeclaration) + { + VisitChildren (methodDeclaration); + } + + public virtual void VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration) + { + VisitChildren (operatorDeclaration); + } + + public virtual void VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration) + { + VisitChildren (propertyDeclaration); + } + + public virtual void VisitAccessor (Accessor accessor) + { + VisitChildren (accessor); + } + + public virtual void VisitVariableInitializer (VariableInitializer variableInitializer) + { + VisitChildren (variableInitializer); + } + + public virtual void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) + { + VisitChildren (parameterDeclaration); + } + + public virtual void VisitConstraint (Constraint constraint) + { + VisitChildren (constraint); + } + + public virtual void VisitBlockStatement (BlockStatement blockStatement) + { + VisitChildren (blockStatement); + } + + public virtual void VisitExpressionStatement (ExpressionStatement expressionStatement) + { + VisitChildren (expressionStatement); + } + + public virtual void VisitBreakStatement (BreakStatement breakStatement) + { + VisitChildren (breakStatement); + } + + public virtual void VisitCheckedStatement (CheckedStatement checkedStatement) + { + VisitChildren (checkedStatement); + } + + public virtual void VisitContinueStatement (ContinueStatement continueStatement) + { + VisitChildren (continueStatement); + } + + public virtual void VisitDoWhileStatement (DoWhileStatement doWhileStatement) + { + VisitChildren (doWhileStatement); + } + + public virtual void VisitEmptyStatement (EmptyStatement emptyStatement) + { + VisitChildren (emptyStatement); + } + + public virtual void VisitFixedStatement (FixedStatement fixedStatement) + { + VisitChildren (fixedStatement); + } + + public virtual void VisitForeachStatement (ForeachStatement foreachStatement) + { + VisitChildren (foreachStatement); + } + + public virtual void VisitForStatement (ForStatement forStatement) + { + VisitChildren (forStatement); + } + + public virtual void VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement) + { + VisitChildren (gotoCaseStatement); + } + + public virtual void VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement) + { + VisitChildren (gotoDefaultStatement); + } + + public virtual void VisitGotoStatement (GotoStatement gotoStatement) + { + VisitChildren (gotoStatement); + } + + public virtual void VisitIfElseStatement (IfElseStatement ifElseStatement) + { + VisitChildren (ifElseStatement); + } + + public virtual void VisitLabelStatement (LabelStatement labelStatement) + { + VisitChildren (labelStatement); + } + + public virtual void VisitLockStatement (LockStatement lockStatement) + { + VisitChildren (lockStatement); + } + + public virtual void VisitReturnStatement (ReturnStatement returnStatement) + { + VisitChildren (returnStatement); + } + + public virtual void VisitSwitchStatement (SwitchStatement switchStatement) + { + VisitChildren (switchStatement); + } + + public virtual void VisitSwitchSection (SwitchSection switchSection) + { + VisitChildren (switchSection); + } + + public virtual void VisitCaseLabel (CaseLabel caseLabel) + { + VisitChildren (caseLabel); + } + + public virtual void VisitThrowStatement (ThrowStatement throwStatement) + { + VisitChildren (throwStatement); + } + + public virtual void VisitTryCatchStatement (TryCatchStatement tryCatchStatement) + { + VisitChildren (tryCatchStatement); + } + + public virtual void VisitCatchClause (CatchClause catchClause) + { + VisitChildren (catchClause); + } + + public virtual void VisitUncheckedStatement (UncheckedStatement uncheckedStatement) + { + VisitChildren (uncheckedStatement); + } + + public virtual void VisitUnsafeStatement (UnsafeStatement unsafeStatement) + { + VisitChildren (unsafeStatement); + } + + public virtual void VisitUsingStatement (UsingStatement usingStatement) + { + VisitChildren (usingStatement); + } + + public virtual void VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement) + { + VisitChildren (variableDeclarationStatement); + } + + public virtual void VisitWhileStatement (WhileStatement whileStatement) + { + VisitChildren (whileStatement); + } + + public virtual void VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement) + { + VisitChildren (yieldBreakStatement); + } + + public virtual void VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement) + { + VisitChildren (yieldReturnStatement); + } + + public virtual void VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression) + { + VisitChildren (anonymousMethodExpression); + } + + public virtual void VisitLambdaExpression (LambdaExpression lambdaExpression) + { + VisitChildren (lambdaExpression); + } + + public virtual void VisitAssignmentExpression (AssignmentExpression assignmentExpression) + { + VisitChildren (assignmentExpression); + } + + public virtual void VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression) + { + VisitChildren (baseReferenceExpression); + } + + public virtual void VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression) + { + VisitChildren (binaryOperatorExpression); + } + + public virtual void VisitCastExpression (CastExpression castExpression) + { + VisitChildren (castExpression); + } + + public virtual void VisitCheckedExpression (CheckedExpression checkedExpression) + { + VisitChildren (checkedExpression); + } + + public virtual void VisitConditionalExpression (ConditionalExpression conditionalExpression) + { + VisitChildren (conditionalExpression); + } + + public virtual void VisitIdentifierExpression (IdentifierExpression identifierExpression) + { + VisitChildren (identifierExpression); + } + + public virtual void VisitIndexerExpression (IndexerExpression indexerExpression) + { + VisitChildren (indexerExpression); + } + + public virtual void VisitInvocationExpression (InvocationExpression invocationExpression) + { + VisitChildren (invocationExpression); + } + + public virtual void VisitDirectionExpression (DirectionExpression directionExpression) + { + VisitChildren (directionExpression); + } + + public virtual void VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression) + { + VisitChildren (memberReferenceExpression); + } + + public virtual void VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression) + { + VisitChildren (nullReferenceExpression); + } + + public virtual void VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression) + { + VisitChildren (objectCreateExpression); + } + + public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + VisitChildren (anonymousTypeCreateExpression); + } + + public virtual void VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression) + { + VisitChildren (arrayCreateExpression); + } + + public virtual void VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression) + { + VisitChildren (parenthesizedExpression); + } + + public virtual void VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression) + { + VisitChildren (pointerReferenceExpression); + } + + public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + VisitChildren (primitiveExpression); + } + + public virtual void VisitSizeOfExpression (SizeOfExpression sizeOfExpression) + { + VisitChildren (sizeOfExpression); + } + + public virtual void VisitStackAllocExpression (StackAllocExpression stackAllocExpression) + { + VisitChildren (stackAllocExpression); + } + + public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + VisitChildren (thisReferenceExpression); + } + + public virtual void VisitTypeOfExpression (TypeOfExpression typeOfExpression) + { + VisitChildren (typeOfExpression); + } + + public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + VisitChildren (typeReferenceExpression); + } + + public virtual void VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression) + { + VisitChildren (unaryOperatorExpression); + } + + public virtual void VisitUncheckedExpression (UncheckedExpression uncheckedExpression) + { + VisitChildren (uncheckedExpression); + } + + public virtual void VisitQueryExpression(QueryExpression queryExpression) + { + VisitChildren (queryExpression); + } + + public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + VisitChildren (queryContinuationClause); + } + + public virtual void VisitQueryFromClause(QueryFromClause queryFromClause) + { + VisitChildren (queryFromClause); + } + + public virtual void VisitQueryLetClause(QueryLetClause queryLetClause) + { + VisitChildren (queryLetClause); + } + + public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + VisitChildren (queryWhereClause); + } + + public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + VisitChildren (queryJoinClause); + } + + public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + VisitChildren (queryOrderClause); + } + + public virtual void VisitQueryOrdering(QueryOrdering queryOrdering) + { + VisitChildren (queryOrdering); + } + + public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + VisitChildren (querySelectClause); + } + + public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + VisitChildren (queryGroupClause); + } + + public virtual void VisitAsExpression (AsExpression asExpression) + { + VisitChildren (asExpression); + } + + public virtual void VisitIsExpression (IsExpression isExpression) + { + VisitChildren (isExpression); + } + + public virtual void VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression) + { + VisitChildren (defaultValueExpression); + } + + public virtual void VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression) + { + VisitChildren (undocumentedExpression); + } + + public virtual void VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) + { + VisitChildren (arrayInitializerExpression); + } + + public virtual void VisitArraySpecifier (ArraySpecifier arraySpecifier) + { + VisitChildren (arraySpecifier); + } + + public virtual void VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression) + { + VisitChildren (namedArgumentExpression); + } + + public virtual void VisitNamedExpression (NamedExpression namedExpression) + { + VisitChildren (namedExpression); + } + + public virtual void VisitErrorNode(AstNode errorNode) + { + VisitChildren(errorNode); + } + + public virtual void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + VisitChildren (placeholder); + } + } + + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual T VisitChildren (AstNode node) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this); + } + return default (T); + } + + public virtual T VisitNullNode(AstNode nullNode) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + return default (T); + } + + public virtual T VisitSyntaxTree (SyntaxTree unit) + { + return VisitChildren (unit); + } + + public virtual T VisitComment (Comment comment) + { + return VisitChildren (comment); + } + + public virtual T VisitNewLine(NewLineNode newLineNode) + { + return VisitChildren(newLineNode); + } + + public virtual T VisitWhitespace(WhitespaceNode whitespaceNode) + { + return VisitChildren(whitespaceNode); + } + + public virtual T VisitText(TextNode textNode) + { + return VisitChildren(textNode); + } + + public virtual T VisitDocumentationReference (DocumentationReference documentationReference) + { + return VisitChildren (documentationReference); + } + + public virtual T VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) + { + return VisitChildren (preProcessorDirective); + } + + public virtual T VisitIdentifier (Identifier identifier) + { + return VisitChildren (identifier); + } + + public virtual T VisitCSharpTokenNode (CSharpTokenNode token) + { + return VisitChildren (token); + } + + public virtual T VisitPrimitiveType (PrimitiveType primitiveType) + { + return VisitChildren (primitiveType); + } + + public virtual T VisitComposedType (ComposedType composedType) + { + return VisitChildren (composedType); + } + + public virtual T VisitSimpleType(SimpleType simpleType) + { + return VisitChildren (simpleType); + } + + public virtual T VisitMemberType(MemberType memberType) + { + return VisitChildren (memberType); + } + + public virtual T VisitAttribute (Attribute attribute) + { + return VisitChildren (attribute); + } + + public virtual T VisitAttributeSection (AttributeSection attributeSection) + { + return VisitChildren (attributeSection); + } + + public virtual T VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration) + { + return VisitChildren (delegateDeclaration); + } + + public virtual T VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration) + { + return VisitChildren (namespaceDeclaration); + } + + public virtual T VisitTypeDeclaration (TypeDeclaration typeDeclaration) + { + return VisitChildren (typeDeclaration); + } + + public virtual T VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration) + { + return VisitChildren (typeParameterDeclaration); + } + + public virtual T VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration) + { + return VisitChildren (enumMemberDeclaration); + } + + public virtual T VisitUsingDeclaration (UsingDeclaration usingDeclaration) + { + return VisitChildren (usingDeclaration); + } + + public virtual T VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration) + { + return VisitChildren (usingDeclaration); + } + + public virtual T VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + return VisitChildren (externAliasDeclaration); + } + + public virtual T VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration) + { + return VisitChildren (constructorDeclaration); + } + + public virtual T VisitConstructorInitializer (ConstructorInitializer constructorInitializer) + { + return VisitChildren (constructorInitializer); + } + + public virtual T VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration) + { + return VisitChildren (destructorDeclaration); + } + + public virtual T VisitEventDeclaration (EventDeclaration eventDeclaration) + { + return VisitChildren (eventDeclaration); + } + + public virtual T VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration) + { + return VisitChildren (eventDeclaration); + } + + public virtual T VisitFieldDeclaration (FieldDeclaration fieldDeclaration) + { + return VisitChildren (fieldDeclaration); + } + + public virtual T VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration) + { + return VisitChildren (fixedFieldDeclaration); + } + + public virtual T VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer) + { + return VisitChildren (fixedVariableInitializer); + } + + public virtual T VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration) + { + return VisitChildren (indexerDeclaration); + } + + public virtual T VisitMethodDeclaration (MethodDeclaration methodDeclaration) + { + return VisitChildren (methodDeclaration); + } + + public virtual T VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration) + { + return VisitChildren (operatorDeclaration); + } + + public virtual T VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration) + { + return VisitChildren (propertyDeclaration); + } + + public virtual T VisitAccessor (Accessor accessor) + { + return VisitChildren (accessor); + } + + public virtual T VisitVariableInitializer (VariableInitializer variableInitializer) + { + return VisitChildren (variableInitializer); + } + + public virtual T VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) + { + return VisitChildren (parameterDeclaration); + } + + public virtual T VisitConstraint (Constraint constraint) + { + return VisitChildren (constraint); + } + + public virtual T VisitBlockStatement (BlockStatement blockStatement) + { + return VisitChildren (blockStatement); + } + + public virtual T VisitExpressionStatement (ExpressionStatement expressionStatement) + { + return VisitChildren (expressionStatement); + } + + public virtual T VisitBreakStatement (BreakStatement breakStatement) + { + return VisitChildren (breakStatement); + } + + public virtual T VisitCheckedStatement (CheckedStatement checkedStatement) + { + return VisitChildren (checkedStatement); + } + + public virtual T VisitContinueStatement (ContinueStatement continueStatement) + { + return VisitChildren (continueStatement); + } + + public virtual T VisitDoWhileStatement (DoWhileStatement doWhileStatement) + { + return VisitChildren (doWhileStatement); + } + + public virtual T VisitEmptyStatement (EmptyStatement emptyStatement) + { + return VisitChildren (emptyStatement); + } + + public virtual T VisitFixedStatement (FixedStatement fixedStatement) + { + return VisitChildren (fixedStatement); + } + + public virtual T VisitForeachStatement (ForeachStatement foreachStatement) + { + return VisitChildren (foreachStatement); + } + + public virtual T VisitForStatement (ForStatement forStatement) + { + return VisitChildren (forStatement); + } + + public virtual T VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement) + { + return VisitChildren (gotoCaseStatement); + } + + public virtual T VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement) + { + return VisitChildren (gotoDefaultStatement); + } + + public virtual T VisitGotoStatement (GotoStatement gotoStatement) + { + return VisitChildren (gotoStatement); + } + + public virtual T VisitIfElseStatement (IfElseStatement ifElseStatement) + { + return VisitChildren (ifElseStatement); + } + + public virtual T VisitLabelStatement (LabelStatement labelStatement) + { + return VisitChildren (labelStatement); + } + + public virtual T VisitLockStatement (LockStatement lockStatement) + { + return VisitChildren (lockStatement); + } + + public virtual T VisitReturnStatement (ReturnStatement returnStatement) + { + return VisitChildren (returnStatement); + } + + public virtual T VisitSwitchStatement (SwitchStatement switchStatement) + { + return VisitChildren (switchStatement); + } + + public virtual T VisitSwitchSection (SwitchSection switchSection) + { + return VisitChildren (switchSection); + } + + public virtual T VisitCaseLabel (CaseLabel caseLabel) + { + return VisitChildren (caseLabel); + } + + public virtual T VisitThrowStatement (ThrowStatement throwStatement) + { + return VisitChildren (throwStatement); + } + + public virtual T VisitTryCatchStatement (TryCatchStatement tryCatchStatement) + { + return VisitChildren (tryCatchStatement); + } + + public virtual T VisitCatchClause (CatchClause catchClause) + { + return VisitChildren (catchClause); + } + + public virtual T VisitUncheckedStatement (UncheckedStatement uncheckedStatement) + { + return VisitChildren (uncheckedStatement); + } + + public virtual T VisitUnsafeStatement (UnsafeStatement unsafeStatement) + { + return VisitChildren (unsafeStatement); + } + + public virtual T VisitUsingStatement (UsingStatement usingStatement) + { + return VisitChildren (usingStatement); + } + + public virtual T VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement) + { + return VisitChildren (variableDeclarationStatement); + } + + public virtual T VisitWhileStatement (WhileStatement whileStatement) + { + return VisitChildren (whileStatement); + } + + public virtual T VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement) + { + return VisitChildren (yieldBreakStatement); + } + + public virtual T VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement) + { + return VisitChildren (yieldReturnStatement); + } + + public virtual T VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression) + { + return VisitChildren (anonymousMethodExpression); + } + + public virtual T VisitLambdaExpression (LambdaExpression lambdaExpression) + { + return VisitChildren (lambdaExpression); + } + + public virtual T VisitAssignmentExpression (AssignmentExpression assignmentExpression) + { + return VisitChildren (assignmentExpression); + } + + public virtual T VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression) + { + return VisitChildren (baseReferenceExpression); + } + + public virtual T VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression) + { + return VisitChildren (binaryOperatorExpression); + } + + public virtual T VisitCastExpression (CastExpression castExpression) + { + return VisitChildren (castExpression); + } + + public virtual T VisitCheckedExpression (CheckedExpression checkedExpression) + { + return VisitChildren (checkedExpression); + } + + public virtual T VisitConditionalExpression (ConditionalExpression conditionalExpression) + { + return VisitChildren (conditionalExpression); + } + + public virtual T VisitIdentifierExpression (IdentifierExpression identifierExpression) + { + return VisitChildren (identifierExpression); + } + + public virtual T VisitIndexerExpression (IndexerExpression indexerExpression) + { + return VisitChildren (indexerExpression); + } + + public virtual T VisitInvocationExpression (InvocationExpression invocationExpression) + { + return VisitChildren (invocationExpression); + } + + public virtual T VisitDirectionExpression (DirectionExpression directionExpression) + { + return VisitChildren (directionExpression); + } + + public virtual T VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression) + { + return VisitChildren (memberReferenceExpression); + } + + public virtual T VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression) + { + return VisitChildren (nullReferenceExpression); + } + + public virtual T VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression) + { + return VisitChildren (objectCreateExpression); + } + + public virtual T VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + return VisitChildren (anonymousTypeCreateExpression); + } + + public virtual T VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression) + { + return VisitChildren (arrayCreateExpression); + } + + public virtual T VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression) + { + return VisitChildren (parenthesizedExpression); + } + + public virtual T VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression) + { + return VisitChildren (pointerReferenceExpression); + } + + public virtual T VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + return VisitChildren (primitiveExpression); + } + + public virtual T VisitSizeOfExpression (SizeOfExpression sizeOfExpression) + { + return VisitChildren (sizeOfExpression); + } + + public virtual T VisitStackAllocExpression (StackAllocExpression stackAllocExpression) + { + return VisitChildren (stackAllocExpression); + } + + public virtual T VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + return VisitChildren (thisReferenceExpression); + } + + public virtual T VisitTypeOfExpression (TypeOfExpression typeOfExpression) + { + return VisitChildren (typeOfExpression); + } + + public virtual T VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + return VisitChildren (typeReferenceExpression); + } + + public virtual T VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression) + { + return VisitChildren (unaryOperatorExpression); + } + + public virtual T VisitUncheckedExpression (UncheckedExpression uncheckedExpression) + { + return VisitChildren (uncheckedExpression); + } + + public virtual T VisitQueryExpression(QueryExpression queryExpression) + { + return VisitChildren (queryExpression); + } + + public virtual T VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + return VisitChildren (queryContinuationClause); + } + + public virtual T VisitQueryFromClause(QueryFromClause queryFromClause) + { + return VisitChildren (queryFromClause); + } + + public virtual T VisitQueryLetClause(QueryLetClause queryLetClause) + { + return VisitChildren (queryLetClause); + } + + public virtual T VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + return VisitChildren (queryWhereClause); + } + + public virtual T VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + return VisitChildren (queryJoinClause); + } + + public virtual T VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + return VisitChildren (queryOrderClause); + } + + public virtual T VisitQueryOrdering(QueryOrdering queryOrdering) + { + return VisitChildren (queryOrdering); + } + + public virtual T VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + return VisitChildren (querySelectClause); + } + + public virtual T VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + return VisitChildren (queryGroupClause); + } + + public virtual T VisitAsExpression (AsExpression asExpression) + { + return VisitChildren (asExpression); + } + + public virtual T VisitIsExpression (IsExpression isExpression) + { + return VisitChildren (isExpression); + } + + public virtual T VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression) + { + return VisitChildren (defaultValueExpression); + } + + public virtual T VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression) + { + return VisitChildren (undocumentedExpression); + } + + public virtual T VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) + { + return VisitChildren (arrayInitializerExpression); + } + + public virtual T VisitArraySpecifier (ArraySpecifier arraySpecifier) + { + return VisitChildren (arraySpecifier); + } + + public virtual T VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression) + { + return VisitChildren (namedArgumentExpression); + } + + public virtual T VisitNamedExpression (NamedExpression namedExpression) + { + return VisitChildren (namedExpression); + } + + public virtual T VisitErrorNode(AstNode errorNode) + { + return VisitChildren(errorNode); + } + + public virtual T VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + return VisitChildren (placeholder); + } + } + + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual S VisitChildren (AstNode node, T data) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this, data); + } + return default (S); + } + + public virtual S VisitNullNode(AstNode nullNode, T data) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + return default (S); + } + + public virtual S VisitSyntaxTree (SyntaxTree unit, T data) + { + return VisitChildren (unit, data); + } + + public virtual S VisitComment (Comment comment, T data) + { + return VisitChildren (comment, data); + } + + public virtual S VisitNewLine(NewLineNode newLineNode, T data) + { + return VisitChildren(newLineNode, data); + } + + public virtual S VisitWhitespace(WhitespaceNode whitespaceNode, T data) + { + return VisitChildren(whitespaceNode, data); + } + + public virtual S VisitText(TextNode textNode, T data) + { + return VisitChildren(textNode, data); + } + + public virtual S VisitDocumentationReference (DocumentationReference documentationReference, T data) + { + return VisitChildren (documentationReference, data); + } + + public virtual S VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective, T data) + { + return VisitChildren (preProcessorDirective, data); + } + + public virtual S VisitIdentifier (Identifier identifier, T data) + { + return VisitChildren (identifier, data); + } + + public virtual S VisitCSharpTokenNode (CSharpTokenNode token, T data) + { + return VisitChildren (token, data); + } + + public virtual S VisitPrimitiveType (PrimitiveType primitiveType, T data) + { + return VisitChildren (primitiveType, data); + } + + public virtual S VisitComposedType (ComposedType composedType, T data) + { + return VisitChildren (composedType, data); + } + + public virtual S VisitSimpleType(SimpleType simpleType, T data) + { + return VisitChildren (simpleType, data); + } + + public virtual S VisitMemberType(MemberType memberType, T data) + { + return VisitChildren (memberType, data); + } + + public virtual S VisitAttribute (Attribute attribute, T data) + { + return VisitChildren (attribute, data); + } + + public virtual S VisitAttributeSection (AttributeSection attributeSection, T data) + { + return VisitChildren (attributeSection, data); + } + + public virtual S VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration, T data) + { + return VisitChildren (delegateDeclaration, data); + } + + public virtual S VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration, T data) + { + return VisitChildren (namespaceDeclaration, data); + } + + public virtual S VisitTypeDeclaration (TypeDeclaration typeDeclaration, T data) + { + return VisitChildren (typeDeclaration, data); + } + + public virtual S VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration, T data) + { + return VisitChildren (typeParameterDeclaration, data); + } + + public virtual S VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration, T data) + { + return VisitChildren (enumMemberDeclaration, data); + } + + public virtual S VisitUsingDeclaration (UsingDeclaration usingDeclaration, T data) + { + return VisitChildren (usingDeclaration, data); + } + + public virtual S VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration, T data) + { + return VisitChildren (usingDeclaration, data); + } + + public virtual S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data) + { + return VisitChildren (externAliasDeclaration, data); + } + + public virtual S VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration, T data) + { + return VisitChildren (constructorDeclaration, data); + } + + public virtual S VisitConstructorInitializer (ConstructorInitializer constructorInitializer, T data) + { + return VisitChildren (constructorInitializer, data); + } + + public virtual S VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration, T data) + { + return VisitChildren (destructorDeclaration, data); + } + + public virtual S VisitEventDeclaration (EventDeclaration eventDeclaration, T data) + { + return VisitChildren (eventDeclaration, data); + } + + public virtual S VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration, T data) + { + return VisitChildren (eventDeclaration, data); + } + + public virtual S VisitFieldDeclaration (FieldDeclaration fieldDeclaration, T data) + { + return VisitChildren (fieldDeclaration, data); + } + + public virtual S VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration, T data) + { + return VisitChildren (fixedFieldDeclaration, data); + } + + public virtual S VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer, T data) + { + return VisitChildren (fixedVariableInitializer, data); + } + + public virtual S VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration, T data) + { + return VisitChildren (indexerDeclaration, data); + } + + public virtual S VisitMethodDeclaration (MethodDeclaration methodDeclaration, T data) + { + return VisitChildren (methodDeclaration, data); + } + + public virtual S VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration, T data) + { + return VisitChildren (operatorDeclaration, data); + } + + public virtual S VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration, T data) + { + return VisitChildren (propertyDeclaration, data); + } + + public virtual S VisitAccessor (Accessor accessor, T data) + { + return VisitChildren (accessor, data); + } + + public virtual S VisitVariableInitializer (VariableInitializer variableInitializer, T data) + { + return VisitChildren (variableInitializer, data); + } + + public virtual S VisitParameterDeclaration (ParameterDeclaration parameterDeclaration, T data) + { + return VisitChildren (parameterDeclaration, data); + } + + public virtual S VisitConstraint (Constraint constraint, T data) + { + return VisitChildren (constraint, data); + } + + public virtual S VisitBlockStatement (BlockStatement blockStatement, T data) + { + return VisitChildren (blockStatement, data); + } + + public virtual S VisitExpressionStatement (ExpressionStatement expressionStatement, T data) + { + return VisitChildren (expressionStatement, data); + } + + public virtual S VisitBreakStatement (BreakStatement breakStatement, T data) + { + return VisitChildren (breakStatement, data); + } + + public virtual S VisitCheckedStatement (CheckedStatement checkedStatement, T data) + { + return VisitChildren (checkedStatement, data); + } + + public virtual S VisitContinueStatement (ContinueStatement continueStatement, T data) + { + return VisitChildren (continueStatement, data); + } + + public virtual S VisitDoWhileStatement (DoWhileStatement doWhileStatement, T data) + { + return VisitChildren (doWhileStatement, data); + } + + public virtual S VisitEmptyStatement (EmptyStatement emptyStatement, T data) + { + return VisitChildren (emptyStatement, data); + } + + public virtual S VisitFixedStatement (FixedStatement fixedStatement, T data) + { + return VisitChildren (fixedStatement, data); + } + + public virtual S VisitForeachStatement (ForeachStatement foreachStatement, T data) + { + return VisitChildren (foreachStatement, data); + } + + public virtual S VisitForStatement (ForStatement forStatement, T data) + { + return VisitChildren (forStatement, data); + } + + public virtual S VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement, T data) + { + return VisitChildren (gotoCaseStatement, data); + } + + public virtual S VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement, T data) + { + return VisitChildren (gotoDefaultStatement, data); + } + + public virtual S VisitGotoStatement (GotoStatement gotoStatement, T data) + { + return VisitChildren (gotoStatement, data); + } + + public virtual S VisitIfElseStatement (IfElseStatement ifElseStatement, T data) + { + return VisitChildren (ifElseStatement, data); + } + + public virtual S VisitLabelStatement (LabelStatement labelStatement, T data) + { + return VisitChildren (labelStatement, data); + } + + public virtual S VisitLockStatement (LockStatement lockStatement, T data) + { + return VisitChildren (lockStatement, data); + } + + public virtual S VisitReturnStatement (ReturnStatement returnStatement, T data) + { + return VisitChildren (returnStatement, data); + } + + public virtual S VisitSwitchStatement (SwitchStatement switchStatement, T data) + { + return VisitChildren (switchStatement, data); + } + + public virtual S VisitSwitchSection (SwitchSection switchSection, T data) + { + return VisitChildren (switchSection, data); + } + + public virtual S VisitCaseLabel (CaseLabel caseLabel, T data) + { + return VisitChildren (caseLabel, data); + } + + public virtual S VisitThrowStatement (ThrowStatement throwStatement, T data) + { + return VisitChildren (throwStatement, data); + } + + public virtual S VisitTryCatchStatement (TryCatchStatement tryCatchStatement, T data) + { + return VisitChildren (tryCatchStatement, data); + } + + public virtual S VisitCatchClause (CatchClause catchClause, T data) + { + return VisitChildren (catchClause, data); + } + + public virtual S VisitUncheckedStatement (UncheckedStatement uncheckedStatement, T data) + { + return VisitChildren (uncheckedStatement, data); + } + + public virtual S VisitUnsafeStatement (UnsafeStatement unsafeStatement, T data) + { + return VisitChildren (unsafeStatement, data); + } + + public virtual S VisitUsingStatement (UsingStatement usingStatement, T data) + { + return VisitChildren (usingStatement, data); + } + + public virtual S VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement, T data) + { + return VisitChildren (variableDeclarationStatement, data); + } + + public virtual S VisitWhileStatement (WhileStatement whileStatement, T data) + { + return VisitChildren (whileStatement, data); + } + + public virtual S VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement, T data) + { + return VisitChildren (yieldBreakStatement, data); + } + + public virtual S VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement, T data) + { + return VisitChildren (yieldReturnStatement, data); + } + + public virtual S VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression, T data) + { + return VisitChildren (anonymousMethodExpression, data); + } + + public virtual S VisitLambdaExpression (LambdaExpression lambdaExpression, T data) + { + return VisitChildren (lambdaExpression, data); + } + + public virtual S VisitAssignmentExpression (AssignmentExpression assignmentExpression, T data) + { + return VisitChildren (assignmentExpression, data); + } + + public virtual S VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression, T data) + { + return VisitChildren (baseReferenceExpression, data); + } + + public virtual S VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression, T data) + { + return VisitChildren (binaryOperatorExpression, data); + } + + public virtual S VisitCastExpression (CastExpression castExpression, T data) + { + return VisitChildren (castExpression, data); + } + + public virtual S VisitCheckedExpression (CheckedExpression checkedExpression, T data) + { + return VisitChildren (checkedExpression, data); + } + + public virtual S VisitConditionalExpression (ConditionalExpression conditionalExpression, T data) + { + return VisitChildren (conditionalExpression, data); + } + + public virtual S VisitIdentifierExpression (IdentifierExpression identifierExpression, T data) + { + return VisitChildren (identifierExpression, data); + } + + public virtual S VisitIndexerExpression (IndexerExpression indexerExpression, T data) + { + return VisitChildren (indexerExpression, data); + } + + public virtual S VisitInvocationExpression (InvocationExpression invocationExpression, T data) + { + return VisitChildren (invocationExpression, data); + } + + public virtual S VisitDirectionExpression (DirectionExpression directionExpression, T data) + { + return VisitChildren (directionExpression, data); + } + + public virtual S VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression, T data) + { + return VisitChildren (memberReferenceExpression, data); + } + + public virtual S VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression, T data) + { + return VisitChildren (nullReferenceExpression, data); + } + + public virtual S VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression, T data) + { + return VisitChildren (objectCreateExpression, data); + } + + public virtual S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data) + { + return VisitChildren (anonymousTypeCreateExpression, data); + } + + public virtual S VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression, T data) + { + return VisitChildren (arrayCreateExpression, data); + } + + public virtual S VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression, T data) + { + return VisitChildren (parenthesizedExpression, data); + } + + public virtual S VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression, T data) + { + return VisitChildren (pointerReferenceExpression, data); + } + + public virtual S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data) + { + return VisitChildren (primitiveExpression, data); + } + + public virtual S VisitSizeOfExpression (SizeOfExpression sizeOfExpression, T data) + { + return VisitChildren (sizeOfExpression, data); + } + + public virtual S VisitStackAllocExpression (StackAllocExpression stackAllocExpression, T data) + { + return VisitChildren (stackAllocExpression, data); + } + + public virtual S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data) + { + return VisitChildren (thisReferenceExpression, data); + } + + public virtual S VisitTypeOfExpression (TypeOfExpression typeOfExpression, T data) + { + return VisitChildren (typeOfExpression, data); + } + + public virtual S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data) + { + return VisitChildren (typeReferenceExpression, data); + } + + public virtual S VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression, T data) + { + return VisitChildren (unaryOperatorExpression, data); + } + + public virtual S VisitUncheckedExpression (UncheckedExpression uncheckedExpression, T data) + { + return VisitChildren (uncheckedExpression, data); + } + + public virtual S VisitQueryExpression(QueryExpression queryExpression, T data) + { + return VisitChildren (queryExpression, data); + } + + public virtual S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data) + { + return VisitChildren (queryContinuationClause, data); + } + + public virtual S VisitQueryFromClause(QueryFromClause queryFromClause, T data) + { + return VisitChildren (queryFromClause, data); + } + + public virtual S VisitQueryLetClause(QueryLetClause queryLetClause, T data) + { + return VisitChildren (queryLetClause, data); + } + + public virtual S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data) + { + return VisitChildren (queryWhereClause, data); + } + + public virtual S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data) + { + return VisitChildren (queryJoinClause, data); + } + + public virtual S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data) + { + return VisitChildren (queryOrderClause, data); + } + + public virtual S VisitQueryOrdering(QueryOrdering queryOrdering, T data) + { + return VisitChildren (queryOrdering, data); + } + + public virtual S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data) + { + return VisitChildren (querySelectClause, data); + } + + public virtual S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data) + { + return VisitChildren (queryGroupClause, data); + } + + public virtual S VisitAsExpression (AsExpression asExpression, T data) + { + return VisitChildren (asExpression, data); + } + + public virtual S VisitIsExpression (IsExpression isExpression, T data) + { + return VisitChildren (isExpression, data); + } + + public virtual S VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression, T data) + { + return VisitChildren (defaultValueExpression, data); + } + + public virtual S VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression, T data) + { + return VisitChildren (undocumentedExpression, data); + } + + public virtual S VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression, T data) + { + return VisitChildren (arrayInitializerExpression, data); + } + + public virtual S VisitArraySpecifier (ArraySpecifier arraySpecifier, T data) + { + return VisitChildren (arraySpecifier, data); + } + + public virtual S VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression, T data) + { + return VisitChildren (namedArgumentExpression, data); + } + + public virtual S VisitNamedExpression (NamedExpression namedExpression, T data) + { + return VisitChildren (namedExpression, data); + } + + public virtual S VisitErrorNode(AstNode errorNode, T data) + { + return VisitChildren(errorNode, data); + } + + public virtual S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data) + { + return VisitChildren (placeholder, data); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs new file mode 100644 index 000000000..633f921b2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs @@ -0,0 +1,149 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a 'cref' reference in XML documentation. + /// + public class DocumentationReference : AstNode + { + public static readonly Role DeclaringTypeRole = new Role("DeclaringType", AstType.Null); + public static readonly Role ConversionOperatorReturnTypeRole = new Role("ConversionOperatorReturnType", AstType.Null); + + SymbolKind symbolKind; + OperatorType operatorType; + bool hasParameterList; + + /// + /// Gets/Sets the entity type. + /// Possible values are: + /// SymbolKind.Operator for operators, + /// SymbolKind.Indexer for indexers, + /// SymbolKind.TypeDefinition for references to primitive types, + /// and SymbolKind.None for everything else. + /// + public SymbolKind SymbolKind { + get { return symbolKind; } + set { + ThrowIfFrozen(); + symbolKind = value; + } + } + + /// + /// Gets/Sets the operator type. + /// This property is only used when SymbolKind==Operator. + /// + public OperatorType OperatorType { + get { return operatorType; } + set { + ThrowIfFrozen(); + operatorType = value; + } + } + + /// + /// Gets/Sets whether a parameter list was provided. + /// + public bool HasParameterList { + get { return hasParameterList; } + set { + ThrowIfFrozen(); + hasParameterList = value; + } + } + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + /// + /// Gets/Sets the declaring type. + /// + public AstType DeclaringType { + get { return GetChildByRole(DeclaringTypeRole); } + set { SetChildByRole(DeclaringTypeRole, value); } + } + + /// + /// Gets/sets the member name. + /// This property is only used when SymbolKind==None. + /// + public string MemberName { + get { return GetChildByRole(Roles.Identifier).Name; } + set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } + } + + /// + /// Gets/Sets the return type of conversion operators. + /// This property is only used when SymbolKind==Operator and OperatorType is explicit or implicit. + /// + public AstType ConversionOperatorReturnType { + get { return GetChildByRole(ConversionOperatorReturnTypeRole); } + set { SetChildByRole(ConversionOperatorReturnTypeRole, value); } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + DocumentationReference o = other as DocumentationReference; + if (!(o != null && this.SymbolKind == o.SymbolKind && this.HasParameterList == o.HasParameterList)) + return false; + if (this.SymbolKind == SymbolKind.Operator) { + if (this.OperatorType != o.OperatorType) + return false; + if (this.OperatorType == OperatorType.Implicit || this.OperatorType == OperatorType.Explicit) { + if (!this.ConversionOperatorReturnType.DoMatch(o.ConversionOperatorReturnType, match)) + return false; + } + } else if (this.SymbolKind == SymbolKind.None) { + if (!MatchString(this.MemberName, o.MemberName)) + return false; + if (!this.TypeArguments.DoMatch(o.TypeArguments, match)) + return false; + } + return this.Parameters.DoMatch(o.Parameters, match); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDocumentationReference (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDocumentationReference (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitDocumentationReference (this, data); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ErrorNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ErrorNode.cs new file mode 100644 index 000000000..36d9ef6e0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ErrorNode.cs @@ -0,0 +1,88 @@ +// +// ErrorNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin (http://www.xamarin.com); +// +// 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 +{ + /// + /// Represents a parsing error in the ast. At the moment it only represents missing closing bracket. + /// This closing bracket is replaced by a node at the highest possible position. + /// (To make GetAstNodeAt (line, col) working). + /// + public class ErrorNode : AstNode + { + static TextLocation maxLoc = new TextLocation (int.MaxValue, int.MaxValue); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override TextLocation StartLocation { + get { + return maxLoc; + } + } + + public override TextLocation EndLocation { + get { + return maxLoc; + } + } + + public ErrorNode () + { + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitErrorNode(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitErrorNode(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitErrorNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as ErrorNode; + return o != null; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return "[ErrorNode]"; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs new file mode 100644 index 000000000..e8de95431 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs @@ -0,0 +1,117 @@ +// +// AnonymousMethodExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [async] delegate(Parameters) {Body} + /// + public class AnonymousMethodExpression : Expression + { + public readonly static TokenRole DelegateKeywordRole = new TokenRole ("delegate"); + public readonly static TokenRole AsyncModifierRole = LambdaExpression.AsyncModifierRole; + + 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 { return hasParameterList || Parameters.Any(); } + set { ThrowIfFrozen(); hasParameterList = value; } + } + + public CSharpTokenNode DelegateToken { + get { return GetChildByRole (DelegateKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public AnonymousMethodExpression () + { + } + + public AnonymousMethodExpression (BlockStatement body, IEnumerable parameters = null) + { + if (parameters != null) { + hasParameterList = true; + foreach (var parameter in parameters) { + AddChild (parameter, Roles.Parameter); + } + } + AddChild (body, Roles.Body); + } + + public AnonymousMethodExpression (BlockStatement body, params ParameterDeclaration[] parameters) : this (body, (IEnumerable)parameters) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAnonymousMethodExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAnonymousMethodExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAnonymousMethodExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AnonymousMethodExpression o = other as AnonymousMethodExpression; + return o != null && this.IsAsync == o.IsAsync && this.HasParameterList == o.HasParameterList + && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs new file mode 100644 index 000000000..944bf61f5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs @@ -0,0 +1,91 @@ +// +// AnonymousTypeCreateExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new { [ExpressionList] } + /// + public class AnonymousTypeCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Initializers { + get { return GetChildrenByRole (Roles.Expression); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AnonymousTypeCreateExpression () + { + } + + public AnonymousTypeCreateExpression (IEnumerable initializers) + { + foreach (var ini in initializers) { + AddChild (ini, Roles.Expression); + } + } + + public AnonymousTypeCreateExpression (params Expression[] initializer) : this ((IEnumerable)initializer) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAnonymousTypeCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAnonymousTypeCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAnonymousTypeCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as AnonymousTypeCreateExpression; + return o != null && this.Initializers.DoMatch(o.Initializers, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayCreateExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayCreateExpression.cs new file mode 100644 index 000000000..3720a3fc8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayCreateExpression.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new Type[Dimensions] + /// + public class ArrayCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + public readonly static Role AdditionalArraySpecifierRole = new Role("AdditionalArraySpecifier"); + public readonly static Role InitializerRole = new Role("Initializer", ArrayInitializerExpression.Null); + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + /// + /// Gets additional array ranks (those without size info). + /// Empty for "new int[5,1]"; will contain a single element for "new int[5][]". + /// + public AstNodeCollection AdditionalArraySpecifiers { + get { return GetChildrenByRole(AdditionalArraySpecifierRole); } + } + + public ArrayInitializerExpression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArrayCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArrayCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArrayCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArrayCreateExpression o = other as ArrayCreateExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.AdditionalArraySpecifiers.DoMatch(o.AdditionalArraySpecifiers, match) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayInitializerExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayInitializerExpression.cs new file mode 100644 index 000000000..fa3246f92 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayInitializerExpression.cs @@ -0,0 +1,192 @@ +// +// ArrayInitializerExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// { Elements } + /// + public class ArrayInitializerExpression : Expression + { + /// + /// For ease of use purposes in the resolver the ast representation + /// of { a, b, c } is { {a}, {b}, {c} }. + /// If IsSingleElement is true then this array initializer expression is a generated one. + /// That has no meaning in the source code (and contains no brace tokens). + /// + public virtual bool IsSingleElement { + get { + return false; + } + } + + public ArrayInitializerExpression() + { + } + + public ArrayInitializerExpression(IEnumerable elements) + { + this.Elements.AddRange(elements); + } + + public ArrayInitializerExpression(params Expression[] elements) + { + this.Elements.AddRange(elements); + } + + #region Null + public new static readonly ArrayInitializerExpression Null = new NullArrayInitializerExpression (); + + sealed class NullArrayInitializerExpression : ArrayInitializerExpression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Elements { + get { return GetChildrenByRole(Roles.Expression); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArrayInitializerExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArrayInitializerExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArrayInitializerExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArrayInitializerExpression o = other as ArrayInitializerExpression; + return o != null && this.Elements.DoMatch(o.Elements, match); + } + + public static ArrayInitializerExpression CreateSingleElementInitializer () + { + return new SingleArrayInitializerExpression(); + } + /// + /// Single elements in array initializers are represented with this special class. + /// + class SingleArrayInitializerExpression : ArrayInitializerExpression + { + public override bool IsSingleElement { + get { + return true; + } + } + + } + + #region PatternPlaceholder + public static implicit operator ArrayInitializerExpression(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : ArrayInitializerExpression, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AsExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AsExpression.cs new file mode 100644 index 000000000..5a7b5ac5d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AsExpression.cs @@ -0,0 +1,150 @@ +// +// AsExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression as TypeReference + /// + public class AsExpression : Expression + { + public readonly static TokenRole AsKeywordRole = new TokenRole ("as"); + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode AsToken { + get { return GetChildByRole (AsKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public AsExpression () + { + } + + public AsExpression (Expression expression, AstType type) + { + AddChild (expression, Roles.Expression); + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAsExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAsExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAsExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AsExpression o = other as AsExpression; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.Type.DoMatch(o.Type, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs new file mode 100644 index 000000000..95d0cdf28 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs @@ -0,0 +1,304 @@ +// +// AssignmentExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq.Expressions; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Left Operator= Right + /// + public class AssignmentExpression : Expression + { + // reuse roles from BinaryOperatorExpression + public readonly static Role LeftRole = BinaryOperatorExpression.LeftRole; + public readonly static Role RightRole = BinaryOperatorExpression.RightRole; + + public readonly static TokenRole AssignRole = new TokenRole ("="); + public readonly static TokenRole AddRole = new TokenRole ("+="); + public readonly static TokenRole SubtractRole = new TokenRole ("-="); + public readonly static TokenRole MultiplyRole = new TokenRole ("*="); + public readonly static TokenRole DivideRole = new TokenRole ("/="); + public readonly static TokenRole ModulusRole = new TokenRole ("%="); + public readonly static TokenRole ShiftLeftRole = new TokenRole ("<<="); + public readonly static TokenRole ShiftRightRole = new TokenRole (">>="); + public readonly static TokenRole BitwiseAndRole = new TokenRole ("&="); + public readonly static TokenRole BitwiseOrRole = new TokenRole ("|="); + public readonly static TokenRole ExclusiveOrRole = new TokenRole ("^="); + + public AssignmentExpression() + { + } + + public AssignmentExpression(Expression left, Expression right) + { + this.Left = left; + this.Right = right; + } + + public AssignmentExpression(Expression left, AssignmentOperatorType op, Expression right) + { + this.Left = left; + this.Operator = op; + this.Right = right; + } + + public AssignmentOperatorType Operator { + get; + set; + } + + public Expression Left { + get { return GetChildByRole (LeftRole); } + set { SetChildByRole(LeftRole, value); } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole(Operator)); } + } + + public Expression Right { + get { return GetChildByRole (RightRole); } + set { SetChildByRole(RightRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAssignmentExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAssignmentExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAssignmentExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AssignmentExpression o = other as AssignmentExpression; + return o != null && (this.Operator == AssignmentOperatorType.Any || this.Operator == o.Operator) + && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); + } + + public static TokenRole GetOperatorRole(AssignmentOperatorType op) + { + switch (op) { + case AssignmentOperatorType.Assign: + return AssignRole; + case AssignmentOperatorType.Add: + return AddRole; + case AssignmentOperatorType.Subtract: + return SubtractRole; + case AssignmentOperatorType.Multiply: + return MultiplyRole; + case AssignmentOperatorType.Divide: + return DivideRole; + case AssignmentOperatorType.Modulus: + return ModulusRole; + case AssignmentOperatorType.ShiftLeft: + return ShiftLeftRole; + case AssignmentOperatorType.ShiftRight: + return ShiftRightRole; + case AssignmentOperatorType.BitwiseAnd: + return BitwiseAndRole; + case AssignmentOperatorType.BitwiseOr: + return BitwiseOrRole; + case AssignmentOperatorType.ExclusiveOr: + return ExclusiveOrRole; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + /// + /// Gets the binary operator for the specified compound assignment operator. + /// Returns null if 'op' is not a compound assignment. + /// + public static BinaryOperatorType? GetCorrespondingBinaryOperator(AssignmentOperatorType op) + { + switch (op) { + case AssignmentOperatorType.Assign: + return null; + case AssignmentOperatorType.Add: + return BinaryOperatorType.Add; + case AssignmentOperatorType.Subtract: + return BinaryOperatorType.Subtract; + case AssignmentOperatorType.Multiply: + return BinaryOperatorType.Multiply; + case AssignmentOperatorType.Divide: + return BinaryOperatorType.Divide; + case AssignmentOperatorType.Modulus: + return BinaryOperatorType.Modulus; + case AssignmentOperatorType.ShiftLeft: + return BinaryOperatorType.ShiftLeft; + case AssignmentOperatorType.ShiftRight: + return BinaryOperatorType.ShiftRight; + case AssignmentOperatorType.BitwiseAnd: + return BinaryOperatorType.BitwiseAnd; + case AssignmentOperatorType.BitwiseOr: + return BinaryOperatorType.BitwiseOr; + case AssignmentOperatorType.ExclusiveOr: + return BinaryOperatorType.ExclusiveOr; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(AssignmentOperatorType op, bool checkForOverflow) + { + switch (op) { + case AssignmentOperatorType.Assign: + return ExpressionType.Assign; + case AssignmentOperatorType.Add: + return checkForOverflow ? ExpressionType.AddAssignChecked : ExpressionType.AddAssign; + case AssignmentOperatorType.Subtract: + return checkForOverflow ? ExpressionType.SubtractAssignChecked : ExpressionType.SubtractAssign; + case AssignmentOperatorType.Multiply: + return checkForOverflow ? ExpressionType.MultiplyAssignChecked : ExpressionType.MultiplyAssign; + case AssignmentOperatorType.Divide: + return ExpressionType.DivideAssign; + case AssignmentOperatorType.Modulus: + return ExpressionType.ModuloAssign; + case AssignmentOperatorType.ShiftLeft: + return ExpressionType.LeftShiftAssign; + case AssignmentOperatorType.ShiftRight: + return ExpressionType.RightShiftAssign; + case AssignmentOperatorType.BitwiseAnd: + return ExpressionType.AndAssign; + case AssignmentOperatorType.BitwiseOr: + return ExpressionType.OrAssign; + case AssignmentOperatorType.ExclusiveOr: + return ExpressionType.ExclusiveOrAssign; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public enum AssignmentOperatorType + { + /// left = right + Assign, + + /// left += right + Add, + /// left -= right + Subtract, + /// left *= right + Multiply, + /// left /= right + Divide, + /// left %= right + Modulus, + + /// left <<= right + ShiftLeft, + /// left >>= right + ShiftRight, + + /// left &= right + BitwiseAnd, + /// left |= right + BitwiseOr, + /// left ^= right + ExclusiveOr, + + /// Any operator (for pattern matching) + Any + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BaseReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BaseReferenceExpression.cs new file mode 100644 index 000000000..399c36c22 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BaseReferenceExpression.cs @@ -0,0 +1,71 @@ +// +// BaseReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// base + /// + public class BaseReferenceExpression : Expression + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + "base".Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBaseReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBaseReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBaseReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BaseReferenceExpression o = other as BaseReferenceExpression; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BinaryOperatorExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BinaryOperatorExpression.cs new file mode 100644 index 000000000..e4408e184 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BinaryOperatorExpression.cs @@ -0,0 +1,325 @@ +// +// BinaryOperatorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq.Expressions; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Left Operator Right + /// + public class BinaryOperatorExpression : Expression + { + public readonly static TokenRole BitwiseAndRole = new TokenRole ("&"); + public readonly static TokenRole BitwiseOrRole = new TokenRole ("|"); + public readonly static TokenRole ConditionalAndRole = new TokenRole ("&&"); + public readonly static TokenRole ConditionalOrRole = new TokenRole ("||"); + public readonly static TokenRole ExclusiveOrRole = new TokenRole ("^"); + public readonly static TokenRole GreaterThanRole = new TokenRole (">"); + public readonly static TokenRole GreaterThanOrEqualRole = new TokenRole (">="); + public readonly static TokenRole EqualityRole = new TokenRole ("=="); + public readonly static TokenRole InEqualityRole = new TokenRole ("!="); + public readonly static TokenRole LessThanRole = new TokenRole ("<"); + public readonly static TokenRole LessThanOrEqualRole = new TokenRole ("<="); + public readonly static TokenRole AddRole = new TokenRole ("+"); + public readonly static TokenRole SubtractRole = new TokenRole ("-"); + public readonly static TokenRole MultiplyRole = new TokenRole ("*"); + public readonly static TokenRole DivideRole = new TokenRole ("/"); + public readonly static TokenRole ModulusRole = new TokenRole ("%"); + public readonly static TokenRole ShiftLeftRole = new TokenRole ("<<"); + public readonly static TokenRole ShiftRightRole = new TokenRole (">>"); + public readonly static TokenRole NullCoalescingRole = new TokenRole ("??"); + + public readonly static Role LeftRole = new Role("Left", Expression.Null); + public readonly static Role RightRole = new Role("Right", Expression.Null); + + public BinaryOperatorExpression() + { + } + + public BinaryOperatorExpression(Expression left, BinaryOperatorType op, Expression right) + { + this.Left = left; + this.Operator = op; + this.Right = right; + } + + public BinaryOperatorType Operator { + get; + set; + } + + public Expression Left { + get { return GetChildByRole (LeftRole); } + set { SetChildByRole(LeftRole, value); } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole (Operator)); } + } + + public Expression Right { + get { return GetChildByRole (RightRole); } + set { SetChildByRole (RightRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBinaryOperatorExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBinaryOperatorExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBinaryOperatorExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BinaryOperatorExpression o = other as BinaryOperatorExpression; + return o != null && (this.Operator == BinaryOperatorType.Any || this.Operator == o.Operator) + && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); + } + + public static TokenRole GetOperatorRole (BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.BitwiseAnd: + return BitwiseAndRole; + case BinaryOperatorType.BitwiseOr: + return BitwiseOrRole; + case BinaryOperatorType.ConditionalAnd: + return ConditionalAndRole; + case BinaryOperatorType.ConditionalOr: + return ConditionalOrRole; + case BinaryOperatorType.ExclusiveOr: + return ExclusiveOrRole; + case BinaryOperatorType.GreaterThan: + return GreaterThanRole; + case BinaryOperatorType.GreaterThanOrEqual: + return GreaterThanOrEqualRole; + case BinaryOperatorType.Equality: + return EqualityRole; + case BinaryOperatorType.InEquality: + return InEqualityRole; + case BinaryOperatorType.LessThan: + return LessThanRole; + case BinaryOperatorType.LessThanOrEqual: + return LessThanOrEqualRole; + case BinaryOperatorType.Add: + return AddRole; + case BinaryOperatorType.Subtract: + return SubtractRole; + case BinaryOperatorType.Multiply: + return MultiplyRole; + case BinaryOperatorType.Divide: + return DivideRole; + case BinaryOperatorType.Modulus: + return ModulusRole; + case BinaryOperatorType.ShiftLeft: + return ShiftLeftRole; + case BinaryOperatorType.ShiftRight: + return ShiftRightRole; + case BinaryOperatorType.NullCoalescing: + return NullCoalescingRole; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(BinaryOperatorType op, bool checkForOverflow) + { + switch (op) { + case BinaryOperatorType.BitwiseAnd: + return ExpressionType.And; + case BinaryOperatorType.BitwiseOr: + return ExpressionType.Or; + case BinaryOperatorType.ConditionalAnd: + return ExpressionType.AndAlso; + case BinaryOperatorType.ConditionalOr: + return ExpressionType.OrElse; + case BinaryOperatorType.ExclusiveOr: + return ExpressionType.ExclusiveOr; + case BinaryOperatorType.GreaterThan: + return ExpressionType.GreaterThan; + case BinaryOperatorType.GreaterThanOrEqual: + return ExpressionType.GreaterThanOrEqual; + case BinaryOperatorType.Equality: + return ExpressionType.Equal; + case BinaryOperatorType.InEquality: + return ExpressionType.NotEqual; + case BinaryOperatorType.LessThan: + return ExpressionType.LessThan; + case BinaryOperatorType.LessThanOrEqual: + return ExpressionType.LessThanOrEqual; + case BinaryOperatorType.Add: + return checkForOverflow ? ExpressionType.AddChecked : ExpressionType.Add; + case BinaryOperatorType.Subtract: + return checkForOverflow ? ExpressionType.SubtractChecked : ExpressionType.Subtract; + case BinaryOperatorType.Multiply: + return checkForOverflow ? ExpressionType.MultiplyChecked : ExpressionType.Multiply; + case BinaryOperatorType.Divide: + return ExpressionType.Divide; + case BinaryOperatorType.Modulus: + return ExpressionType.Modulo; + case BinaryOperatorType.ShiftLeft: + return ExpressionType.LeftShift; + case BinaryOperatorType.ShiftRight: + return ExpressionType.RightShift; + case BinaryOperatorType.NullCoalescing: + return ExpressionType.Coalesce; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public enum BinaryOperatorType + { + /// + /// Any binary operator (used in pattern matching) + /// + Any, + + // We avoid 'logical or' on purpose, because it's not clear if that refers to the bitwise + // or to the short-circuiting (conditional) operator: + // MCS and old NRefactory used bitwise='|', logical='||' + // but the C# spec uses logical='|', conditional='||' + /// left & right + BitwiseAnd, + /// left | right + BitwiseOr, + /// left && right + ConditionalAnd, + /// left || right + ConditionalOr, + /// left ^ right + ExclusiveOr, + + /// left > right + GreaterThan, + /// left >= right + GreaterThanOrEqual, + /// left == right + Equality, + /// left != right + InEquality, + /// left < right + LessThan, + /// left <= right + LessThanOrEqual, + + /// left + right + Add, + /// left - right + Subtract, + /// left * right + Multiply, + /// left / right + Divide, + /// left % right + Modulus, + + /// left << right + ShiftLeft, + /// left >> right + ShiftRight, + + /// left ?? right + NullCoalescing + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CastExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CastExpression.cs new file mode 100644 index 000000000..e771d18fe --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CastExpression.cs @@ -0,0 +1,152 @@ +// +// CastExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// (CastTo)Expression + /// + public class CastExpression : Expression + { + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CastExpression () + { + } + + public CastExpression (AstType castToType, Expression expression) + { + AddChild (castToType, Roles.Type); + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCastExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCastExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCastExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CastExpression o = other as CastExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Expression.DoMatch(o.Expression, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CheckedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CheckedExpression.cs new file mode 100644 index 000000000..66bdcb54d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CheckedExpression.cs @@ -0,0 +1,83 @@ +// +// CheckedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// checked(Expression) + /// + public class CheckedExpression : Expression + { + public readonly static TokenRole CheckedKeywordRole = new TokenRole ("checked"); + + public CSharpTokenNode CheckedToken { + get { return GetChildByRole (CheckedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CheckedExpression () + { + } + + public CheckedExpression (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCheckedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCheckedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCheckedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CheckedExpression o = other as CheckedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ConditionalExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ConditionalExpression.cs new file mode 100644 index 000000000..4367a0cce --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ConditionalExpression.cs @@ -0,0 +1,162 @@ +// +// ConditionalExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Condition ? TrueExpression : FalseExpression + /// + public class ConditionalExpression : Expression + { + public readonly static Role ConditionRole = Roles.Condition; + public readonly static TokenRole QuestionMarkRole = new TokenRole("?"); + public readonly static Role TrueRole = new Role("True", Expression.Null); + public readonly static TokenRole ColonRole = Roles.Colon; + public readonly static Role FalseRole = new Role("False", Expression.Null); + + public Expression Condition { + get { return GetChildByRole(ConditionRole); } + set { SetChildByRole(ConditionRole, value); } + } + + public CSharpTokenNode QuestionMarkToken { + get { return GetChildByRole (QuestionMarkRole); } + } + + public Expression TrueExpression { + get { return GetChildByRole(TrueRole); } + set { SetChildByRole(TrueRole, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (ColonRole); } + } + + public Expression FalseExpression { + get { return GetChildByRole(FalseRole); } + set { SetChildByRole(FalseRole, value); } + } + + public ConditionalExpression () + { + } + + public ConditionalExpression (Expression condition, Expression trueExpression, Expression falseExpression) + { + AddChild (condition, ConditionRole); + AddChild (trueExpression, TrueRole); + AddChild (falseExpression, FalseRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConditionalExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConditionalExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConditionalExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConditionalExpression o = other as ConditionalExpression; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.TrueExpression.DoMatch(o.TrueExpression, match) && this.FalseExpression.DoMatch(o.FalseExpression, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DefaultValueExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DefaultValueExpression.cs new file mode 100644 index 000000000..0aab343f3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DefaultValueExpression.cs @@ -0,0 +1,84 @@ +// +// DefaultValueExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// default(Type) + /// + public class DefaultValueExpression : Expression + { + public readonly static TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public CSharpTokenNode DefaultToken { + get { return GetChildByRole (DefaultKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public DefaultValueExpression () + { + } + + public DefaultValueExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDefaultValueExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDefaultValueExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDefaultValueExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DefaultValueExpression o = other as DefaultValueExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DirectionExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DirectionExpression.cs new file mode 100644 index 000000000..a17c117bb --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DirectionExpression.cs @@ -0,0 +1,89 @@ +// +// DirectionExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum FieldDirection + { + None, + Out, + Ref + } + + /// + /// ref Expression + /// + public class DirectionExpression : Expression + { + public readonly static TokenRole RefKeywordRole = new TokenRole ("ref"); + public readonly static TokenRole OutKeywordRole = new TokenRole ("out"); + + public FieldDirection FieldDirection { + get; + set; + } + + public CSharpTokenNode FieldDirectionToken { + get { return FieldDirection == ICSharpCode.NRefactory.CSharp.FieldDirection.Ref ? GetChildByRole (RefKeywordRole) : GetChildByRole (OutKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public DirectionExpression () + { + } + + public DirectionExpression (FieldDirection direction, Expression expression) + { + this.FieldDirection = direction; + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDirectionExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDirectionExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDirectionExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DirectionExpression o = other as DirectionExpression; + return o != null && this.FieldDirection == o.FieldDirection && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ErrorExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ErrorExpression.cs new file mode 100644 index 000000000..6054792e4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ErrorExpression.cs @@ -0,0 +1,129 @@ +// +// ErrorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// 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 +{ + [Obsolete("This class is obsolete. Remove all referencing code.")] + public class EmptyExpression : AstNode + { + #region implemented abstract members of AstNode + + public override void AcceptVisitor(IAstVisitor visitor) + { + throw new NotImplementedException(); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + throw new NotImplementedException(); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + throw new NotImplementedException(); + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + throw new NotImplementedException(); + } + + public override NodeType NodeType { + get { + throw new NotImplementedException(); + } + } + + #endregion + + + } + + public class ErrorExpression : Expression + { + TextLocation location; + + public override TextLocation StartLocation { + get { + return location; + } + } + + public override TextLocation EndLocation { + get { + return location; + } + } + + public string Error { + get; + private set; + } + + public ErrorExpression () + { + } + + public ErrorExpression (TextLocation location) + { + this.location = location; + } + + public ErrorExpression (string error) + { + this.Error = error; + } + + public ErrorExpression (string error, TextLocation location) + { + this.location = location; + this.Error = error; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitErrorNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitErrorNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitErrorNode(this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as ErrorExpression; + return o != null; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/Expression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/Expression.cs new file mode 100644 index 000000000..22962a606 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/Expression.cs @@ -0,0 +1,230 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Base class for expressions. + /// + /// + /// This class is useful even though it doesn't provide any additional functionality: + /// It can be used to communicate more information in APIs, e.g. "this subnode will always be an expression" + /// + public abstract class Expression : AstNode + { + #region Null + public new static readonly Expression Null = new NullExpression (); + + sealed class NullExpression : Expression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator Expression(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : Expression, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Expression; + } + } + + public new Expression Clone() + { + return (Expression)base.Clone(); + } + + public Expression ReplaceWith(Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException("replaceFunction"); + return (Expression)base.ReplaceWith(node => replaceFunction((Expression)node)); + } + + #region Builder methods + /// + /// Builds an member reference expression using this expression as target. + /// + public virtual MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + /// + /// Builds an indexer expression using this expression as target. + /// + public virtual IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = this; + expr.Arguments.AddRange(arguments); + return expr; + } + + /// + /// Builds an indexer expression using this expression as target. + /// + public virtual IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = this; + expr.Arguments.AddRange(arguments); + return expr; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, IEnumerable arguments) + { + return Invoke(methodName, null, arguments); + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, params Expression[] arguments) + { + return Invoke(methodName, null, arguments); + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = this; + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = this; + ie.Arguments.AddRange(arguments); + return ie; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = this; + ie.Arguments.AddRange(arguments); + return ie; + } + + public virtual CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = this }; + } + + public virtual AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = this }; + } + + public virtual IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = this }; + } + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IdentifierExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IdentifierExpression.cs new file mode 100644 index 000000000..0ec466c3f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IdentifierExpression.cs @@ -0,0 +1,93 @@ +// +// IdentifierExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class IdentifierExpression : Expression + { + public IdentifierExpression() + { + } + + public IdentifierExpression(string identifier) + { + this.Identifier = identifier; + } + + public IdentifierExpression(string identifier, TextLocation location) + { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (identifier, location)); + } + +// public Identifier IdentifierToken { +// get { return GetChildByRole (Roles.Identifier); } +// } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIdentifierExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIdentifierExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIdentifierExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IdentifierExpression o = other as IdentifierExpression; + return o != null && MatchString(this.Identifier, o.Identifier) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IndexerExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IndexerExpression.cs new file mode 100644 index 000000000..cbc80c2cf --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IndexerExpression.cs @@ -0,0 +1,92 @@ +// +// IndexerExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target[Arguments] + /// + public class IndexerExpression : Expression + { + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public IndexerExpression () + { + } + + public IndexerExpression (Expression target, IEnumerable arguments) + { + AddChild (target, Roles.TargetExpression); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public IndexerExpression (Expression target, params Expression[] arguments) : this (target, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIndexerExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIndexerExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIndexerExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IndexerExpression o = other as IndexerExpression; + return o != null && this.Target.DoMatch(o.Target, match) && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/InvocationExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/InvocationExpression.cs new file mode 100644 index 000000000..ad768d541 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/InvocationExpression.cs @@ -0,0 +1,92 @@ +// +// InvocationExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target(Arguments) + /// + public class InvocationExpression : Expression + { + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitInvocationExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitInvocationExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitInvocationExpression (this, data); + } + + public InvocationExpression () + { + } + + public InvocationExpression (Expression target, IEnumerable arguments) + { + AddChild (target, Roles.TargetExpression); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public InvocationExpression (Expression target, params Expression[] arguments) : this (target, (IEnumerable)arguments) + { + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + InvocationExpression o = other as InvocationExpression; + return o != null && this.Target.DoMatch(o.Target, match) && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IsExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IsExpression.cs new file mode 100644 index 000000000..791ab25d7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IsExpression.cs @@ -0,0 +1,150 @@ +// +// TypeOfIsExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression is Type + /// + public class IsExpression : Expression + { + public readonly static TokenRole IsKeywordRole = new TokenRole ("is"); + + public Expression Expression { + get { return GetChildByRole(Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode IsToken { + get { return GetChildByRole (IsKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public IsExpression() + { + } + + public IsExpression (Expression expression, AstType type) + { + AddChild (expression, Roles.Expression); + AddChild (type, Roles.Type); + } + + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIsExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIsExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIsExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IsExpression o = other as IsExpression; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.Type.DoMatch(o.Type, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs new file mode 100644 index 000000000..e85902d84 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs @@ -0,0 +1,89 @@ +// +// LambdaExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [async] Parameters => Body + /// + public class LambdaExpression : Expression + { + public readonly static TokenRole AsyncModifierRole = new TokenRole ("async"); + public readonly static TokenRole ArrowRole = new TokenRole ("=>"); + public static readonly Role BodyRole = new Role("Body", AstNode.Null); + + bool isAsync; + + public bool IsAsync { + get { return isAsync; } + set { ThrowIfFrozen(); isAsync = value; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode ArrowToken { + get { return GetChildByRole (ArrowRole); } + } + + public AstNode Body { + get { return GetChildByRole (BodyRole); } + set { SetChildByRole (BodyRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLambdaExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLambdaExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLambdaExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LambdaExpression o = other as LambdaExpression; + return o != null && this.IsAsync == o.IsAsync && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/MemberReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/MemberReferenceExpression.cs new file mode 100644 index 000000000..334c6a260 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/MemberReferenceExpression.cs @@ -0,0 +1,119 @@ +// +// MemberReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target.MemberName + /// + public class MemberReferenceExpression : Expression + { + public Expression Target { + get { + return GetChildByRole(Roles.TargetExpression); + } + set { + SetChildByRole(Roles.TargetExpression, value); + } + } + + public CSharpTokenNode DotToken { + get { return GetChildByRole (Roles.Dot); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode LChevronToken { + get { return GetChildByRole (Roles.LChevron); } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public CSharpTokenNode RChevronToken { + get { return GetChildByRole (Roles.RChevron); } + } + + public MemberReferenceExpression () + { + } + + public MemberReferenceExpression (Expression target, string memberName, IEnumerable arguments = null) + { + AddChild (target, Roles.TargetExpression); + MemberName = memberName; + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.TypeArgument); + } + } + } + + public MemberReferenceExpression (Expression target, string memberName, params AstType[] arguments) : this (target, memberName, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMemberReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMemberReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMemberReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MemberReferenceExpression o = other as MemberReferenceExpression; + return o != null && this.Target.DoMatch(o.Target, match) && MatchString(this.MemberName, o.MemberName) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedArgumentExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedArgumentExpression.cs new file mode 100644 index 000000000..6b485f015 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedArgumentExpression.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2010-2013 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 +{ + /// + /// Represents a named argument passed to a method or attribute. + /// name: expression + /// + public class NamedArgumentExpression : Expression + { + public NamedArgumentExpression() + { + } + + public NamedArgumentExpression(string name, Expression expression) + { + this.Name = name; + this.Expression = expression; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNamedArgumentExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNamedArgumentExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamedArgumentExpression(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NamedArgumentExpression o = other as NamedArgumentExpression; + return o != null && MatchString(this.Name, o.Name) && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedExpression.cs new file mode 100644 index 000000000..92bc993b1 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedExpression.cs @@ -0,0 +1,97 @@ +// +// NamedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// name = expression + /// This isn't the same as 'assign' even though it has the same syntax. + /// This expression is used in object initializers and for named attribute arguments [Attr(FieldName = value)]. + /// + public class NamedExpression : Expression + { + public NamedExpression() + { + } + + public NamedExpression (string name, Expression expression) + { + this.Name = name; + this.Expression = expression; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNamedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNamedExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamedExpression(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as NamedExpression; + return o != null && MatchString(this.Name, o.Name) && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NullReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NullReferenceExpression.cs new file mode 100644 index 000000000..fbfeb6f91 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NullReferenceExpression.cs @@ -0,0 +1,83 @@ +// +// NullReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// null + /// + public class NullReferenceExpression : Expression + { + TextLocation location; + public override TextLocation StartLocation { + get { + return location; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.location = value; + } + + public override TextLocation EndLocation { + get { + return new TextLocation (location.Line, location.Column + "null".Length); + } + } + + public NullReferenceExpression () + { + } + + public NullReferenceExpression (TextLocation location) + { + this.location = location; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NullReferenceExpression o = other as NullReferenceExpression; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ObjectCreateExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ObjectCreateExpression.cs new file mode 100644 index 000000000..a9665f8c7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ObjectCreateExpression.cs @@ -0,0 +1,104 @@ +// +// ObjectCreateExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new Type(Arguments) { Initializer } + /// + public class ObjectCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + public readonly static Role InitializerRole = ArrayCreateExpression.InitializerRole; + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public ArrayInitializerExpression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public ObjectCreateExpression () + { + } + + public ObjectCreateExpression (AstType type, IEnumerable arguments = null) + { + AddChild (type, Roles.Type); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public ObjectCreateExpression (AstType type, params Expression[] arguments) : this (type, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitObjectCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitObjectCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitObjectCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ObjectCreateExpression o = other as ObjectCreateExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs new file mode 100644 index 000000000..7dddcb3fb --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs @@ -0,0 +1,98 @@ +// +// ParenthesizedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// ( Expression ) + /// + public class ParenthesizedExpression : Expression + { + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public ParenthesizedExpression() + { + } + + public ParenthesizedExpression(Expression expr) + { + Expression = expr; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitParenthesizedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitParenthesizedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitParenthesizedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ParenthesizedExpression o = other as ParenthesizedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + + /// + /// Gets whether the expression acts like a parenthesized expression, + /// i.e. whether information about the expected type (for lambda type inference) flows + /// into the inner expression. + /// + /// Returns true for ParenthesizedExpression, CheckedExpression or UncheckedExpression; false otherwise. + public static bool ActsAsParenthesizedExpression(AstNode expression) + { + return expression is ParenthesizedExpression || expression is CheckedExpression || expression is UncheckedExpression; + } + + /// + /// Unpacks the given expression if it is a ParenthesizedExpression, CheckedExpression or UncheckedExpression. + /// + public static Expression UnpackParenthesizedExpression(Expression expr) + { + while (ActsAsParenthesizedExpression(expr)) + expr = expr.GetChildByRole(Roles.Expression); + return expr; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs new file mode 100644 index 000000000..35c4a7203 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs @@ -0,0 +1,90 @@ +// +// PointerReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target->MemberName + /// + public class PointerReferenceExpression : Expression + { + public readonly static TokenRole ArrowRole = new TokenRole ("->"); + + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode ArrowToken { + get { return GetChildByRole (ArrowRole); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPointerReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPointerReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPointerReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PointerReferenceExpression o = other as PointerReferenceExpression; + return o != null && MatchString(this.MemberName, o.MemberName) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs new file mode 100644 index 000000000..adfe7c3ba --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs @@ -0,0 +1,162 @@ +// +// PrimitiveExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 +{ + /// + /// Represents a literal value. + /// + public class PrimitiveExpression : Expression + { + public static readonly object AnyValue = new object(); + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.startLocation = value; + this.endLocation = null; + } + + string literalValue; + TextLocation? endLocation; + public override TextLocation EndLocation { + get { + if (!endLocation.HasValue) { + endLocation = value is string ? AdvanceLocation (StartLocation, literalValue ?? "") : + new TextLocation (StartLocation.Line, StartLocation.Column + (literalValue ?? "").Length); + } + return endLocation.Value; + } + } + + object value; + + public object Value { + get { return this.value; } + set { + ThrowIfFrozen(); + this.value = value; + literalValue = null; + } + } + + /// Never returns null. + public string LiteralValue { + get { return literalValue ?? ""; } + } + + /// Can be null. + public string UnsafeLiteralValue { + get { return literalValue; } + } + + public void SetValue(object value, string literalValue) + { + if (value == null) + throw new ArgumentNullException(); + ThrowIfFrozen(); + this.value = value; + this.literalValue = literalValue; + } + + public PrimitiveExpression (object value) + { + this.Value = value; + this.literalValue = null; + } + + public PrimitiveExpression (object value, string literalValue) + { + this.Value = value; + this.literalValue = literalValue; + } + + public PrimitiveExpression (object value, TextLocation startLocation, string literalValue) + { + this.Value = value; + this.startLocation = startLocation; + this.literalValue = literalValue; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPrimitiveExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPrimitiveExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPrimitiveExpression (this, data); + } + + unsafe static TextLocation AdvanceLocation(TextLocation startLocation, string str) + { + int line = startLocation.Line; + int col = startLocation.Column; + fixed (char* start = str) { + char* p = start; + char* endPtr = start + str.Length; + while (p < endPtr) { + var nl = NewLine.GetDelimiterLength(*p, () => { + char* nextp = p + 1; + if (nextp < endPtr) + return *nextp; + return '\0'; + }); + if (nl > 0) { + line++; + col = 1; + if (nl == 2) + p++; + } else { + col++; + } + p++; + } + } + return new TextLocation (line, col); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PrimitiveExpression o = other as PrimitiveExpression; + return o != null && (this.Value == AnyValue || object.Equals(this.Value, o.Value)); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/QueryExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/QueryExpression.cs new file mode 100644 index 000000000..b52f50a47 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/QueryExpression.cs @@ -0,0 +1,655 @@ +// Copyright (c) 2010-2013 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; +namespace ICSharpCode.NRefactory.CSharp +{ + public class QueryExpression : Expression + { + public static readonly Role ClauseRole = new Role("Clause"); + + #region Null + public new static readonly QueryExpression Null = new NullQueryExpression (); + + sealed class NullQueryExpression : QueryExpression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + public AstNodeCollection Clauses { + get { return GetChildrenByRole(ClauseRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitQueryExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryExpression o = other as QueryExpression; + return o != null && !o.IsNull && this.Clauses.DoMatch(o.Clauses, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public abstract class QueryClause : AstNode + { + public override NodeType NodeType { + get { return NodeType.QueryClause; } + } + } + + /// + /// Represents a query continuation. + /// "(from .. select ..) into Identifier" or "(from .. group .. by ..) into Identifier" + /// Note that "join .. into .." is not a query continuation! + /// + /// This is always the first(!!) clause in a query expression. + /// The tree for "from a in b select c into d select e" looks like this: + /// new QueryExpression { + /// new QueryContinuationClause { + /// PrecedingQuery = new QueryExpression { + /// new QueryFromClause(a in b), + /// new QuerySelectClause(c) + /// }, + /// Identifier = d + /// }, + /// new QuerySelectClause(e) + /// } + /// + public class QueryContinuationClause : QueryClause + { + public static readonly Role PrecedingQueryRole = new Role("PrecedingQuery", QueryExpression.Null); + public static readonly TokenRole IntoKeywordRole = new TokenRole ("into"); + + public QueryExpression PrecedingQuery { + get { return GetChildByRole(PrecedingQueryRole); } + set { SetChildByRole(PrecedingQueryRole, value); } + } + + public CSharpTokenNode IntoKeyword { + get { return GetChildByRole (IntoKeywordRole); } + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole (Roles.Identifier); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryContinuationClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryContinuationClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryContinuationClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryContinuationClause o = other as QueryContinuationClause; + return o != null && MatchString(this.Identifier, o.Identifier) && this.PrecedingQuery.DoMatch(o.PrecedingQuery, match); + } + } + + public class QueryFromClause : QueryClause + { + public static readonly TokenRole FromKeywordRole = new TokenRole ("from"); + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + + public CSharpTokenNode FromKeyword { + get { return GetChildByRole (FromKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole(Roles.Identifier); } + } + + public CSharpTokenNode InKeyword { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryFromClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryFromClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryFromClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryFromClause o = other as QueryFromClause; + return o != null && this.Type.DoMatch(o.Type, match) && MatchString(this.Identifier, o.Identifier) + && this.Expression.DoMatch(o.Expression, match); + } + } + + public class QueryLetClause : QueryClause + { + public readonly static TokenRole LetKeywordRole = new TokenRole ("let"); + + public CSharpTokenNode LetKeyword { + get { return GetChildByRole(LetKeywordRole); } + } + + public string Identifier { + get { + return GetChildByRole(Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole(Roles.Identifier); } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole(Roles.Assign); } + } + + public Expression Expression { + get { return GetChildByRole(Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryLetClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryLetClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryLetClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryLetClause o = other as QueryLetClause; + return o != null && MatchString(this.Identifier, o.Identifier) && this.Expression.DoMatch(o.Expression, match); + } + } + + + public class QueryWhereClause : QueryClause + { + public readonly static TokenRole WhereKeywordRole = new TokenRole ("where"); + + public CSharpTokenNode WhereKeyword { + get { return GetChildByRole (WhereKeywordRole); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryWhereClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryWhereClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryWhereClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryWhereClause o = other as QueryWhereClause; + return o != null && this.Condition.DoMatch(o.Condition, match); + } + } + + /// + /// Represents a join or group join clause. + /// + public class QueryJoinClause : QueryClause + { + public static readonly TokenRole JoinKeywordRole = new TokenRole ("join"); + public static readonly Role TypeRole = Roles.Type; + public static readonly Role JoinIdentifierRole = Roles.Identifier; + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + public static readonly Role InExpressionRole = Roles.Expression; + public static readonly TokenRole OnKeywordRole = new TokenRole ("on"); + public static readonly Role OnExpressionRole = new Role("OnExpression", Expression.Null); + public static readonly TokenRole EqualsKeywordRole = new TokenRole ("equals"); + public static readonly Role EqualsExpressionRole = new Role("EqualsExpression", Expression.Null); + public static readonly TokenRole IntoKeywordRole = new TokenRole ("into"); + public static readonly Role IntoIdentifierRole = new Role("IntoIdentifier", Identifier.Null); + + public bool IsGroupJoin { + get { return !string.IsNullOrEmpty(this.IntoIdentifier); } + } + + public CSharpTokenNode JoinKeyword { + get { return GetChildByRole (JoinKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (TypeRole); } + set { SetChildByRole (TypeRole, value); } + } + + public string JoinIdentifier { + get { + return GetChildByRole(JoinIdentifierRole).Name; + } + set { + SetChildByRole(JoinIdentifierRole, Identifier.Create (value)); + } + } + + public Identifier JoinIdentifierToken { + get { return GetChildByRole(JoinIdentifierRole); } + } + + public CSharpTokenNode InKeyword { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression InExpression { + get { return GetChildByRole (InExpressionRole); } + set { SetChildByRole (InExpressionRole, value); } + } + + public CSharpTokenNode OnKeyword { + get { return GetChildByRole (OnKeywordRole); } + } + + public Expression OnExpression { + get { return GetChildByRole (OnExpressionRole); } + set { SetChildByRole (OnExpressionRole, value); } + } + + public CSharpTokenNode EqualsKeyword { + get { return GetChildByRole (EqualsKeywordRole); } + } + + public Expression EqualsExpression { + get { return GetChildByRole (EqualsExpressionRole); } + set { SetChildByRole (EqualsExpressionRole, value); } + } + + public CSharpTokenNode IntoKeyword { + get { return GetChildByRole (IntoKeywordRole); } + } + + public string IntoIdentifier { + get { + return GetChildByRole (IntoIdentifierRole).Name; + } + set { + SetChildByRole(IntoIdentifierRole, Identifier.Create (value)); + } + } + + public Identifier IntoIdentifierToken { + get { return GetChildByRole(IntoIdentifierRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryJoinClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryJoinClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryJoinClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryJoinClause o = other as QueryJoinClause; + return o != null && this.IsGroupJoin == o.IsGroupJoin + && this.Type.DoMatch(o.Type, match) && MatchString(this.JoinIdentifier, o.JoinIdentifier) + && this.InExpression.DoMatch(o.InExpression, match) && this.OnExpression.DoMatch(o.OnExpression, match) + && this.EqualsExpression.DoMatch(o.EqualsExpression, match) + && MatchString(this.IntoIdentifier, o.IntoIdentifier); + } + } + + public class QueryOrderClause : QueryClause + { + public static readonly TokenRole OrderbyKeywordRole = new TokenRole ("orderby"); + public static readonly Role OrderingRole = new Role("Ordering"); + + public CSharpTokenNode OrderbyToken { + get { return GetChildByRole (OrderbyKeywordRole); } + } + + public AstNodeCollection Orderings { + get { return GetChildrenByRole (OrderingRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryOrderClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryOrderClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryOrderClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryOrderClause o = other as QueryOrderClause; + return o != null && this.Orderings.DoMatch(o.Orderings, match); + } + } + + public class QueryOrdering : AstNode + { + public readonly static TokenRole AscendingKeywordRole = new TokenRole ("ascending"); + public readonly static TokenRole DescendingKeywordRole = new TokenRole ("descending"); + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public QueryOrderingDirection Direction { + get; + set; + } + + public CSharpTokenNode DirectionToken { + get { return Direction == QueryOrderingDirection.Ascending ? GetChildByRole (AscendingKeywordRole) : GetChildByRole (DescendingKeywordRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryOrdering (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryOrdering (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryOrdering (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryOrdering o = other as QueryOrdering; + return o != null && this.Direction == o.Direction && this.Expression.DoMatch(o.Expression, match); + } + } + + public enum QueryOrderingDirection + { + None, + Ascending, + Descending + } + + public class QuerySelectClause : QueryClause + { + public readonly static TokenRole SelectKeywordRole = new TokenRole ("select"); + + public CSharpTokenNode SelectKeyword { + get { return GetChildByRole (SelectKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQuerySelectClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQuerySelectClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQuerySelectClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QuerySelectClause o = other as QuerySelectClause; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } + + public class QueryGroupClause : QueryClause + { + public static readonly TokenRole GroupKeywordRole = new TokenRole ("group"); + public static readonly Role ProjectionRole = new Role("Projection", Expression.Null); + public static readonly TokenRole ByKeywordRole = new TokenRole ("by"); + public static readonly Role KeyRole = new Role("Key", Expression.Null); + + public CSharpTokenNode GroupKeyword { + get { return GetChildByRole (GroupKeywordRole); } + } + + public Expression Projection { + get { return GetChildByRole (ProjectionRole); } + set { SetChildByRole (ProjectionRole, value); } + } + + public CSharpTokenNode ByKeyword { + get { return GetChildByRole (ByKeywordRole); } + } + + public Expression Key { + get { return GetChildByRole (KeyRole); } + set { SetChildByRole (KeyRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryGroupClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryGroupClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryGroupClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryGroupClause o = other as QueryGroupClause; + return o != null && this.Projection.DoMatch(o.Projection, match) && this.Key.DoMatch(o.Key, match); + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/SizeOfExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/SizeOfExpression.cs new file mode 100644 index 000000000..8a794960c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/SizeOfExpression.cs @@ -0,0 +1,83 @@ +// +// SizeOfExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// sizeof(Type) + /// + public class SizeOfExpression : Expression + { + public readonly static TokenRole SizeofKeywordRole = new TokenRole ("sizeof"); + + public CSharpTokenNode SizeOfToken { + get { return GetChildByRole (SizeofKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public SizeOfExpression () + { + } + + public SizeOfExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSizeOfExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSizeOfExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSizeOfExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SizeOfExpression o = other as SizeOfExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/StackAllocExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/StackAllocExpression.cs new file mode 100644 index 000000000..ad9f58c1b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/StackAllocExpression.cs @@ -0,0 +1,79 @@ +// +// StackAllocExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// stackalloc Type[Count] + /// + public class StackAllocExpression : Expression + { + public readonly static TokenRole StackallocKeywordRole = new TokenRole ("stackalloc"); + + public CSharpTokenNode StackAllocToken { + get { return GetChildByRole (StackallocKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public Expression CountExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitStackAllocExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitStackAllocExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitStackAllocExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + StackAllocExpression o = other as StackAllocExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.CountExpression.DoMatch(o.CountExpression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ThisReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ThisReferenceExpression.cs new file mode 100644 index 000000000..481ef6658 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ThisReferenceExpression.cs @@ -0,0 +1,71 @@ +// +// ThisReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// this + /// + public class ThisReferenceExpression : Expression + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + "this".Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitThisReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitThisReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitThisReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ThisReferenceExpression o = other as ThisReferenceExpression; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeOfExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeOfExpression.cs new file mode 100644 index 000000000..fd2e93ab8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeOfExpression.cs @@ -0,0 +1,84 @@ +// +// TypeOfExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// typeof(Type) + /// + public class TypeOfExpression : Expression + { + public readonly static TokenRole TypeofKeywordRole = new TokenRole ("typeof"); + + public CSharpTokenNode TypeOfToken { + get { return GetChildByRole (TypeofKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public TypeOfExpression () + { + } + + public TypeOfExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeOfExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeOfExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTypeOfExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeOfExpression o = other as TypeOfExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeReferenceExpression.cs new file mode 100644 index 000000000..84b2d60dc --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeReferenceExpression.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2010-2013 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 +{ + /// + /// Represents an AstType as an expression. + /// This is used when calling a method on a primitive type: "int.Parse()" + /// + public class TypeReferenceExpression : Expression + { + public AstType Type { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeReferenceExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitTypeReferenceExpression(this, data); + } + + public TypeReferenceExpression () + { + } + + public TypeReferenceExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeReferenceExpression o = other as TypeReferenceExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UnaryOperatorExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UnaryOperatorExpression.cs new file mode 100644 index 000000000..878d6132f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UnaryOperatorExpression.cs @@ -0,0 +1,181 @@ +// +// UnaryOperatorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq.Expressions; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Operator Expression + /// + public class UnaryOperatorExpression : Expression + { + public readonly static TokenRole NotRole = new TokenRole ("!"); + public readonly static TokenRole BitNotRole = new TokenRole ("~"); + public readonly static TokenRole MinusRole = new TokenRole ("-"); + public readonly static TokenRole PlusRole = new TokenRole ("+"); + public readonly static TokenRole IncrementRole = new TokenRole ("++"); + public readonly static TokenRole DecrementRole = new TokenRole ("--"); + public readonly static TokenRole DereferenceRole = new TokenRole ("*"); + public readonly static TokenRole AddressOfRole = new TokenRole ("&"); + public readonly static TokenRole AwaitRole = new TokenRole ("await"); + + public UnaryOperatorExpression() + { + } + + public UnaryOperatorExpression(UnaryOperatorType op, Expression expression) + { + this.Operator = op; + this.Expression = expression; + } + + public UnaryOperatorType Operator { + get; + set; + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole (Operator)); } + } + + static Expression NoUnaryExpressionError = new ErrorExpression ("No unary expression"); + public Expression Expression { + get { return GetChildByRole (Roles.Expression) ?? NoUnaryExpressionError; } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUnaryOperatorExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUnaryOperatorExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUnaryOperatorExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UnaryOperatorExpression o = other as UnaryOperatorExpression; + return o != null && (this.Operator == UnaryOperatorType.Any || this.Operator == o.Operator) + && this.Expression.DoMatch(o.Expression, match); + } + + public static TokenRole GetOperatorRole(UnaryOperatorType op) + { + switch (op) { + case UnaryOperatorType.Not: + return NotRole; + case UnaryOperatorType.BitNot: + return BitNotRole; + case UnaryOperatorType.Minus: + return MinusRole; + case UnaryOperatorType.Plus: + return PlusRole; + case UnaryOperatorType.Increment: + case UnaryOperatorType.PostIncrement: + return IncrementRole; + case UnaryOperatorType.PostDecrement: + case UnaryOperatorType.Decrement: + return DecrementRole; + case UnaryOperatorType.Dereference: + return DereferenceRole; + case UnaryOperatorType.AddressOf: + return AddressOfRole; + case UnaryOperatorType.Await: + return AwaitRole; + default: + throw new NotSupportedException("Invalid value for UnaryOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(UnaryOperatorType op, bool checkForOverflow) + { + switch (op) { + case UnaryOperatorType.Not: + return ExpressionType.Not; + case UnaryOperatorType.BitNot: + return ExpressionType.OnesComplement; + case UnaryOperatorType.Minus: + return checkForOverflow ? ExpressionType.NegateChecked : ExpressionType.Negate; + case UnaryOperatorType.Plus: + return ExpressionType.UnaryPlus; + case UnaryOperatorType.Increment: + return ExpressionType.PreIncrementAssign; + case UnaryOperatorType.Decrement: + return ExpressionType.PreDecrementAssign; + case UnaryOperatorType.PostIncrement: + return ExpressionType.PostIncrementAssign; + case UnaryOperatorType.PostDecrement: + return ExpressionType.PostDecrementAssign; + case UnaryOperatorType.Dereference: + case UnaryOperatorType.AddressOf: + case UnaryOperatorType.Await: + return ExpressionType.Extension; + default: + throw new NotSupportedException("Invalid value for UnaryOperatorType"); + } + } + } + + public enum UnaryOperatorType + { + /// + /// Any unary operator (used in pattern matching) + /// + Any, + + /// Logical not (!a) + Not, + /// Bitwise not (~a) + BitNot, + /// Unary minus (-a) + Minus, + /// Unary plus (+a) + Plus, + /// Pre increment (++a) + Increment, + /// Pre decrement (--a) + Decrement, + /// Post increment (a++) + PostIncrement, + /// Post decrement (a--) + PostDecrement, + /// Dereferencing (*a) + Dereference, + /// Get address (&a) + AddressOf, + /// C# 5.0 await + Await + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UncheckedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UncheckedExpression.cs new file mode 100644 index 000000000..5b8686a26 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UncheckedExpression.cs @@ -0,0 +1,83 @@ +// +// UncheckedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unchecked(Expression) + /// + public class UncheckedExpression : Expression + { + public readonly static TokenRole UncheckedKeywordRole = new TokenRole ("unchecked"); + + public CSharpTokenNode UncheckedToken { + get { return GetChildByRole (UncheckedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public UncheckedExpression () + { + } + + public UncheckedExpression (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUncheckedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUncheckedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUncheckedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UncheckedExpression o = other as UncheckedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UndocumentedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UndocumentedExpression.cs new file mode 100644 index 000000000..0efc0d70f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UndocumentedExpression.cs @@ -0,0 +1,105 @@ +// +// UndocumentedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum UndocumentedExpressionType + { + ArgListAccess, // __arglist + ArgList, // __arglist (a1, a2, ..., an) + RefValue, // __refvalue (expr , type) + RefType, // __reftype (expr) + MakeRef // __makeref (expr) + } + + /// + /// Represents undocumented expressions. + /// + public class UndocumentedExpression : Expression + { + public readonly static TokenRole ArglistKeywordRole = new TokenRole ("__arglist"); + public readonly static TokenRole RefvalueKeywordRole = new TokenRole ("__refvalue"); + public readonly static TokenRole ReftypeKeywordRole = new TokenRole ("__reftype"); + public readonly static TokenRole MakerefKeywordRole = new TokenRole ("__makeref"); + + public UndocumentedExpressionType UndocumentedExpressionType { + get; set; + } + + public CSharpTokenNode UndocumentedToken { + get { + switch (UndocumentedExpressionType) { + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.ArgListAccess: + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.ArgList: + return GetChildByRole (ArglistKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.RefValue: + return GetChildByRole (RefvalueKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.RefType: + return GetChildByRole (ReftypeKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.MakeRef: + return GetChildByRole (MakerefKeywordRole); + } + return CSharpTokenNode.Null; + } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUndocumentedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUndocumentedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUndocumentedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UndocumentedExpression o = other as UndocumentedExpression; + return o != null && this.UndocumentedExpressionType == o.UndocumentedExpressionType && this.Arguments.DoMatch(o.Arguments, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Attribute.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Attribute.cs new file mode 100644 index 000000000..cc99936e5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Attribute.cs @@ -0,0 +1,93 @@ +// +// Attribute.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Attribute(Arguments) + /// + public class Attribute : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return base.GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + // HasArgumentList == false: [Empty] + public bool HasArgumentList { + get; + set; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAttribute (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAttribute (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAttribute (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + Attribute o = other as Attribute; + return o != null && this.Type.DoMatch (o.Type, match) && this.Arguments.DoMatch (o.Arguments, match); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + if (IsNull) + return "Null"; + return base.ToString(formattingOptions); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/AttributeSection.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/AttributeSection.cs new file mode 100644 index 000000000..67b04412b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/AttributeSection.cs @@ -0,0 +1,174 @@ +// +// AttributeSection.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [AttributeTarget: Attributes] + /// + public class AttributeSection : AstNode + { + #region PatternPlaceholder + public static implicit operator AttributeSection(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : AttributeSection, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public string AttributeTarget { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier AttributeTargetToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection Attributes { + get { return base.GetChildrenByRole (Roles.Attribute); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAttributeSection (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAttributeSection (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAttributeSection (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AttributeSection o = other as AttributeSection; + return o != null && MatchString(this.AttributeTarget, o.AttributeTarget) && this.Attributes.DoMatch(o.Attributes, match); + } + + public AttributeSection() + { + } + + public AttributeSection(Attribute attr) + { + this.Attributes.Add(attr); + } + +// public static string GetAttributeTargetName(AttributeTarget attributeTarget) +// { +// switch (attributeTarget) { +// case AttributeTarget.None: +// return null; +// case AttributeTarget.Assembly: +// return "assembly"; +// case AttributeTarget.Module: +// return "module"; +// case AttributeTarget.Type: +// return "type"; +// case AttributeTarget.Param: +// return "param"; +// case AttributeTarget.Field: +// return "field"; +// case AttributeTarget.Return: +// return "return"; +// case AttributeTarget.Method: +// return "method"; +// default: +// throw new NotSupportedException("Invalid value for AttributeTarget"); +// } +// } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs new file mode 100644 index 000000000..9d53f6e66 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs @@ -0,0 +1,140 @@ +// +// Comment.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum CommentType + { + /// + /// "//" comment + /// + SingleLine, + /// + /// "/* */" comment + /// + MultiLine, + /// + /// "///" comment + /// + Documentation, + /// + /// Inactive code (code in non-taken "#if") + /// + InactiveCode, + /// + /// "/** */" comment + /// + MultiLineDocumentation + } + + public class Comment : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + CommentType commentType; + + public CommentType CommentType { + get { return commentType; } + set { ThrowIfFrozen(); commentType = value; } + } + + /// + /// Returns true if the is Documentation or MultiLineDocumentation. + /// + public bool IsDocumentation { + get { + return commentType == CommentType.Documentation || commentType == CommentType.MultiLineDocumentation; + } + } + + bool startsLine; + + public bool StartsLine { + get { return startsLine; } + set { ThrowIfFrozen(); startsLine = value; } + } + + string content; + + public string Content { + get { return content; } + set { ThrowIfFrozen(); content = value; } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public Comment (string content, CommentType type = CommentType.SingleLine) + { + this.CommentType = type; + this.Content = content; + } + + public Comment (CommentType commentType, TextLocation startLocation, TextLocation endLocation) + { + this.CommentType = commentType; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitComment (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitComment (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitComment (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Comment o = other as Comment; + return o != null && this.CommentType == o.CommentType && MatchString(this.Content, o.Content); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Constraint.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Constraint.cs new file mode 100644 index 000000000..c49930427 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Constraint.cs @@ -0,0 +1,85 @@ +// +// Constraint.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// where TypeParameter : BaseTypes + /// + /// + /// new(), struct and class constraints are represented using a PrimitiveType "new", "struct" or "class" + /// + public class Constraint : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode WhereKeyword { + get { return GetChildByRole (Roles.WhereKeyword); } + } + + public SimpleType TypeParameter { + get { + return GetChildByRole (Roles.ConstraintTypeParameter); + } + set { + SetChildByRole(Roles.ConstraintTypeParameter, value); + } + } + + public AstNodeCollection BaseTypes { + get { + return GetChildrenByRole(Roles.BaseType); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstraint (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstraint (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstraint (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Constraint o = other as Constraint; + return o != null && this.TypeParameter.DoMatch (o.TypeParameter, match) && this.BaseTypes.DoMatch(o.BaseTypes, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/DelegateDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/DelegateDeclaration.cs new file mode 100644 index 000000000..68489bc7f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/DelegateDeclaration.cs @@ -0,0 +1,92 @@ +// +// DelegateDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// delegate ReturnType Name<TypeParameters>(Parameters) where Constraints; + /// + public class DelegateDeclaration : EntityDeclaration + { + public override NodeType NodeType { + get { return NodeType.TypeDeclaration; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.TypeDefinition; } + } + + public CSharpTokenNode DelegateToken { + get { return GetChildByRole(Roles.DelegateKeyword); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole (Roles.Constraint); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDelegateDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDelegateDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDelegateDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DelegateDeclaration o = other as DelegateDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.TypeParameters.DoMatch(o.TypeParameters, match) && this.Parameters.DoMatch(o.Parameters, match) + && this.Constraints.DoMatch(o.Constraints, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs new file mode 100644 index 000000000..9404d417f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs @@ -0,0 +1,91 @@ +// +// ExternAliasDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// extern alias ; + /// + public class ExternAliasDeclaration : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode ExternToken { + get { return GetChildByRole (Roles.ExternKeyword); } + } + + public CSharpTokenNode AliasToken { + get { return GetChildByRole (Roles.AliasKeyword); } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitExternAliasDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitExternAliasDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitExternAliasDeclaration (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as ExternAliasDeclaration; + return o != null && MatchString (this.Name, o.Name); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NamespaceDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NamespaceDeclaration.cs new file mode 100644 index 000000000..dbcf0192d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NamespaceDeclaration.cs @@ -0,0 +1,158 @@ +// +// NamespaceDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// namespace Name { Members } + /// + public class NamespaceDeclaration : AstNode + { + public static readonly Role MemberRole = SyntaxTree.MemberRole; + public static readonly Role NamespaceNameRole = new Role("NamespaceName", AstType.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode NamespaceToken { + get { return GetChildByRole(Roles.NamespaceKeyword); } + } + + public AstType NamespaceName { + get { return GetChildByRole(NamespaceNameRole) ?? AstType.Null; } + set { SetChildByRole(NamespaceNameRole, value); } + } + + public string Name { + get { + return UsingDeclaration.ConstructNamespace(NamespaceName); + } + set { + var arr = value.Split('.'); + NamespaceName = ConstructType(arr, arr.Length - 1); + } + } + + static AstType ConstructType(string[] arr, int i) + { + if (i < 0 || i >= arr.Length) + throw new ArgumentOutOfRangeException("i"); + if (i == 0) + return new SimpleType(arr[i]); + return new MemberType(ConstructType(arr, i - 1), arr[i]); + } + + /// + /// Gets the full namespace name (including any parent namespaces) + /// + public string FullName { + get { + NamespaceDeclaration parentNamespace = Parent as NamespaceDeclaration; + if (parentNamespace != null) + return BuildQualifiedName(parentNamespace.FullName, Name); + return Name; + } + } + + public IEnumerable Identifiers { + get { + var result = new Stack(); + AstType type = NamespaceName; + while (type is MemberType) { + var mt = (MemberType)type; + result.Push(mt.MemberName); + type = mt.Target; + } + if (type is SimpleType) + result.Push(((SimpleType)type).Identifier); + return result; + } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole(Roles.LBrace); } + } + + public AstNodeCollection Members { + get { return GetChildrenByRole(MemberRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole(Roles.RBrace); } + } + + public NamespaceDeclaration() + { + } + + public NamespaceDeclaration(string name) + { + this.Name = name; + } + + public static string BuildQualifiedName(string name1, string name2) + { + if (string.IsNullOrEmpty(name1)) + return name2; + if (string.IsNullOrEmpty(name2)) + return name1; + return name1 + "." + name2; + } + + public void AddMember(AstNode child) + { + AddChild(child, MemberRole); + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitNamespaceDeclaration(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitNamespaceDeclaration(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamespaceDeclaration(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NamespaceDeclaration o = other as NamespaceDeclaration; + return o != null && MatchString(this.Name, o.Name) && this.Members.DoMatch(o.Members, match); + } + } +} ; diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NewLineNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NewLineNode.cs new file mode 100644 index 000000000..12b004420 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NewLineNode.cs @@ -0,0 +1,91 @@ +using System; +namespace ICSharpCode.NRefactory.CSharp +{ + + /// + /// A New line node represents a line break in the text. + /// + public sealed class NewLineNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + const uint newLineMask = 0xfu << AstNodeFlagsUsedBits; + static readonly UnicodeNewline[] newLineTypes = { + UnicodeNewline.Unknown, + UnicodeNewline.LF, + UnicodeNewline.CRLF, + UnicodeNewline.CR, + UnicodeNewline.NEL, + UnicodeNewline.VT, + UnicodeNewline.FF, + UnicodeNewline.LS, + UnicodeNewline.PS + }; + + public UnicodeNewline NewLineType { + get { + return newLineTypes[(flags & newLineMask) >> AstNodeFlagsUsedBits]; + } + set { + ThrowIfFrozen(); + int pos = Array.IndexOf(newLineTypes, value); + if (pos < 0) + pos = 0; + flags &= ~newLineMask; // clear old newline type + flags |= (uint)pos << AstNodeFlagsUsedBits; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (startLocation.Line + 1, 1); + } + } + + public NewLineNode() : this (TextLocation.Empty) + { + } + + public NewLineNode(TextLocation startLocation) + { + this.startLocation = startLocation; + } + + public sealed override string ToString(CSharpFormattingOptions formattingOptions) + { + return NewLine.GetString (NewLineType); + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitNewLine (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitNewLine (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNewLine (this, data); + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + return other is NewLineNode; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs new file mode 100644 index 000000000..631f35e98 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs @@ -0,0 +1,205 @@ +// +// PreProcessorDirective.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum PreProcessorDirectiveType : byte + { + Invalid = 0, + Region = 1, + Endregion = 2, + + If = 3, + Endif = 4, + Elif = 5, + Else = 6, + + Define = 7, + Undef = 8, + Error = 9, + Warning = 10, + Pragma = 11, + Line = 12 + } + + public class LinePreprocessorDirective : PreProcessorDirective + { + public int LineNumber { + get; + set; + } + + public string FileName { + get; + set; + } + + public LinePreprocessorDirective(TextLocation startLocation, TextLocation endLocation) : base (PreProcessorDirectiveType.Line, startLocation, endLocation) + { + } + + public LinePreprocessorDirective(string argument = null) : base (PreProcessorDirectiveType.Line, argument) + { + } + } + + public class PragmaWarningPreprocessorDirective : PreProcessorDirective + { + public static readonly Role WarningRole = new Role ("Warning"); + + public static readonly TokenRole PragmaKeywordRole = new TokenRole ("#pragma"); + public static readonly TokenRole WarningKeywordRole = new TokenRole ("warning"); + public static readonly TokenRole DisableKeywordRole = new TokenRole ("disable"); + public static readonly TokenRole RestoreKeywordRole = new TokenRole ("restore"); + + public bool Disable { + get { + return !DisableToken.IsNull; + } + } + + public CSharpTokenNode PragmaToken { + get { return GetChildByRole (PragmaKeywordRole); } + } + + public CSharpTokenNode WarningToken { + get { return GetChildByRole (WarningKeywordRole); } + } + + public CSharpTokenNode DisableToken { + get { return GetChildByRole (DisableKeywordRole); } + } + + public CSharpTokenNode RestoreToken { + get { return GetChildByRole (RestoreKeywordRole); } + } + + public AstNodeCollection Warnings { + get { return GetChildrenByRole(WarningRole); } + } + + public override TextLocation EndLocation { + get { + var child = LastChild; + if (child == null) + return base.EndLocation; + return child.EndLocation; + } + } + + public PragmaWarningPreprocessorDirective(TextLocation startLocation, TextLocation endLocation) : base (PreProcessorDirectiveType.Pragma, startLocation, endLocation) + { + } + + public PragmaWarningPreprocessorDirective(string argument = null) : base (PreProcessorDirectiveType.Pragma, argument) + { + } + + public bool IsDefined(int pragmaWarning) + { + return Warnings.Select(w => (int)w.Value).Any(n => n == pragmaWarning); + } + } + + public class PreProcessorDirective : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public PreProcessorDirectiveType Type { + get; + set; + } + + public string Argument { + get; + set; + } + + /// + /// For an '#if' directive, specifies whether the condition evaluated to true. + /// + public bool Take { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public PreProcessorDirective(PreProcessorDirectiveType type, TextLocation startLocation, TextLocation endLocation) + { + this.Type = type; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public PreProcessorDirective(PreProcessorDirectiveType type, string argument = null) + { + this.Type = type; + this.Argument = argument; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPreProcessorDirective (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPreProcessorDirective (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPreProcessorDirective (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PreProcessorDirective o = other as PreProcessorDirective; + return o != null && Type == o.Type && MatchString(Argument, o.Argument); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TextNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TextNode.cs new file mode 100644 index 000000000..4c7f9b942 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TextNode.cs @@ -0,0 +1,94 @@ +// +// TextNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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 +{ + /// + /// A text node contains text without syntactic or semantic information. + /// (non parseable part of a text) + /// + public class TextNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public string Text { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public TextNode(string text) : this (text, TextLocation.Empty, TextLocation.Empty) + { + } + + public TextNode(string text, TextLocation startLocation, TextLocation endLocation) + { + this.Text = text; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitText (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitText (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitText (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as TextNode; + return o != null && o.Text == Text; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs new file mode 100644 index 000000000..f2dedfa16 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs @@ -0,0 +1,145 @@ +// +// TypeDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum ClassType + { + Class, + Struct, + Interface, + Enum + } + + /// + /// class Name<TypeParameters> : BaseTypes where Constraints; + /// + public class TypeDeclaration : EntityDeclaration + { + public override NodeType NodeType { + get { return NodeType.TypeDeclaration; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.TypeDefinition; } + } + + ClassType classType; + + public CSharpTokenNode TypeKeyword { + get { + switch (classType) { + case ClassType.Class: + return GetChildByRole(Roles.ClassKeyword); + case ClassType.Struct: + return GetChildByRole(Roles.StructKeyword); + case ClassType.Interface: + return GetChildByRole(Roles.InterfaceKeyword); + case ClassType.Enum: + return GetChildByRole(Roles.EnumKeyword); + default: + return CSharpTokenNode.Null; + } + } + } + + public ClassType ClassType { + get { return classType; } + set { + ThrowIfFrozen(); + classType = value; + } + } + + public CSharpTokenNode LChevronToken { + get { return GetChildByRole (Roles.LChevron); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode RChevronToken { + get { return GetChildByRole (Roles.RChevron); } + } + + + + public CSharpTokenNode ColonToken { + get { + return GetChildByRole(Roles.Colon); + } + } + + public AstNodeCollection BaseTypes { + get { return GetChildrenByRole(Roles.BaseType); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole(Roles.Constraint); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Members { + get { return GetChildrenByRole (Roles.TypeMemberRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTypeDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeDeclaration o = other as TypeDeclaration; + return o != null && this.ClassType == o.ClassType && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.TypeParameters.DoMatch(o.TypeParameters, match) + && this.BaseTypes.DoMatch(o.BaseTypes, match) && this.Constraints.DoMatch(o.Constraints, match) + && this.Members.DoMatch(o.Members, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs new file mode 100644 index 000000000..c992b629a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs @@ -0,0 +1,113 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [in|out] Name + /// + /// Represents a type parameter. + /// Note: mirroring the C# syntax, constraints are not part of the type parameter declaration, but belong + /// to the parent type or method. + /// + public class TypeParameterDeclaration : AstNode + { + public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; + public static readonly TokenRole OutVarianceKeywordRole = new TokenRole ("out"); + public static readonly TokenRole InVarianceKeywordRole = new TokenRole ("in"); + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public AstNodeCollection Attributes { + get { return GetChildrenByRole (AttributeRole); } + } + + VarianceModifier variance; + + public VarianceModifier Variance { + get { return variance; } + set { ThrowIfFrozen(); variance = value; } + } + + public CSharpTokenNode VarianceToken { + get { + switch (Variance) { + case VarianceModifier.Covariant: + return GetChildByRole(OutVarianceKeywordRole); + case VarianceModifier.Contravariant: + return GetChildByRole(InVarianceKeywordRole); + default: + return CSharpTokenNode.Null; + } + } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public TypeParameterDeclaration () + { + } + + public TypeParameterDeclaration (string name) + { + Name = name; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeParameterDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeParameterDeclaration (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitTypeParameterDeclaration(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeParameterDeclaration o = other as TypeParameterDeclaration; + return o != null && this.Variance == o.Variance && MatchString(this.Name, o.Name) && this.Attributes.DoMatch(o.Attributes, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs new file mode 100644 index 000000000..9924132d3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs @@ -0,0 +1,107 @@ +// +// UsingAliasDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using Alias = Import; + /// + public class UsingAliasDeclaration : AstNode + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role AliasRole = new Role("Alias", Identifier.Null); + public static readonly Role ImportRole = UsingDeclaration.ImportRole; + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public string Alias { + get { + return GetChildByRole (AliasRole).Name; + } + set { + SetChildByRole(AliasRole, Identifier.Create (value)); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public AstType Import { + get { return GetChildByRole (ImportRole); } + set { SetChildByRole (ImportRole, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public UsingAliasDeclaration () + { + } + + public UsingAliasDeclaration (string alias, string nameSpace) + { + AddChild (Identifier.Create (alias), AliasRole); + AddChild (new SimpleType (nameSpace), ImportRole); + } + + public UsingAliasDeclaration (string alias, AstType import) + { + AddChild (Identifier.Create (alias), AliasRole); + AddChild (import, ImportRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingAliasDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingAliasDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingAliasDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingAliasDeclaration o = other as UsingAliasDeclaration; + return o != null && MatchString(this.Alias, o.Alias) && this.Import.DoMatch(o.Import, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingDeclaration.cs new file mode 100644 index 000000000..9e0c35a89 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingDeclaration.cs @@ -0,0 +1,122 @@ +// +// UsingDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq; +using System.Text; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using Import; + /// + public class UsingDeclaration : AstNode + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role ImportRole = new Role("Import", AstType.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public AstType Import { + get { return GetChildByRole (ImportRole); } + set { SetChildByRole (ImportRole, value); } + } + + public string Namespace { + get { return ConstructNamespace (Import); } + } + + internal static string ConstructNamespace (AstType type) + { + var stack = new Stack(); + while (type is MemberType) { + var mt = (MemberType)type; + stack.Push(mt.MemberName); + type = mt.Target; + if (mt.IsDoubleColon) { + stack.Push("::"); + } else { + stack.Push("."); + } + } + if (type is SimpleType) + stack.Push(((SimpleType)type).Identifier); + + var result = new StringBuilder(); + while (stack.Count > 0) + result.Append(stack.Pop()); + return result.ToString(); + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public UsingDeclaration () + { + } + + public UsingDeclaration (string nameSpace) + { + AddChild (AstType.Create (nameSpace), ImportRole); + } + + public UsingDeclaration (AstType import) + { + AddChild (import, ImportRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingDeclaration o = other as UsingDeclaration; + return o != null && this.Import.DoMatch(o.Import, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/WhitespaceNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/WhitespaceNode.cs new file mode 100644 index 000000000..c7e37f704 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/WhitespaceNode.cs @@ -0,0 +1,91 @@ +// +// WhitespaceNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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 +{ + /// + /// A Whitespace node contains only whitespaces. + /// + public class WhitespaceNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public string WhiteSpaceText { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (startLocation.Line, startLocation.Column + WhiteSpaceText.Length); + } + } + + public WhitespaceNode(string whiteSpaceText) : this (whiteSpaceText, TextLocation.Empty) + { + } + + public WhitespaceNode(string whiteSpaceText, TextLocation startLocation) + { + this.WhiteSpaceText = WhiteSpaceText; + this.startLocation = startLocation; + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitWhitespace (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitWhitespace (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitWhitespace (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as WhitespaceNode; + return o != null && o.WhiteSpaceText == WhiteSpaceText; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IAstVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IAstVisitor.cs new file mode 100644 index 000000000..4aadddbdc --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IAstVisitor.cs @@ -0,0 +1,418 @@ +// Copyright (c) 2010-2013 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 +{ + /// + /// AST visitor. + /// + public interface IAstVisitor + { + void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression); + void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression); + void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression); + void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression); + void VisitAsExpression(AsExpression asExpression); + void VisitAssignmentExpression(AssignmentExpression assignmentExpression); + void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression); + void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression); + void VisitCastExpression(CastExpression castExpression); + void VisitCheckedExpression(CheckedExpression checkedExpression); + void VisitConditionalExpression(ConditionalExpression conditionalExpression); + void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression); + void VisitDirectionExpression(DirectionExpression directionExpression); + void VisitIdentifierExpression(IdentifierExpression identifierExpression); + void VisitIndexerExpression(IndexerExpression indexerExpression); + void VisitInvocationExpression(InvocationExpression invocationExpression); + void VisitIsExpression(IsExpression isExpression); + void VisitLambdaExpression(LambdaExpression lambdaExpression); + void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression); + void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression); + void VisitNamedExpression(NamedExpression namedExpression); + void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression); + void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression); + void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression); + void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression); + void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression); + void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression); + void VisitSizeOfExpression(SizeOfExpression sizeOfExpression); + void VisitStackAllocExpression(StackAllocExpression stackAllocExpression); + void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression); + void VisitTypeOfExpression(TypeOfExpression typeOfExpression); + void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression); + void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression); + void VisitUncheckedExpression(UncheckedExpression uncheckedExpression); + + void VisitQueryExpression(QueryExpression queryExpression); + void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause); + void VisitQueryFromClause(QueryFromClause queryFromClause); + void VisitQueryLetClause(QueryLetClause queryLetClause); + void VisitQueryWhereClause(QueryWhereClause queryWhereClause); + void VisitQueryJoinClause(QueryJoinClause queryJoinClause); + void VisitQueryOrderClause(QueryOrderClause queryOrderClause); + void VisitQueryOrdering(QueryOrdering queryOrdering); + void VisitQuerySelectClause(QuerySelectClause querySelectClause); + void VisitQueryGroupClause(QueryGroupClause queryGroupClause); + + void VisitAttribute(Attribute attribute); + void VisitAttributeSection(AttributeSection attributeSection); + void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration); + void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration); + void VisitTypeDeclaration(TypeDeclaration typeDeclaration); + void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration); + void VisitUsingDeclaration(UsingDeclaration usingDeclaration); + void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration); + + void VisitBlockStatement(BlockStatement blockStatement); + void VisitBreakStatement(BreakStatement breakStatement); + void VisitCheckedStatement(CheckedStatement checkedStatement); + void VisitContinueStatement(ContinueStatement continueStatement); + void VisitDoWhileStatement(DoWhileStatement doWhileStatement); + void VisitEmptyStatement(EmptyStatement emptyStatement); + void VisitExpressionStatement(ExpressionStatement expressionStatement); + void VisitFixedStatement(FixedStatement fixedStatement); + void VisitForeachStatement(ForeachStatement foreachStatement); + void VisitForStatement(ForStatement forStatement); + void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement); + void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement); + void VisitGotoStatement(GotoStatement gotoStatement); + void VisitIfElseStatement(IfElseStatement ifElseStatement); + void VisitLabelStatement(LabelStatement labelStatement); + void VisitLockStatement(LockStatement lockStatement); + void VisitReturnStatement(ReturnStatement returnStatement); + void VisitSwitchStatement(SwitchStatement switchStatement); + void VisitSwitchSection(SwitchSection switchSection); + void VisitCaseLabel(CaseLabel caseLabel); + void VisitThrowStatement(ThrowStatement throwStatement); + void VisitTryCatchStatement(TryCatchStatement tryCatchStatement); + void VisitCatchClause(CatchClause catchClause); + void VisitUncheckedStatement(UncheckedStatement uncheckedStatement); + void VisitUnsafeStatement(UnsafeStatement unsafeStatement); + void VisitUsingStatement(UsingStatement usingStatement); + void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + void VisitWhileStatement(WhileStatement whileStatement); + void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); + void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); + + void VisitAccessor(Accessor accessor); + void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration); + void VisitConstructorInitializer(ConstructorInitializer constructorInitializer); + void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration); + void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration); + void VisitEventDeclaration(EventDeclaration eventDeclaration); + void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration); + void VisitFieldDeclaration(FieldDeclaration fieldDeclaration); + void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration); + void VisitMethodDeclaration(MethodDeclaration methodDeclaration); + void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration); + void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration); + void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration); + void VisitVariableInitializer(VariableInitializer variableInitializer); + void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration); + void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer); + + void VisitSyntaxTree(SyntaxTree syntaxTree); + void VisitSimpleType(SimpleType simpleType); + void VisitMemberType(MemberType memberType); + void VisitComposedType(ComposedType composedType); + void VisitArraySpecifier(ArraySpecifier arraySpecifier); + void VisitPrimitiveType(PrimitiveType primitiveType); + + void VisitComment(Comment comment); + void VisitNewLine(NewLineNode newLineNode); + void VisitWhitespace(WhitespaceNode whitespaceNode); + void VisitText(TextNode textNode); + void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective); + void VisitDocumentationReference(DocumentationReference documentationReference); + + void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration); + void VisitConstraint(Constraint constraint); + void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); + void VisitIdentifier(Identifier identifier); + + void VisitNullNode(AstNode nullNode); + void VisitErrorNode(AstNode errorNode); + void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); + } + + /// + /// AST visitor. + /// + public interface IAstVisitor + { + S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression); + S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression); + S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression); + S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression); + S VisitAsExpression(AsExpression asExpression); + S VisitAssignmentExpression(AssignmentExpression assignmentExpression); + S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression); + S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression); + S VisitCastExpression(CastExpression castExpression); + S VisitCheckedExpression(CheckedExpression checkedExpression); + S VisitConditionalExpression(ConditionalExpression conditionalExpression); + S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression); + S VisitDirectionExpression(DirectionExpression directionExpression); + S VisitIdentifierExpression(IdentifierExpression identifierExpression); + S VisitIndexerExpression(IndexerExpression indexerExpression); + S VisitInvocationExpression(InvocationExpression invocationExpression); + S VisitIsExpression(IsExpression isExpression); + S VisitLambdaExpression(LambdaExpression lambdaExpression); + S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression); + S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression); + S VisitNamedExpression(NamedExpression namedExpression); + S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression); + S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression); + S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression); + S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression); + S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression); + S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression); + S VisitSizeOfExpression(SizeOfExpression sizeOfExpression); + S VisitStackAllocExpression(StackAllocExpression stackAllocExpression); + S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression); + S VisitTypeOfExpression(TypeOfExpression typeOfExpression); + S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression); + S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression); + S VisitUncheckedExpression(UncheckedExpression uncheckedExpression); + + S VisitQueryExpression(QueryExpression queryExpression); + S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause); + S VisitQueryFromClause(QueryFromClause queryFromClause); + S VisitQueryLetClause(QueryLetClause queryLetClause); + S VisitQueryWhereClause(QueryWhereClause queryWhereClause); + S VisitQueryJoinClause(QueryJoinClause queryJoinClause); + S VisitQueryOrderClause(QueryOrderClause queryOrderClause); + S VisitQueryOrdering(QueryOrdering queryOrdering); + S VisitQuerySelectClause(QuerySelectClause querySelectClause); + S VisitQueryGroupClause(QueryGroupClause queryGroupClause); + + S VisitAttribute(Attribute attribute); + S VisitAttributeSection(AttributeSection attributeSection); + S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration); + S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration); + S VisitTypeDeclaration(TypeDeclaration typeDeclaration); + S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration); + S VisitUsingDeclaration(UsingDeclaration usingDeclaration); + S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration); + + S VisitBlockStatement(BlockStatement blockStatement); + S VisitBreakStatement(BreakStatement breakStatement); + S VisitCheckedStatement(CheckedStatement checkedStatement); + S VisitContinueStatement(ContinueStatement continueStatement); + S VisitDoWhileStatement(DoWhileStatement doWhileStatement); + S VisitEmptyStatement(EmptyStatement emptyStatement); + S VisitExpressionStatement(ExpressionStatement expressionStatement); + S VisitFixedStatement(FixedStatement fixedStatement); + S VisitForeachStatement(ForeachStatement foreachStatement); + S VisitForStatement(ForStatement forStatement); + S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement); + S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement); + S VisitGotoStatement(GotoStatement gotoStatement); + S VisitIfElseStatement(IfElseStatement ifElseStatement); + S VisitLabelStatement(LabelStatement labelStatement); + S VisitLockStatement(LockStatement lockStatement); + S VisitReturnStatement(ReturnStatement returnStatement); + S VisitSwitchStatement(SwitchStatement switchStatement); + S VisitSwitchSection(SwitchSection switchSection); + S VisitCaseLabel(CaseLabel caseLabel); + S VisitThrowStatement(ThrowStatement throwStatement); + S VisitTryCatchStatement(TryCatchStatement tryCatchStatement); + S VisitCatchClause(CatchClause catchClause); + S VisitUncheckedStatement(UncheckedStatement uncheckedStatement); + S VisitUnsafeStatement(UnsafeStatement unsafeStatement); + S VisitUsingStatement(UsingStatement usingStatement); + S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + S VisitWhileStatement(WhileStatement whileStatement); + S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); + S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); + + S VisitAccessor(Accessor accessor); + S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration); + S VisitConstructorInitializer(ConstructorInitializer constructorInitializer); + S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration); + S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration); + S VisitEventDeclaration(EventDeclaration eventDeclaration); + S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration); + S VisitFieldDeclaration(FieldDeclaration fieldDeclaration); + S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration); + S VisitMethodDeclaration(MethodDeclaration methodDeclaration); + S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration); + S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration); + S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration); + S VisitVariableInitializer(VariableInitializer variableInitializer); + S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration); + S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer); + + S VisitSyntaxTree(SyntaxTree syntaxTree); + S VisitSimpleType(SimpleType simpleType); + S VisitMemberType(MemberType memberType); + S VisitComposedType(ComposedType composedType); + S VisitArraySpecifier(ArraySpecifier arraySpecifier); + S VisitPrimitiveType(PrimitiveType primitiveType); + + S VisitComment(Comment comment); + S VisitWhitespace(WhitespaceNode whitespaceNode); + S VisitText(TextNode textNode); + S VisitNewLine(NewLineNode newLineNode); + S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective); + S VisitDocumentationReference(DocumentationReference documentationReference); + + S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration); + S VisitConstraint(Constraint constraint); + S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); + S VisitIdentifier(Identifier identifier); + + S VisitNullNode(AstNode nullNode); + S VisitErrorNode(AstNode errorNode); + S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); + } + + /// + /// AST visitor. + /// + public interface IAstVisitor + { + S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, T data); + S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, T data); + S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, T data); + S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, T data); + S VisitAsExpression(AsExpression asExpression, T data); + S VisitAssignmentExpression(AssignmentExpression assignmentExpression, T data); + S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, T data); + S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, T data); + S VisitCastExpression(CastExpression castExpression, T data); + S VisitCheckedExpression(CheckedExpression checkedExpression, T data); + S VisitConditionalExpression(ConditionalExpression conditionalExpression, T data); + S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, T data); + S VisitDirectionExpression(DirectionExpression directionExpression, T data); + S VisitIdentifierExpression(IdentifierExpression identifierExpression, T data); + S VisitIndexerExpression(IndexerExpression indexerExpression, T data); + S VisitInvocationExpression(InvocationExpression invocationExpression, T data); + S VisitIsExpression(IsExpression isExpression, T data); + S VisitLambdaExpression(LambdaExpression lambdaExpression, T data); + S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, T data); + S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, T data); + S VisitNamedExpression(NamedExpression namedExpression, T data); + S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, T data); + S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, T data); + S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data); + S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, T data); + S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, T data); + S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data); + S VisitSizeOfExpression(SizeOfExpression sizeOfExpression, T data); + S VisitStackAllocExpression(StackAllocExpression stackAllocExpression, T data); + S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data); + S VisitTypeOfExpression(TypeOfExpression typeOfExpression, T data); + S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data); + S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, T data); + S VisitUncheckedExpression(UncheckedExpression uncheckedExpression, T data); + + S VisitQueryExpression(QueryExpression queryExpression, T data); + S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data); + S VisitQueryFromClause(QueryFromClause queryFromClause, T data); + S VisitQueryLetClause(QueryLetClause queryLetClause, T data); + S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data); + S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data); + S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data); + S VisitQueryOrdering(QueryOrdering queryOrdering, T data); + S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data); + S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data); + + S VisitAttribute(Attribute attribute, T data); + S VisitAttributeSection(AttributeSection attributeSection, T data); + S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, T data); + S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, T data); + S VisitTypeDeclaration(TypeDeclaration typeDeclaration, T data); + S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration, T data); + S VisitUsingDeclaration(UsingDeclaration usingDeclaration, T data); + S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data); + + S VisitBlockStatement(BlockStatement blockStatement, T data); + S VisitBreakStatement(BreakStatement breakStatement, T data); + S VisitCheckedStatement(CheckedStatement checkedStatement, T data); + S VisitContinueStatement(ContinueStatement continueStatement, T data); + S VisitDoWhileStatement(DoWhileStatement doWhileStatement, T data); + S VisitEmptyStatement(EmptyStatement emptyStatement, T data); + S VisitExpressionStatement(ExpressionStatement expressionStatement, T data); + S VisitFixedStatement(FixedStatement fixedStatement, T data); + S VisitForeachStatement(ForeachStatement foreachStatement, T data); + S VisitForStatement(ForStatement forStatement, T data); + S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, T data); + S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, T data); + S VisitGotoStatement(GotoStatement gotoStatement, T data); + S VisitIfElseStatement(IfElseStatement ifElseStatement, T data); + S VisitLabelStatement(LabelStatement labelStatement, T data); + S VisitLockStatement(LockStatement lockStatement, T data); + S VisitReturnStatement(ReturnStatement returnStatement, T data); + S VisitSwitchStatement(SwitchStatement switchStatement, T data); + S VisitSwitchSection(SwitchSection switchSection, T data); + S VisitCaseLabel(CaseLabel caseLabel, T data); + S VisitThrowStatement(ThrowStatement throwStatement, T data); + S VisitTryCatchStatement(TryCatchStatement tryCatchStatement, T data); + S VisitCatchClause(CatchClause catchClause, T data); + S VisitUncheckedStatement(UncheckedStatement uncheckedStatement, T data); + S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data); + S VisitUsingStatement(UsingStatement usingStatement, T data); + S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data); + S VisitWhileStatement(WhileStatement whileStatement, T data); + S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data); + S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data); + + S VisitAccessor(Accessor accessor, T data); + S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, T data); + S VisitConstructorInitializer(ConstructorInitializer constructorInitializer, T data); + S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, T data); + S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration, T data); + S VisitEventDeclaration(EventDeclaration eventDeclaration, T data); + S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration, T data); + S VisitFieldDeclaration(FieldDeclaration fieldDeclaration, T data); + S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, T data); + S VisitMethodDeclaration(MethodDeclaration methodDeclaration, T data); + S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, T data); + S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, T data); + S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, T data); + S VisitVariableInitializer(VariableInitializer variableInitializer, T data); + S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration, T data); + S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer, T data); + + S VisitSyntaxTree(SyntaxTree syntaxTree, T data); + S VisitSimpleType(SimpleType simpleType, T data); + S VisitMemberType(MemberType memberType, T data); + S VisitComposedType(ComposedType composedType, T data); + S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data); + S VisitPrimitiveType(PrimitiveType primitiveType, T data); + + S VisitComment(Comment comment, T data); + S VisitNewLine(NewLineNode newLineNode, T data); + S VisitWhitespace(WhitespaceNode whitespaceNode, T data); + S VisitText(TextNode textNode, T data); + S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective, T data); + S VisitDocumentationReference(DocumentationReference documentationReference, T data); + + S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration, T data); + S VisitConstraint(Constraint constraint, T data); + S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode, T data); + S VisitIdentifier(Identifier identifier, T data); + + S VisitNullNode(AstNode nullNode, T data); + S VisitErrorNode(AstNode errorNode, T data); + S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data); + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs new file mode 100644 index 000000000..cf403afbd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs @@ -0,0 +1,173 @@ +// +// Identifier.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 +{ + public class Identifier : AstNode + { + public new static readonly Identifier Null = new NullIdentifier (); + sealed class NullIdentifier : Identifier + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Token; + } + } + + string name; + public string Name { + get { return this.name; } + set { + if (value == null) + throw new ArgumentNullException("value"); + ThrowIfFrozen(); + this.name = value; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.startLocation = value; + } + + const uint verbatimBit = 1u << AstNodeFlagsUsedBits; + + public bool IsVerbatim { + get { + return (flags & verbatimBit) != 0; + } + set { + ThrowIfFrozen(); + if (value) + flags |= verbatimBit; + else + flags &= ~verbatimBit; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length + (IsVerbatim ? 1 : 0)); + } + } + + Identifier () + { + this.name = string.Empty; + } + + protected Identifier (string name, TextLocation location) + { + if (name == null) + throw new ArgumentNullException("name"); + this.Name = name; + this.startLocation = location; + } + + public static Identifier Create (string name) + { + return Create (name, TextLocation.Empty); + } + + public static Identifier Create (string name, TextLocation location) + { + if (string.IsNullOrEmpty(name)) + return Identifier.Null; + if (name[0] == '@') + return new Identifier (name.Substring (1), new TextLocation (location.Line, location.Column + 1)) { IsVerbatim = true }; + else + return new Identifier (name, location); + } + + public static Identifier Create (string name, TextLocation location, bool isVerbatim) + { + if (string.IsNullOrEmpty (name)) + return Identifier.Null; + + if (isVerbatim) + return new Identifier (name, location) { IsVerbatim = true }; + return new Identifier (name, location); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIdentifier (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIdentifier (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIdentifier (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Identifier o = other as Identifier; + return o != null && !o.IsNull && MatchString(this.Name, o.Name); + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IdentifierExpressionBackreference.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IdentifierExpressionBackreference.cs new file mode 100644 index 000000000..7bfacd990 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IdentifierExpressionBackreference.cs @@ -0,0 +1,54 @@ +// Copyright (c) 2010-2013 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.Linq; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Matches identifier expressions that have the same identifier as the referenced variable/type definition/method definition. + /// + public class IdentifierExpressionBackreference : Pattern + { + readonly string referencedGroupName; + + public string ReferencedGroupName { + get { return referencedGroupName; } + } + + public IdentifierExpressionBackreference(string referencedGroupName) + { + if (referencedGroupName == null) + throw new ArgumentNullException("referencedGroupName"); + this.referencedGroupName = referencedGroupName; + } + + public override bool DoMatch(INode other, Match match) + { + CSharp.IdentifierExpression ident = other as CSharp.IdentifierExpression; + if (ident == null || ident.TypeArguments.Any()) + return false; + CSharp.AstNode referenced = (CSharp.AstNode)match.Get(referencedGroupName).Last(); + if (referenced == null) + return false; + return ident.Identifier == referenced.GetChildByRole(CSharp.Roles.Identifier).Name; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs new file mode 100644 index 000000000..2045d00ed --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs @@ -0,0 +1,150 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Text; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class MemberType : AstType + { + public static readonly Role TargetRole = new Role("Target", AstType.Null); + + bool isDoubleColon; + + public bool IsDoubleColon { + get { return isDoubleColon; } + set { + ThrowIfFrozen(); + isDoubleColon = value; + } + } + + public AstType Target { + get { return GetChildByRole(TargetRole); } + set { SetChildByRole(TargetRole, value); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public MemberType () + { + } + + public MemberType (AstType target, string memberName) + { + this.Target = target; + this.MemberName = memberName; + } + + public MemberType (AstType target, string memberName, IEnumerable typeArguments) + { + this.Target = target; + this.MemberName = memberName; + foreach (var arg in typeArguments) { + AddChild (arg, Roles.TypeArgument); + } + } + + public MemberType (AstType target, string memberName, params AstType[] typeArguments) : this (target, memberName, (IEnumerable)typeArguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMemberType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMemberType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMemberType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MemberType o = other as MemberType; + return o != null && this.IsDoubleColon == o.IsDoubleColon + && MatchString(this.MemberName, o.MemberName) && this.Target.DoMatch(o.Target, match) + && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + + TypeOrNamespaceReference t; + if (this.IsDoubleColon) { + SimpleType st = this.Target as SimpleType; + if (st != null) { + t = interningProvider.Intern(new AliasNamespaceReference(interningProvider.Intern(st.Identifier))); + } else { + t = null; + } + } else { + t = this.Target.ToTypeReference(lookupMode, interningProvider) as TypeOrNamespaceReference; + } + if (t == null) + return SpecialType.UnknownType; + var typeArguments = new List(); + foreach (var ta in this.TypeArguments) { + typeArguments.Add(ta.ToTypeReference(lookupMode, interningProvider)); + } + string memberName = interningProvider.Intern(this.MemberName); + return interningProvider.Intern(new MemberTypeOrNamespaceReference(t, memberName, interningProvider.InternList(typeArguments), lookupMode)); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs new file mode 100644 index 000000000..eb320495c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs @@ -0,0 +1,65 @@ +// +// Modifiers.cs +// +// Author: +// Mike Krüger +// +// Copyright (C) 2008 Novell, Inc (http://www.novell.com) +// +// 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 +{ + [Flags] + public enum Modifiers + { + None = 0, + + Private = 0x0001, + Internal = 0x0002, + Protected = 0x0004, + Public = 0x0008, + + Abstract = 0x0010, + Virtual = 0x0020, + Sealed = 0x0040, + Static = 0x0080, + Override = 0x0100, + Readonly = 0x0200, + Const = 0x0400, + New = 0x0800, + Partial = 0x1000, + + Extern = 0x2000, + Volatile = 0x4000, + Unsafe = 0x8000, + Async = 0x10000, + + VisibilityMask = Private | Internal | Protected | Public, + + /// + /// Special value used to match any modifiers during pattern matching. + /// + Any = unchecked((int)0x80000000) + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/NodeType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/NodeType.cs new file mode 100644 index 000000000..cf96a60c8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/NodeType.cs @@ -0,0 +1,56 @@ +// +// NodeType.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 +{ + public enum NodeType + { + Unknown, + /// + /// AstType + /// + TypeReference, + /// + /// Type or delegate declaration + /// + TypeDeclaration, + Member, + Statement, + Expression, + Token, + QueryClause, + /// + /// Comment or whitespace or pre-processor directive + /// + Whitespace, + /// + /// Placeholder for a pattern + /// + Pattern + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ObservableAstVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ObservableAstVisitor.cs new file mode 100644 index 000000000..898d51864 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ObservableAstVisitor.cs @@ -0,0 +1,857 @@ +// +// ObservableAstVisitor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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 +{ + public class ObservableAstVisitor : IAstVisitor + { + void Visit(Action enter, Action leave, T node) where T : AstNode + { + if (enter != null) + enter(node); + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces children. + next = child.NextSibling; + child.AcceptVisitor (this); + } + if (leave != null) + leave(node); + } + + void IAstVisitor.VisitNullNode(AstNode nullNode) + { + } + + void IAstVisitor.VisitErrorNode(AstNode nullNode) + { + } + + public event Action EnterSyntaxTree, LeaveSyntaxTree; + + void IAstVisitor.VisitSyntaxTree(SyntaxTree unit) + { + Visit(EnterSyntaxTree, LeaveSyntaxTree, unit); + } + + public event Action EnterComment, LeaveComment; + + void IAstVisitor.VisitComment(Comment comment) + { + Visit(EnterComment, LeaveComment, comment); + } + + public event Action EnterNewLine, LeaveNewLine; + + void IAstVisitor.VisitNewLine(NewLineNode newLineNode) + { + Visit(EnterNewLine, LeaveNewLine, newLineNode); + } + + public event Action EnterWhitespace, LeaveWhitespace; + + void IAstVisitor.VisitWhitespace(WhitespaceNode whitespace) + { + Visit(EnterWhitespace, LeaveWhitespace, whitespace); + } + + public event Action EnterText, LeaveText; + + void IAstVisitor.VisitText(TextNode textNode) + { + Visit(EnterText, LeaveText, textNode); + } + + public event Action EnterPreProcessorDirective, LeavePreProcessorDirective; + void IAstVisitor.VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + Visit(EnterPreProcessorDirective, LeavePreProcessorDirective, preProcessorDirective); + } + + public event Action EnterDocumentationReference, LeaveDocumentationReference; + + void IAstVisitor.VisitDocumentationReference(DocumentationReference documentationReference) + { + Visit(EnterDocumentationReference, LeaveDocumentationReference, documentationReference); + } + + public event Action EnterIdentifier, LeaveIdentifier; + + void IAstVisitor.VisitIdentifier(Identifier identifier) + { + Visit(EnterIdentifier, LeaveIdentifier, identifier); + } + + public event Action EnterCSharpTokenNode, LeaveCSharpTokenNode; + + void IAstVisitor.VisitCSharpTokenNode(CSharpTokenNode token) + { + Visit(EnterCSharpTokenNode, LeaveCSharpTokenNode, token); + } + + public event Action EnterPrimitiveType, LeavePrimitiveType; + + void IAstVisitor.VisitPrimitiveType(PrimitiveType primitiveType) + { + Visit(EnterPrimitiveType, LeavePrimitiveType, primitiveType); + } + + public event Action EnterComposedType, LeaveComposedType; + + void IAstVisitor.VisitComposedType(ComposedType composedType) + { + Visit(EnterComposedType, LeaveComposedType, composedType); + } + + public event Action EnterSimpleType, LeaveSimpleType; + + void IAstVisitor.VisitSimpleType(SimpleType simpleType) + { + Visit(EnterSimpleType, LeaveSimpleType, simpleType); + } + + public event Action EnterMemberType, LeaveMemberType; + + void IAstVisitor.VisitMemberType(MemberType memberType) + { + Visit(EnterMemberType, LeaveMemberType, memberType); + } + + public event Action EnterAttribute, LeaveAttribute; + + void IAstVisitor.VisitAttribute(Attribute attribute) + { + Visit(EnterAttribute, LeaveAttribute, attribute); + } + + public event Action EnterAttributeSection, LeaveAttributeSection; + + void IAstVisitor.VisitAttributeSection(AttributeSection attributeSection) + { + Visit(EnterAttributeSection, LeaveAttributeSection, attributeSection); + } + + public event Action EnterDelegateDeclaration, LeaveDelegateDeclaration; + + void IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + Visit(EnterDelegateDeclaration, LeaveDelegateDeclaration, delegateDeclaration); + } + + public event Action EnterNamespaceDeclaration, LeaveNamespaceDeclaration; + + void IAstVisitor.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + Visit(EnterNamespaceDeclaration, LeaveNamespaceDeclaration, namespaceDeclaration); + } + + public event Action EnterTypeDeclaration, LeaveTypeDeclaration; + + void IAstVisitor.VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + Visit(EnterTypeDeclaration, LeaveTypeDeclaration, typeDeclaration); + } + + public event Action EnterTypeParameterDeclaration, LeaveTypeParameterDeclaration; + + void IAstVisitor.VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + Visit(EnterTypeParameterDeclaration, LeaveTypeParameterDeclaration, typeParameterDeclaration); + } + + public event Action EnterEnumMemberDeclaration, LeaveEnumMemberDeclaration; + + void IAstVisitor.VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + Visit(EnterEnumMemberDeclaration, LeaveEnumMemberDeclaration, enumMemberDeclaration); + } + + public event Action EnterUsingDeclaration, LeaveUsingDeclaration; + + void IAstVisitor.VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + Visit(EnterUsingDeclaration, LeaveUsingDeclaration, usingDeclaration); + } + + public event Action EnterUsingAliasDeclaration, LeaveUsingAliasDeclaration; + + void IAstVisitor.VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) + { + Visit(EnterUsingAliasDeclaration, LeaveUsingAliasDeclaration, usingDeclaration); + } + + public event Action EnterExternAliasDeclaration, LeaveExternAliasDeclaration; + + void IAstVisitor.VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + Visit(EnterExternAliasDeclaration, LeaveExternAliasDeclaration, externAliasDeclaration); + } + + public event Action EnterConstructorDeclaration, LeaveConstructorDeclaration; + + void IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + Visit(EnterConstructorDeclaration, LeaveConstructorDeclaration, constructorDeclaration); + } + + public event Action EnterConstructorInitializer, LeaveConstructorInitializer; + + void IAstVisitor.VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + Visit(EnterConstructorInitializer, LeaveConstructorInitializer, constructorInitializer); + } + + public event Action EnterDestructorDeclaration, LeaveDestructorDeclaration; + + void IAstVisitor.VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + Visit(EnterDestructorDeclaration, LeaveDestructorDeclaration, destructorDeclaration); + } + + public event Action EnterEventDeclaration, LeaveEventDeclaration; + + void IAstVisitor.VisitEventDeclaration(EventDeclaration eventDeclaration) + { + Visit(EnterEventDeclaration, LeaveEventDeclaration, eventDeclaration); + } + + public event Action EnterCustomEventDeclaration, LeaveCustomEventDeclaration; + + void IAstVisitor.VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + Visit(EnterCustomEventDeclaration, LeaveCustomEventDeclaration, eventDeclaration); + } + + public event Action EnterFieldDeclaration, LeaveFieldDeclaration; + + void IAstVisitor.VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + Visit(EnterFieldDeclaration, LeaveFieldDeclaration, fieldDeclaration); + } + + public event Action EnterFixedFieldDeclaration, LeaveFixedFieldDeclaration; + + void IAstVisitor.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + Visit(EnterFixedFieldDeclaration, LeaveFixedFieldDeclaration, fixedFieldDeclaration); + } + + public event Action EnterFixedVariableInitializer, LeaveFixedVariableInitializer; + + void IAstVisitor.VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) + { + Visit(EnterFixedVariableInitializer, LeaveFixedVariableInitializer, fixedVariableInitializer); + } + + public event Action EnterIndexerDeclaration, LeaveIndexerDeclaration; + + void IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + Visit(EnterIndexerDeclaration, LeaveIndexerDeclaration, indexerDeclaration); + } + + public event Action EnterMethodDeclaration, LeaveMethodDeclaration; + + void IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + Visit(EnterMethodDeclaration, LeaveMethodDeclaration, methodDeclaration); + } + + public event Action EnterOperatorDeclaration, LeaveOperatorDeclaration; + + void IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + Visit(EnterOperatorDeclaration, LeaveOperatorDeclaration, operatorDeclaration); + } + + public event Action EnterPropertyDeclaration, LeavePropertyDeclaration; + + void IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + Visit(EnterPropertyDeclaration, LeavePropertyDeclaration, propertyDeclaration); + } + + public event Action EnterAccessor, LeaveAccessor; + + void IAstVisitor.VisitAccessor(Accessor accessor) + { + Visit(EnterAccessor, LeaveAccessor, accessor); + } + + public event Action EnterVariableInitializer, LeaveVariableInitializer; + + void IAstVisitor.VisitVariableInitializer(VariableInitializer variableInitializer) + { + Visit(EnterVariableInitializer, LeaveVariableInitializer, variableInitializer); + } + + public event Action EnterParameterDeclaration, LeaveParameterDeclaration; + + void IAstVisitor.VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + Visit(EnterParameterDeclaration, LeaveParameterDeclaration, parameterDeclaration); + } + + public event Action EnterConstraint, LeaveConstraint; + + void IAstVisitor.VisitConstraint(Constraint constraint) + { + Visit(EnterConstraint, LeaveConstraint, constraint); + } + + public event Action EnterBlockStatement, LeaveBlockStatement; + + void IAstVisitor.VisitBlockStatement(BlockStatement blockStatement) + { + Visit(EnterBlockStatement, LeaveBlockStatement, blockStatement); + } + + public event Action EnterExpressionStatement, LeaveExpressionStatement; + + void IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement) + { + Visit(EnterExpressionStatement, LeaveExpressionStatement, expressionStatement); + } + + public event Action EnterBreakStatement, LeaveBreakStatement; + + void IAstVisitor.VisitBreakStatement(BreakStatement breakStatement) + { + Visit(EnterBreakStatement, LeaveBreakStatement, breakStatement); + } + + public event Action EnterCheckedStatement, LeaveCheckedStatement; + + void IAstVisitor.VisitCheckedStatement(CheckedStatement checkedStatement) + { + Visit(EnterCheckedStatement, LeaveCheckedStatement, checkedStatement); + } + + public event Action EnterContinueStatement, LeaveContinueStatement; + + void IAstVisitor.VisitContinueStatement(ContinueStatement continueStatement) + { + Visit(EnterContinueStatement, LeaveContinueStatement, continueStatement); + } + + public event Action EnterDoWhileStatement, LeaveDoWhileStatement; + + void IAstVisitor.VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + Visit(EnterDoWhileStatement, LeaveDoWhileStatement, doWhileStatement); + } + + public event Action EnterEmptyStatement, LeaveEmptyStatement; + + void IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement) + { + Visit(EnterEmptyStatement, LeaveEmptyStatement, emptyStatement); + } + + public event Action EnterFixedStatement, LeaveFixedStatement; + + void IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement) + { + Visit(EnterFixedStatement, LeaveFixedStatement, fixedStatement); + } + + public event Action EnterForeachStatement, LeaveForeachStatement; + + void IAstVisitor.VisitForeachStatement(ForeachStatement foreachStatement) + { + Visit(EnterForeachStatement, LeaveForeachStatement, foreachStatement); + } + + public event Action EnterForStatement, LeaveForStatement; + + void IAstVisitor.VisitForStatement(ForStatement forStatement) + { + Visit(EnterForStatement, LeaveForStatement, forStatement); + } + + public event Action EnterGotoCaseStatement, LeaveGotoCaseStatement; + + void IAstVisitor.VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) + { + Visit(EnterGotoCaseStatement, LeaveGotoCaseStatement, gotoCaseStatement); + } + + public event Action EnterGotoDefaultStatement, LeaveGotoDefaultStatement; + + void IAstVisitor.VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) + { + Visit(EnterGotoDefaultStatement, LeaveGotoDefaultStatement, gotoDefaultStatement); + } + + public event Action EnterGotoStatement, LeaveGotoStatement; + + void IAstVisitor.VisitGotoStatement(GotoStatement gotoStatement) + { + Visit(EnterGotoStatement, LeaveGotoStatement, gotoStatement); + } + + public event Action EnterIfElseStatement, LeaveIfElseStatement; + + void IAstVisitor.VisitIfElseStatement(IfElseStatement ifElseStatement) + { + Visit(EnterIfElseStatement, LeaveIfElseStatement, ifElseStatement); + } + + public event Action EnterLabelStatement, LeaveLabelStatement; + + void IAstVisitor.VisitLabelStatement(LabelStatement labelStatement) + { + Visit(EnterLabelStatement, LeaveLabelStatement, labelStatement); + } + + public event Action EnterLockStatement, LeaveLockStatement; + + void IAstVisitor.VisitLockStatement(LockStatement lockStatement) + { + Visit(EnterLockStatement, LeaveLockStatement, lockStatement); + } + + public event Action EnterReturnStatement, LeaveReturnStatement; + + void IAstVisitor.VisitReturnStatement(ReturnStatement returnStatement) + { + Visit(EnterReturnStatement, LeaveReturnStatement, returnStatement); + } + + public event Action EnterSwitchStatement, LeaveSwitchStatement; + + void IAstVisitor.VisitSwitchStatement(SwitchStatement switchStatement) + { + Visit(EnterSwitchStatement, LeaveSwitchStatement, switchStatement); + } + + public event Action EnterSwitchSection, LeaveSwitchSection; + + void IAstVisitor.VisitSwitchSection(SwitchSection switchSection) + { + Visit(EnterSwitchSection, LeaveSwitchSection, switchSection); + } + + public event Action EnterCaseLabel, LeaveCaseLabel; + + void IAstVisitor.VisitCaseLabel(CaseLabel caseLabel) + { + Visit(EnterCaseLabel, LeaveCaseLabel, caseLabel); + } + + public event Action EnterThrowStatement, LeaveThrowStatement; + + void IAstVisitor.VisitThrowStatement(ThrowStatement throwStatement) + { + Visit(EnterThrowStatement, LeaveThrowStatement, throwStatement); + } + + public event Action EnterTryCatchStatement, LeaveTryCatchStatement; + + void IAstVisitor.VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + Visit(EnterTryCatchStatement, LeaveTryCatchStatement, tryCatchStatement); + } + + public event Action EnterCatchClause, LeaveCatchClause; + + void IAstVisitor.VisitCatchClause(CatchClause catchClause) + { + Visit(EnterCatchClause, LeaveCatchClause, catchClause); + } + + public event Action EnterUncheckedStatement, LeaveUncheckedStatement; + + void IAstVisitor.VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + Visit(EnterUncheckedStatement, LeaveUncheckedStatement, uncheckedStatement); + } + + public event Action EnterUnsafeStatement, LeaveUnsafeStatement; + + void IAstVisitor.VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + Visit(EnterUnsafeStatement, LeaveUnsafeStatement, unsafeStatement); + } + + public event Action EnterUsingStatement, LeaveUsingStatement; + + void IAstVisitor.VisitUsingStatement(UsingStatement usingStatement) + { + Visit(EnterUsingStatement, LeaveUsingStatement, usingStatement); + } + + public event Action EnterVariableDeclarationStatement, LeaveVariableDeclarationStatement; + + void IAstVisitor.VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + Visit(EnterVariableDeclarationStatement, LeaveVariableDeclarationStatement, variableDeclarationStatement); + } + + public event Action EnterWhileStatement, LeaveWhileStatement; + + void IAstVisitor.VisitWhileStatement(WhileStatement whileStatement) + { + Visit(EnterWhileStatement, LeaveWhileStatement, whileStatement); + } + + public event Action EnterYieldBreakStatement, LeaveYieldBreakStatement; + + void IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + Visit(EnterYieldBreakStatement, LeaveYieldBreakStatement, yieldBreakStatement); + } + + public event Action EnterYieldReturnStatement, LeaveYieldReturnStatement; + + void IAstVisitor.VisitYieldReturnStatement(YieldReturnStatement yieldStatement) + { + Visit(EnterYieldReturnStatement, LeaveYieldReturnStatement, yieldStatement); + } + + public event Action EnterAnonymousMethodExpression, LeaveAnonymousMethodExpression; + + void IAstVisitor.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + Visit(EnterAnonymousMethodExpression, LeaveAnonymousMethodExpression, anonymousMethodExpression); + } + + public event Action EnterLambdaExpression, LeaveLambdaExpression; + + void IAstVisitor.VisitLambdaExpression(LambdaExpression lambdaExpression) + { + Visit(EnterLambdaExpression, LeaveLambdaExpression, lambdaExpression); + } + + public event Action EnterAssignmentExpression, LeaveAssignmentExpression; + + void IAstVisitor.VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + Visit(EnterAssignmentExpression, LeaveAssignmentExpression, assignmentExpression); + } + + public event Action EnterBaseReferenceExpression, LeaveBaseReferenceExpression; + + void IAstVisitor.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) + { + Visit(EnterBaseReferenceExpression, LeaveBaseReferenceExpression, baseReferenceExpression); + } + + public event Action EnterBinaryOperatorExpression, LeaveBinaryOperatorExpression; + + void IAstVisitor.VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + Visit(EnterBinaryOperatorExpression, LeaveBinaryOperatorExpression, binaryOperatorExpression); + } + + public event Action EnterCastExpression, LeaveCastExpression; + + void IAstVisitor.VisitCastExpression(CastExpression castExpression) + { + Visit(EnterCastExpression, LeaveCastExpression, castExpression); + } + + public event Action EnterCheckedExpression, LeaveCheckedExpression; + + void IAstVisitor.VisitCheckedExpression(CheckedExpression checkedExpression) + { + Visit(EnterCheckedExpression, LeaveCheckedExpression, checkedExpression); + } + + public event Action EnterConditionalExpression, LeaveConditionalExpression; + + void IAstVisitor.VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + Visit(EnterConditionalExpression, LeaveConditionalExpression, conditionalExpression); + } + + public event Action EnterIdentifierExpression, LeaveIdentifierExpression; + + void IAstVisitor.VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + Visit(EnterIdentifierExpression, LeaveIdentifierExpression, identifierExpression); + } + + public event Action EnterIndexerExpression, LeaveIndexerExpression; + + void IAstVisitor.VisitIndexerExpression(IndexerExpression indexerExpression) + { + Visit(EnterIndexerExpression, LeaveIndexerExpression, indexerExpression); + } + + public event Action EnterInvocationExpression, LeaveInvocationExpression; + + void IAstVisitor.VisitInvocationExpression(InvocationExpression invocationExpression) + { + Visit(EnterInvocationExpression, LeaveInvocationExpression, invocationExpression); + } + + public event Action EnterDirectionExpression, LeaveDirectionExpression; + + void IAstVisitor.VisitDirectionExpression(DirectionExpression directionExpression) + { + Visit(EnterDirectionExpression, LeaveDirectionExpression, directionExpression); + } + + public event Action EnterMemberReferenceExpression, LeaveMemberReferenceExpression; + + void IAstVisitor.VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + Visit(EnterMemberReferenceExpression, LeaveMemberReferenceExpression, memberReferenceExpression); + } + + public event Action EnterNullReferenceExpression, LeaveNullReferenceExpression; + + void IAstVisitor.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) + { + Visit(EnterNullReferenceExpression, LeaveNullReferenceExpression, nullReferenceExpression); + } + + public event Action EnterObjectCreateExpression, LeaveObjectCreateExpression; + + void IAstVisitor.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + Visit(EnterObjectCreateExpression, LeaveObjectCreateExpression, objectCreateExpression); + } + + public event Action EnterAnonymousTypeCreateExpression, LeaveAnonymousTypeCreateExpression; + + void IAstVisitor.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + Visit(EnterAnonymousTypeCreateExpression, LeaveAnonymousTypeCreateExpression, anonymousTypeCreateExpression); + } + + public event Action EnterArrayCreateExpression, LeaveArrayCreateExpression; + + void IAstVisitor.VisitArrayCreateExpression(ArrayCreateExpression arraySCreateExpression) + { + Visit(EnterArrayCreateExpression, LeaveArrayCreateExpression, arraySCreateExpression); + } + + public event Action EnterParenthesizedExpression, LeaveParenthesizedExpression; + + void IAstVisitor.VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + Visit(EnterParenthesizedExpression, LeaveParenthesizedExpression, parenthesizedExpression); + } + + public event Action EnterPointerReferenceExpression, LeavePointerReferenceExpression; + + void IAstVisitor.VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + Visit(EnterPointerReferenceExpression, LeavePointerReferenceExpression, pointerReferenceExpression); + } + + public event Action EnterPrimitiveExpression, LeavePrimitiveExpression; + + void IAstVisitor.VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + Visit(EnterPrimitiveExpression, LeavePrimitiveExpression, primitiveExpression); + } + + public event Action EnterSizeOfExpression, LeaveSizeOfExpression; + + void IAstVisitor.VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + Visit(EnterSizeOfExpression, LeaveSizeOfExpression, sizeOfExpression); + } + + public event Action EnterStackAllocExpression, LeaveStackAllocExpression; + + void IAstVisitor.VisitStackAllocExpression(StackAllocExpression stackAllocExpression) + { + Visit(EnterStackAllocExpression, LeaveStackAllocExpression, stackAllocExpression); + } + + public event Action EnterThisReferenceExpression, LeaveThisReferenceExpression; + + void IAstVisitor.VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + Visit(EnterThisReferenceExpression, LeaveThisReferenceExpression, thisReferenceExpression); + } + + public event Action EnterTypeOfExpression, LeaveTypeOfExpression; + + void IAstVisitor.VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + Visit(EnterTypeOfExpression, LeaveTypeOfExpression, typeOfExpression); + } + + public event Action EnterTypeReferenceExpression, LeaveTypeReferenceExpression; + + void IAstVisitor.VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + Visit(EnterTypeReferenceExpression, LeaveTypeReferenceExpression, typeReferenceExpression); + } + + public event Action EnterUnaryOperatorExpression, LeaveUnaryOperatorExpression; + + void IAstVisitor.VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + Visit(EnterUnaryOperatorExpression, LeaveUnaryOperatorExpression, unaryOperatorExpression); + } + + public event Action EnterUncheckedExpression, LeaveUncheckedExpression; + + void IAstVisitor.VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + Visit(EnterUncheckedExpression, LeaveUncheckedExpression, uncheckedExpression); + } + + public event Action EnterQueryExpression, LeaveQueryExpression; + + void IAstVisitor.VisitQueryExpression(QueryExpression queryExpression) + { + Visit(EnterQueryExpression, LeaveQueryExpression, queryExpression); + } + + public event Action EnterQueryContinuationClause, LeaveQueryContinuationClause; + + void IAstVisitor.VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + Visit(EnterQueryContinuationClause, LeaveQueryContinuationClause, queryContinuationClause); + } + + public event Action EnterQueryFromClause, LeaveQueryFromClause; + + void IAstVisitor.VisitQueryFromClause(QueryFromClause queryFromClause) + { + Visit(EnterQueryFromClause, LeaveQueryFromClause, queryFromClause); + } + + public event Action EnterQueryLetClause, LeaveQueryLetClause; + + void IAstVisitor.VisitQueryLetClause(QueryLetClause queryLetClause) + { + Visit(EnterQueryLetClause, LeaveQueryLetClause, queryLetClause); + } + + public event Action EnterQueryWhereClause, LeaveQueryWhereClause; + + void IAstVisitor.VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + Visit(EnterQueryWhereClause, LeaveQueryWhereClause, queryWhereClause); + } + + public event Action EnterQueryJoinClause, LeaveQueryJoinClause; + + void IAstVisitor.VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + Visit(EnterQueryJoinClause, LeaveQueryJoinClause, queryJoinClause); + } + + public event Action EnterQueryOrderClause, LeaveQueryOrderClause; + + void IAstVisitor.VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + Visit(EnterQueryOrderClause, LeaveQueryOrderClause, queryOrderClause); + } + + public event Action EnterQueryOrdering, LeaveQueryOrdering; + + void IAstVisitor.VisitQueryOrdering(QueryOrdering queryOrdering) + { + Visit(EnterQueryOrdering, LeaveQueryOrdering, queryOrdering); + } + + public event Action EnterQuerySelectClause, LeaveQuerySelectClause; + + void IAstVisitor.VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + Visit(EnterQuerySelectClause, LeaveQuerySelectClause, querySelectClause); + } + + public event Action EnterQueryGroupClause, LeaveQueryGroupClause; + + void IAstVisitor.VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + Visit(EnterQueryGroupClause, LeaveQueryGroupClause, queryGroupClause); + } + + public event Action EnterAsExpression, LeaveAsExpression; + + void IAstVisitor.VisitAsExpression(AsExpression asExpression) + { + Visit(EnterAsExpression, LeaveAsExpression, asExpression); + } + + public event Action EnterIsExpression, LeaveIsExpression; + + void IAstVisitor.VisitIsExpression(IsExpression isExpression) + { + Visit(EnterIsExpression, LeaveIsExpression, isExpression); + } + + public event Action EnterDefaultValueExpression, LeaveDefaultValueExpression; + + void IAstVisitor.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) + { + Visit(EnterDefaultValueExpression, LeaveDefaultValueExpression, defaultValueExpression); + } + + public event Action EnterUndocumentedExpression, LeaveUndocumentedExpression; + + void IAstVisitor.VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) + { + Visit(EnterUndocumentedExpression, LeaveUndocumentedExpression, undocumentedExpression); + } + + public event Action EnterArrayInitializerExpression, LeaveArrayInitializerExpression; + + void IAstVisitor.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + Visit(EnterArrayInitializerExpression, LeaveArrayInitializerExpression, arrayInitializerExpression); + } + + public event Action EnterArraySpecifier, LeaveArraySpecifier; + + void IAstVisitor.VisitArraySpecifier(ArraySpecifier arraySpecifier) + { + Visit(EnterArraySpecifier, LeaveArraySpecifier, arraySpecifier); + } + + public event Action EnterNamedArgumentExpression, LeaveNamedArgumentExpression; + + void IAstVisitor.VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + Visit(EnterNamedArgumentExpression, LeaveNamedArgumentExpression, namedArgumentExpression); + } + + public event Action EnterNamedExpression, LeaveNamedExpression; + + void IAstVisitor.VisitNamedExpression(NamedExpression namedExpression) + { + Visit(EnterNamedExpression, LeaveNamedExpression, namedExpression); + } + + void IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs new file mode 100644 index 000000000..5b52a37ac --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs @@ -0,0 +1,163 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class PrimitiveType : AstType + { + 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); } + } + + public PrimitiveType() + { + } + + public PrimitiveType(string keyword) + { + this.Keyword = keyword; + } + + public PrimitiveType(string keyword, TextLocation location) + { + this.Keyword = keyword; + this.location = location; + } + + public override TextLocation StartLocation { + get { + return location; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.location = value; + } + + public override TextLocation EndLocation { + get { + return new TextLocation (location.Line, location.Column + keyword.Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPrimitiveType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPrimitiveType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPrimitiveType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PrimitiveType o = other as PrimitiveType; + return o != null && MatchString(this.Keyword, o.Keyword); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return Keyword; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + KnownTypeCode typeCode = GetTypeCodeForPrimitiveType(this.Keyword); + if (typeCode == KnownTypeCode.None) + return new UnknownType(null, this.Keyword); + else + return KnownTypeReference.Get(typeCode); + } + + public static KnownTypeCode GetTypeCodeForPrimitiveType(string keyword) + { + switch (keyword) { + case "string": + return KnownTypeCode.String; + case "int": + return KnownTypeCode.Int32; + case "uint": + return KnownTypeCode.UInt32; + case "object": + return KnownTypeCode.Object; + case "bool": + return KnownTypeCode.Boolean; + case "sbyte": + return KnownTypeCode.SByte; + case "byte": + return KnownTypeCode.Byte; + case "short": + return KnownTypeCode.Int16; + case "ushort": + return KnownTypeCode.UInt16; + case "long": + return KnownTypeCode.Int64; + case "ulong": + return KnownTypeCode.UInt64; + case "float": + return KnownTypeCode.Single; + case "double": + return KnownTypeCode.Double; + case "decimal": + return KnownTypeCode.Decimal; + case "char": + return KnownTypeCode.Char; + case "void": + return KnownTypeCode.Void; + default: + return KnownTypeCode.None; + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs new file mode 100644 index 000000000..a7408c91d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs @@ -0,0 +1,96 @@ +// +// Roles.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// 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 +{ + 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); + public static readonly Role Parameter = new Role ("Parameter"); + public static readonly Role Argument = new Role ("Argument", CSharp.Expression.Null); + public static readonly Role Type = new Role ("Type", CSharp.AstType.Null); + public static readonly Role Expression = new Role ("Expression", CSharp.Expression.Null); + public static readonly Role TargetExpression = new Role ("Target", CSharp.Expression.Null); + public readonly static Role Condition = new Role ("Condition", CSharp.Expression.Null); + public static readonly Role TypeParameter = new Role ("TypeParameter"); + public static readonly Role TypeArgument = new Role ("TypeArgument", CSharp.AstType.Null); + public readonly static Role Constraint = new Role ("Constraint"); + public static readonly Role Variable = new Role ("Variable", VariableInitializer.Null); + public static readonly Role EmbeddedStatement = new Role ("EmbeddedStatement", CSharp.Statement.Null); + public readonly static Role TypeMemberRole = new Role ("TypeMember"); + + + // public static readonly TokenRole Keyword = new TokenRole ("Keyword", CSharpTokenNode.Null); +// public static readonly TokenRole InKeyword = new TokenRole ("InKeyword", CSharpTokenNode.Null); + + // some pre defined constants for most used punctuation + public static readonly TokenRole LPar = new TokenRole ("("); + public static readonly TokenRole RPar = new TokenRole (")"); + public static readonly TokenRole LBracket = new TokenRole ("["); + public static readonly TokenRole RBracket = new TokenRole ("]"); + public static readonly TokenRole LBrace = new TokenRole ("{"); + public static readonly TokenRole RBrace = new TokenRole ("}"); + public static readonly TokenRole LChevron = new TokenRole ("<"); + public static readonly TokenRole RChevron = new TokenRole (">"); + public static readonly TokenRole Comma = new TokenRole (","); + public static readonly TokenRole Dot = new TokenRole ("."); + public static readonly TokenRole Semicolon = new TokenRole (";"); + public static readonly TokenRole Assign = new TokenRole ("="); + public static readonly TokenRole Colon = new TokenRole (":"); + public static readonly TokenRole DoubleColon = new TokenRole ("::"); + public static readonly Role Comment = new Role ("Comment"); + public static readonly Role NewLine = new Role ("NewLine"); + public static readonly Role Whitespace = new Role ("Whitespace"); + public static readonly Role Text = new Role ("Text"); + public static readonly Role PreProcessorDirective = new Role ("PreProcessorDirective"); + public static readonly Role Error = new Role ("Error"); + + public readonly static Role BaseType = new Role ("BaseType", AstType.Null); + + public static readonly Role Attribute = new Role ("Attribute"); + public static readonly Role AttributeTargetRole = new Role ("AttributeTarget", CSharpTokenNode.Null); + + public readonly static TokenRole WhereKeyword = new TokenRole ("where"); + public readonly static Role ConstraintTypeParameter = new Role ("TypeParameter", SimpleType.Null); + public readonly static TokenRole DelegateKeyword = new TokenRole ("delegate"); + public static readonly TokenRole ExternKeyword = new TokenRole ("extern"); + public static readonly TokenRole AliasKeyword = new TokenRole ("alias"); + public static readonly TokenRole NamespaceKeyword = new TokenRole ("namespace"); + + public static readonly TokenRole EnumKeyword = new TokenRole ("enum"); + public static readonly TokenRole InterfaceKeyword = new TokenRole ("interface"); + public static readonly TokenRole StructKeyword = new TokenRole ("struct"); + public static readonly TokenRole ClassKeyword = new TokenRole ("class"); + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SimpleType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SimpleType.cs new file mode 100644 index 000000000..529a62fbe --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SimpleType.cs @@ -0,0 +1,169 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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.Text; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class SimpleType : AstType + { + #region Null + public new static readonly SimpleType Null = new NullSimpleType (); + + sealed class NullSimpleType : SimpleType + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + return SpecialType.UnknownType; + } + } + #endregion + + public SimpleType() + { + } + + public SimpleType(string identifier) + { + this.Identifier = identifier; + } + + public SimpleType (Identifier identifier) + { + this.IdentifierToken = identifier; + } + + public SimpleType(string identifier, TextLocation location) + { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (identifier, location)); + } + + public SimpleType (string identifier, IEnumerable typeArguments) + { + this.Identifier = identifier; + foreach (var arg in typeArguments) { + AddChild (arg, Roles.TypeArgument); + } + } + + public SimpleType (string identifier, params AstType[] typeArguments) : this (identifier, (IEnumerable)typeArguments) + { + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSimpleType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSimpleType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSimpleType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SimpleType o = other as SimpleType; + return o != null && MatchString(this.Identifier, o.Identifier) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + var typeArguments = new List(); + foreach (var ta in this.TypeArguments) { + typeArguments.Add(ta.ToTypeReference(lookupMode, interningProvider)); + } + string identifier = interningProvider.Intern(this.Identifier); + if (typeArguments.Count == 0 && string.IsNullOrEmpty(identifier)) { + // empty SimpleType is used for typeof(List<>). + return SpecialType.UnboundTypeArgument; + } + var t = new SimpleTypeOrNamespaceReference(identifier, interningProvider.InternList(typeArguments), lookupMode); + return interningProvider.Intern(t); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BlockStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BlockStatement.cs new file mode 100644 index 000000000..24b9cd106 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BlockStatement.cs @@ -0,0 +1,164 @@ +// +// BlockStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// { Statements } + /// + public class BlockStatement : Statement, IEnumerable + { + public static readonly Role StatementRole = new Role("Statement", Statement.Null); + + #region Null + public static readonly new BlockStatement Null = new NullBlockStatement (); + sealed class NullBlockStatement : BlockStatement + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator BlockStatement(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : BlockStatement, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Statements { + get { return GetChildrenByRole (StatementRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBlockStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBlockStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBlockStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BlockStatement o = other as BlockStatement; + return o != null && !o.IsNull && this.Statements.DoMatch(o.Statements, match); + } + + public void Add(Statement statement) + { + AddChild(statement, StatementRole); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.Statements.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.Statements.GetEnumerator(); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BreakStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BreakStatement.cs new file mode 100644 index 000000000..4bb4e39ef --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BreakStatement.cs @@ -0,0 +1,65 @@ +// +// BreakStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// break; + /// + public class BreakStatement : Statement + { + public static readonly TokenRole BreakKeywordRole = new TokenRole ("break"); + + public CSharpTokenNode BreakToken { + get { return GetChildByRole (BreakKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBreakStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBreakStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBreakStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BreakStatement o = other as BreakStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/CheckedStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/CheckedStatement.cs new file mode 100644 index 000000000..803067aff --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/CheckedStatement.cs @@ -0,0 +1,75 @@ +// +// CheckedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// checked BodyBlock + /// + public class CheckedStatement : Statement + { + public static readonly TokenRole CheckedKeywordRole = new TokenRole ("checked"); + + public CSharpTokenNode CheckedToken { + get { return GetChildByRole (CheckedKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public CheckedStatement () + { + } + + public CheckedStatement (BlockStatement body) + { + AddChild (body, Roles.Body); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCheckedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCheckedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCheckedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CheckedStatement o = other as CheckedStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ContinueStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ContinueStatement.cs new file mode 100644 index 000000000..aac1690b2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ContinueStatement.cs @@ -0,0 +1,65 @@ +// +// ContinueStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// continue; + /// + public class ContinueStatement : Statement + { + public static readonly TokenRole ContinueKeywordRole = new TokenRole ("continue"); + + public CSharpTokenNode ContinueToken { + get { return GetChildByRole (ContinueKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitContinueStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitContinueStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitContinueStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ContinueStatement o = other as ContinueStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/DoWhileStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/DoWhileStatement.cs new file mode 100644 index 000000000..280ca7cea --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/DoWhileStatement.cs @@ -0,0 +1,99 @@ +// +// DoWhileStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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 +{ + /// + /// "do EmbeddedStatement while(Condition);" + /// + public class DoWhileStatement : Statement + { + public static readonly TokenRole DoKeywordRole = new TokenRole ("do"); + public static readonly TokenRole WhileKeywordRole = new TokenRole ("while"); + + public CSharpTokenNode DoToken { + get { return GetChildByRole (DoKeywordRole); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public CSharpTokenNode WhileToken { + get { return GetChildByRole (WhileKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDoWhileStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDoWhileStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDoWhileStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DoWhileStatement o = other as DoWhileStatement; + return o != null && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match) && this.Condition.DoMatch(o.Condition, match); + } + + public DoWhileStatement() + { + } + + public DoWhileStatement(Expression condition, Statement embeddedStatement) + { + this.Condition = condition; + this.EmbeddedStatement = embeddedStatement; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs new file mode 100644 index 000000000..deaa3a9c4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs @@ -0,0 +1,72 @@ +// +// EmptyStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// ; + /// + public class EmptyStatement : Statement + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + 1); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEmptyStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEmptyStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEmptyStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EmptyStatement o = other as EmptyStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ExpressionStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ExpressionStatement.cs new file mode 100644 index 000000000..1fdc4ddc4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ExpressionStatement.cs @@ -0,0 +1,73 @@ +// +// ExpressionStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression; + /// + public class ExpressionStatement : Statement + { + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitExpressionStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitExpressionStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitExpressionStatement (this, data); + } + + public ExpressionStatement() + { + } + + public ExpressionStatement(Expression expression) + { + this.Expression = expression; + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ExpressionStatement o = other as ExpressionStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/FixedStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/FixedStatement.cs new file mode 100644 index 000000000..d44366504 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/FixedStatement.cs @@ -0,0 +1,85 @@ +// +// FixedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// fixed (Type Variables) EmbeddedStatement + /// + public class FixedStatement : Statement + { + public static readonly TokenRole FixedKeywordRole = new TokenRole ("fixed"); + + public CSharpTokenNode FixedToken { + get { return GetChildByRole (FixedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + FixedStatement o = other as FixedStatement; + return o != null && this.Type.DoMatch(o.Type, match) && this.Variables.DoMatch(o.Variables, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForStatement.cs new file mode 100644 index 000000000..d369536d0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForStatement.cs @@ -0,0 +1,97 @@ +// +// ForStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// for (Initializers; Condition; Iterators) EmbeddedStatement + /// + public class ForStatement : Statement + { + public static readonly TokenRole ForKeywordRole = new TokenRole ("for"); + public readonly static Role InitializerRole = new Role("Initializer", Statement.Null); + public readonly static Role IteratorRole = new Role("Iterator", Statement.Null); + + public CSharpTokenNode ForToken { + get { return GetChildByRole (ForKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + /// + /// Gets the list of initializer statements. + /// Note: this contains multiple statements for "for (a = 2, b = 1; a > b; a--)", but contains + /// only a single statement for "for (int a = 2, b = 1; a > b; a--)" (a single VariableDeclarationStatement with two variables) + /// + public AstNodeCollection Initializers { + get { return GetChildrenByRole (InitializerRole); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public AstNodeCollection Iterators { + get { return GetChildrenByRole (IteratorRole); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitForStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitForStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitForStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ForStatement o = other as ForStatement; + return o != null && this.Initializers.DoMatch(o.Initializers, match) && this.Condition.DoMatch(o.Condition, match) + && this.Iterators.DoMatch(o.Iterators, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForeachStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForeachStatement.cs new file mode 100644 index 000000000..b3a9c5f78 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForeachStatement.cs @@ -0,0 +1,108 @@ +// +// ForeachStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// foreach (Type VariableName in InExpression) EmbeddedStatement + /// + public class ForeachStatement : Statement + { + public static readonly TokenRole ForeachKeywordRole = new TokenRole ("foreach"); + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + + public CSharpTokenNode ForeachToken { + get { return GetChildByRole (ForeachKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType VariableType { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string VariableName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier VariableNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode InToken { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression InExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitForeachStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitForeachStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitForeachStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ForeachStatement o = other as ForeachStatement; + return o != null && this.VariableType.DoMatch(o.VariableType, match) && MatchString(this.VariableName, o.VariableName) + && this.InExpression.DoMatch(o.InExpression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/GotoStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/GotoStatement.cs new file mode 100644 index 000000000..7aff7a82f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/GotoStatement.cs @@ -0,0 +1,178 @@ +// +// GotoStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// "goto Label;" + /// + public class GotoStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + + public GotoStatement () + { + } + + public GotoStatement (string label) + { + this.Label = label; + } + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public string Label { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + if (string.IsNullOrEmpty(value)) + SetChildByRole(Roles.Identifier, null); + else + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoStatement o = other as GotoStatement; + return o != null && MatchString(this.Label, o.Label); + } + } + + /// + /// or "goto case LabelExpression;" + /// + public class GotoCaseStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + public static readonly TokenRole CaseKeywordRole = new TokenRole ("case"); + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public CSharpTokenNode CaseToken { + get { return GetChildByRole (CaseKeywordRole); } + } + + /// + /// Used for "goto case LabelExpression;" + /// + public Expression LabelExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoCaseStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoCaseStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoCaseStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoCaseStatement o = other as GotoCaseStatement; + return o != null && this.LabelExpression.DoMatch(o.LabelExpression, match); + } + } + + /// + /// or "goto default;" + /// + public class GotoDefaultStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + public static readonly TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public CSharpTokenNode DefaultToken { + get { return GetChildByRole (DefaultKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoDefaultStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoDefaultStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoDefaultStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoDefaultStatement o = other as GotoDefaultStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/IfElseStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/IfElseStatement.cs new file mode 100644 index 000000000..70ece3fd5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/IfElseStatement.cs @@ -0,0 +1,103 @@ +// +// IfElseStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// if (Condition) TrueStatement else FalseStatement + /// + public class IfElseStatement : Statement + { + public readonly static TokenRole IfKeywordRole = new TokenRole ("if"); + public readonly static Role ConditionRole = Roles.Condition; + public readonly static Role TrueRole = new Role("True", Statement.Null); + public readonly static TokenRole ElseKeywordRole = new TokenRole ("else"); + public readonly static Role FalseRole = new Role("False", Statement.Null); + + public CSharpTokenNode IfToken { + get { return GetChildByRole (IfKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (ConditionRole); } + set { SetChildByRole (ConditionRole, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement TrueStatement { + get { return GetChildByRole (TrueRole); } + set { SetChildByRole (TrueRole, value); } + } + + public CSharpTokenNode ElseToken { + get { return GetChildByRole (ElseKeywordRole); } + } + + public Statement FalseStatement { + get { return GetChildByRole (FalseRole); } + set { SetChildByRole (FalseRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIfElseStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIfElseStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIfElseStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IfElseStatement o = other as IfElseStatement; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.TrueStatement.DoMatch(o.TrueStatement, match) && this.FalseStatement.DoMatch(o.FalseStatement, match); + } + + public IfElseStatement() + { + } + + public IfElseStatement(Expression condition, Statement trueStatement, Statement falseStatement = null) + { + this.Condition = condition; + this.TrueStatement = trueStatement; + this.FalseStatement = falseStatement; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LabelStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LabelStatement.cs new file mode 100644 index 000000000..43d22cea7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LabelStatement.cs @@ -0,0 +1,73 @@ +// +// LabelStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Label: + /// + public class LabelStatement : Statement + { + public string Label { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier LabelToken { + get { return GetChildByRole (Roles.Identifier); } + set { SetChildByRole (Roles.Identifier, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLabelStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLabelStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLabelStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LabelStatement o = other as LabelStatement; + return o != null && MatchString(this.Label, o.Label); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LockStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LockStatement.cs new file mode 100644 index 000000000..e59f99308 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LockStatement.cs @@ -0,0 +1,79 @@ +// +// LockStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// lock (Expression) EmbeddedStatement; + /// + public class LockStatement : Statement + { + public static readonly TokenRole LockKeywordRole = new TokenRole ("lock"); + + public CSharpTokenNode LockToken { + get { return GetChildByRole (LockKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLockStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLockStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLockStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LockStatement o = other as LockStatement; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ReturnStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ReturnStatement.cs new file mode 100644 index 000000000..0970bce43 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ReturnStatement.cs @@ -0,0 +1,79 @@ +// +// ReturnStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// return Expression; + /// + public class ReturnStatement : Statement + { + public static readonly TokenRole ReturnKeywordRole = new TokenRole ("return"); + + public CSharpTokenNode ReturnToken { + get { return GetChildByRole (ReturnKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public ReturnStatement () + { + } + + public ReturnStatement (Expression returnExpression) + { + AddChild (returnExpression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitReturnStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitReturnStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitReturnStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ReturnStatement o = other as ReturnStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/Statement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/Statement.cs new file mode 100644 index 000000000..24d3ede92 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/Statement.cs @@ -0,0 +1,132 @@ +// Copyright (c) 2010-2013 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 +{ + /// + /// Base class for statements. + /// + /// + /// This class is useful even though it doesn't provide any additional functionality: + /// It can be used to communicate more information in APIs, e.g. "this subnode will always be a statement" + /// + public abstract class Statement : AstNode + { + #region Null + public new static readonly Statement Null = new NullStatement (); + + sealed class NullStatement : Statement + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator Statement(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : Statement, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public new Statement Clone() + { + return (Statement)base.Clone(); + } + + public Statement ReplaceWith(Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException("replaceFunction"); + return (Statement)base.ReplaceWith(node => replaceFunction((Statement)node)); + } + + public override NodeType NodeType { + get { return NodeType.Statement; } + } + + public static implicit operator Statement (Expression type) + { + return new ExpressionStatement(type); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/SwitchStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/SwitchStatement.cs new file mode 100644 index 000000000..fa8e80cd5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/SwitchStatement.cs @@ -0,0 +1,230 @@ +// +// SwitchStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// switch (Expression) { SwitchSections } + /// + public class SwitchStatement : Statement + { + public static readonly TokenRole SwitchKeywordRole = new TokenRole ("switch"); + public static readonly Role SwitchSectionRole = new Role("SwitchSection"); + + public CSharpTokenNode SwitchToken { + get { return GetChildByRole (SwitchKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection SwitchSections { + get { return GetChildrenByRole (SwitchSectionRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSwitchStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSwitchStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSwitchStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SwitchStatement o = other as SwitchStatement; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.SwitchSections.DoMatch(o.SwitchSections, match); + } + } + + public class SwitchSection : AstNode + { + #region PatternPlaceholder + public static implicit operator SwitchSection(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : SwitchSection, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public static readonly Role CaseLabelRole = new Role("CaseLabel"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstNodeCollection CaseLabels { + get { return GetChildrenByRole (CaseLabelRole); } + } + + public AstNodeCollection Statements { + get { return GetChildrenByRole (Roles.EmbeddedStatement); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSwitchSection (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSwitchSection (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSwitchSection (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SwitchSection o = other as SwitchSection; + return o != null && this.CaseLabels.DoMatch(o.CaseLabels, match) && this.Statements.DoMatch(o.Statements, match); + } + } + + public class CaseLabel : AstNode + { + public static readonly TokenRole CaseKeywordRole = new TokenRole ("case"); + public static readonly TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + /// + /// Gets or sets the expression. The expression can be null - if the expression is null, it's the default switch section. + /// + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public CaseLabel () + { + } + + public CaseLabel (Expression expression) + { + this.Expression = expression; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCaseLabel (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCaseLabel (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCaseLabel (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CaseLabel o = other as CaseLabel; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ThrowStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ThrowStatement.cs new file mode 100644 index 000000000..98e27d1e7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ThrowStatement.cs @@ -0,0 +1,79 @@ +// +// ThrowStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// throw Expression; + /// + public class ThrowStatement : Statement + { + public static readonly TokenRole ThrowKeywordRole = new TokenRole ("throw"); + + public CSharpTokenNode ThrowToken { + get { return GetChildByRole (ThrowKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public ThrowStatement () + { + } + + public ThrowStatement (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitThrowStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitThrowStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitThrowStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ThrowStatement o = other as ThrowStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/TryCatchStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/TryCatchStatement.cs new file mode 100644 index 000000000..3611574b7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/TryCatchStatement.cs @@ -0,0 +1,241 @@ +// +// TryCatchStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// try TryBlock CatchClauses finally FinallyBlock + /// + public class TryCatchStatement : Statement + { + public static readonly TokenRole TryKeywordRole = new TokenRole ("try"); + public static readonly Role TryBlockRole = new Role("TryBlock", BlockStatement.Null); + public static readonly Role CatchClauseRole = new Role("CatchClause", CatchClause.Null); + public static readonly TokenRole FinallyKeywordRole = new TokenRole ("finally"); + public static readonly Role FinallyBlockRole = new Role("FinallyBlock", BlockStatement.Null); + + public CSharpTokenNode TryToken { + get { return GetChildByRole (TryKeywordRole); } + } + + public BlockStatement TryBlock { + get { return GetChildByRole (TryBlockRole); } + set { SetChildByRole (TryBlockRole, value); } + } + + public AstNodeCollection CatchClauses { + get { return GetChildrenByRole (CatchClauseRole); } + } + + public CSharpTokenNode FinallyToken { + get { return GetChildByRole (FinallyKeywordRole); } + } + + public BlockStatement FinallyBlock { + get { return GetChildByRole (FinallyBlockRole); } + set { SetChildByRole (FinallyBlockRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTryCatchStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTryCatchStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTryCatchStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TryCatchStatement o = other as TryCatchStatement; + return o != null && this.TryBlock.DoMatch(o.TryBlock, match) && this.CatchClauses.DoMatch(o.CatchClauses, match) && this.FinallyBlock.DoMatch(o.FinallyBlock, match); + } + } + + /// + /// catch (Type VariableName) { Body } + /// + public class CatchClause : AstNode + { + public static readonly TokenRole CatchKeywordRole = new TokenRole ("catch"); + + #region Null + public new static readonly CatchClause Null = new NullCatchClause (); + + sealed class NullCatchClause : CatchClause + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator CatchClause(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : CatchClause, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode CatchToken { + get { return GetChildByRole (CatchKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string VariableName { + get { return GetChildByRole (Roles.Identifier).Name; } + set { + if (string.IsNullOrEmpty(value)) + SetChildByRole (Roles.Identifier, null); + else + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier VariableNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCatchClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCatchClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCatchClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CatchClause o = other as CatchClause; + return o != null && this.Type.DoMatch(o.Type, match) && MatchString(this.VariableName, o.VariableName) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UncheckedStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UncheckedStatement.cs new file mode 100644 index 000000000..765cd9ab3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UncheckedStatement.cs @@ -0,0 +1,75 @@ +// +// UncheckedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unchecked BodyBlock + /// + public class UncheckedStatement : Statement + { + public static readonly TokenRole UncheckedKeywordRole = new TokenRole ("unchecked"); + + public CSharpTokenNode UncheckedToken { + get { return GetChildByRole (UncheckedKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public UncheckedStatement () + { + } + + public UncheckedStatement (BlockStatement body) + { + AddChild (body, Roles.Body); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUncheckedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUncheckedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUncheckedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UncheckedStatement o = other as UncheckedStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UnsafeStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UnsafeStatement.cs new file mode 100644 index 000000000..fa6421ae6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UnsafeStatement.cs @@ -0,0 +1,66 @@ +// +// UnsafeStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unsafe { Body } + /// + public class UnsafeStatement : Statement + { + public static readonly TokenRole UnsafeKeywordRole = new TokenRole ("unsafe"); + + public CSharpTokenNode UnsafeToken { + get { return GetChildByRole (UnsafeKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUnsafeStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUnsafeStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUnsafeStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UnsafeStatement o = other as UnsafeStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UsingStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UsingStatement.cs new file mode 100644 index 000000000..c87304675 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UsingStatement.cs @@ -0,0 +1,83 @@ +// +// UsingStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using (ResourceAcquisition) EmbeddedStatement + /// + public class UsingStatement : Statement + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role ResourceAcquisitionRole = new Role("ResourceAcquisition", AstNode.Null); + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + /// + /// Either a VariableDeclarationStatement, or an Expression. + /// + public AstNode ResourceAcquisition { + get { return GetChildByRole (ResourceAcquisitionRole); } + set { SetChildByRole (ResourceAcquisitionRole, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingStatement o = other as UsingStatement; + return o != null && this.ResourceAcquisition.DoMatch(o.ResourceAcquisition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/VariableDeclarationStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/VariableDeclarationStatement.cs new file mode 100644 index 000000000..32c141d96 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/VariableDeclarationStatement.cs @@ -0,0 +1,90 @@ +// +// VariableDeclarationStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class VariableDeclarationStatement : Statement + { + public static readonly Role ModifierRole = EntityDeclaration.ModifierRole; + + public VariableDeclarationStatement() + { + } + + public VariableDeclarationStatement(AstType type, string name, Expression initializer = null) + { + this.Type = type; + this.Variables.Add(new VariableInitializer(name, initializer)); + } + + public Modifiers Modifiers { + get { return EntityDeclaration.GetModifiers(this); } + set { EntityDeclaration.SetModifiers(this, value); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public VariableInitializer GetVariable (string name) + { + return Variables.FirstOrNullObject (vi => vi.Name == name); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitVariableDeclarationStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitVariableDeclarationStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitVariableDeclarationStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + VariableDeclarationStatement o = other as VariableDeclarationStatement; + return o != null && this.Modifiers == o.Modifiers && this.Type.DoMatch(o.Type, match) && this.Variables.DoMatch(o.Variables, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/WhileStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/WhileStatement.cs new file mode 100644 index 000000000..e38daa144 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/WhileStatement.cs @@ -0,0 +1,89 @@ +// +// WhileStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// "while (Condition) EmbeddedStatement" + /// + public class WhileStatement : Statement + { + public static readonly TokenRole WhileKeywordRole = new TokenRole ("while"); + + public CSharpTokenNode WhileToken { + get { return GetChildByRole (WhileKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitWhileStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitWhileStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitWhileStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + WhileStatement o = other as WhileStatement; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + + public WhileStatement() + { + } + + public WhileStatement(Expression condition, Statement embeddedStatement) + { + this.Condition = condition; + this.EmbeddedStatement = embeddedStatement; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldBreakStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldBreakStatement.cs new file mode 100644 index 000000000..ea5cac4a6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldBreakStatement.cs @@ -0,0 +1,70 @@ +// +// YieldBreakStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// yield break; + /// + public class YieldBreakStatement : Statement + { + public static readonly TokenRole YieldKeywordRole = new TokenRole ("yield"); + public static readonly TokenRole BreakKeywordRole = new TokenRole ("break"); + + public CSharpTokenNode YieldToken { + get { return GetChildByRole (YieldKeywordRole); } + } + + public CSharpTokenNode BreakToken { + get { return GetChildByRole (BreakKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitYieldBreakStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitYieldBreakStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitYieldBreakStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + YieldBreakStatement o = other as YieldBreakStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldReturnStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldReturnStatement.cs new file mode 100644 index 000000000..6539bf0c0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldReturnStatement.cs @@ -0,0 +1,75 @@ +// +// YieldStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// yield return Expression; + /// + public class YieldReturnStatement : Statement + { + public static readonly TokenRole YieldKeywordRole = new TokenRole ("yield"); + public static readonly TokenRole ReturnKeywordRole = new TokenRole ("return"); + + public CSharpTokenNode YieldToken { + get { return GetChildByRole (YieldKeywordRole); } + } + + public CSharpTokenNode ReturnToken { + get { return GetChildByRole (ReturnKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitYieldReturnStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitYieldReturnStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitYieldReturnStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + YieldReturnStatement o = other as YieldReturnStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs new file mode 100644 index 000000000..bac28bb76 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2013 Daniel Grunwald +// +// 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 +{ + /// + /// Extension methods for the syntax tree. + /// + public static class SyntaxExtensions + { + public static bool IsComparisonOperator(this OperatorType operatorType) + { + switch (operatorType) { + case OperatorType.Equality: + case OperatorType.Inequality: + case OperatorType.GreaterThan: + case OperatorType.LessThan: + case OperatorType.GreaterThanOrEqual: + case OperatorType.LessThanOrEqual: + return true; + default: + return false; + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs new file mode 100644 index 000000000..6ac536159 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs @@ -0,0 +1,193 @@ +// +// SyntaxTree.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; +using System.Threading; +using Mono.CSharp; +using System.IO; +using ICSharpCode.NRefactory.Editor; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Obsolete("CompilationUnit was renamed to SyntaxTree", true)] + public class CompilationUnit {} + + public class SyntaxTree : AstNode + { + public static readonly Role MemberRole = new Role("Member", AstNode.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + string fileName; + + /// + /// Gets/Sets the file name of this syntax tree. + /// + public string FileName { + get { return fileName; } + set { + ThrowIfFrozen(); + fileName = value; + } + } + + public AstNodeCollection Members { + get { return GetChildrenByRole(MemberRole); } + } + + IList conditionalSymbols = null; + + List errors = new List (); + + public List Errors { + get { return errors; } + } + + + /// + /// Gets the conditional symbols used to parse the source file. Note that this list contains + /// the conditional symbols at the start of the first token in the file - including the ones defined + /// in the source file. + /// + public IList ConditionalSymbols { + get { + return conditionalSymbols ?? EmptyList.Instance; + } + internal set { + conditionalSymbols = value; + } + } + + /// + /// Gets the expression that was on top of the parse stack. + /// This is the only way to get an expression that isn't part of a statment. + /// (eg. when an error follows an expression). + /// + /// This is used for code completion to 'get the expression before a token - like ., <, ('. + /// + public AstNode TopExpression { + get; + internal set; + } + + public SyntaxTree () + { + } + + /// + /// 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 || curNode is DelegateDeclaration) { + yield return (EntityDeclaration)curNode; + } + foreach (var child in curNode.Children) { + if (!(child is Statement || child is Expression) && + (child.Role != Roles.TypeMemberRole || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) + nodeStack.Push (child); + } + } + } + + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SyntaxTree o = other as SyntaxTree; + return o != null && this.Members.DoMatch(o.Members, match); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSyntaxTree (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSyntaxTree (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSyntaxTree (this, data); + } + + /// + /// Converts this syntax tree into a parsed file that can be stored in the type system. + /// + public CSharpUnresolvedFile ToTypeSystem () + { + if (string.IsNullOrEmpty (this.FileName)) + throw new InvalidOperationException ("Cannot use ToTypeSystem() on a syntax tree without file name."); + var v = new TypeSystemConvertVisitor (this.FileName); + v.VisitSyntaxTree (this); + return v.UnresolvedFile; + } + + public static SyntaxTree Parse (string program, string fileName = "", CompilerSettings settings = null, CancellationToken cancellationToken = default (CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + var parser = new CSharpParser (settings); + return parser.Parse (program, fileName); + } + + public static SyntaxTree Parse (TextReader reader, string fileName = "", CompilerSettings settings = null, CancellationToken cancellationToken = default (CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + var parser = new CSharpParser (settings); + return parser.Parse (reader, fileName); + } + + public static SyntaxTree Parse (Stream stream, string fileName = "", CompilerSettings settings = null, CancellationToken cancellationToken = default (CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + var parser = new CSharpParser (settings); + return parser.Parse (stream, fileName); + } + + public static SyntaxTree Parse (ITextSource textSource, string fileName = "", CompilerSettings settings = null, CancellationToken cancellationToken = default (CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + var parser = new CSharpParser (settings); + return parser.Parse (textSource, fileName); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TokenRole.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TokenRole.cs new file mode 100644 index 000000000..8c9c7392a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TokenRole.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A specific role only used for C# tokens + /// + public sealed class TokenRole : Role + { + internal readonly static List Tokens = new List (); + internal readonly static List TokenLengths = new List (); + internal readonly uint TokenIndex; + + static TokenRole () + { + // null token + Tokens.Add (""); + TokenLengths.Add (0); + } + + /// + /// Gets the token as string. Note that the token Name and Token value may differ. + /// + public string Token { + get; + private set; + } + + /// + /// Gets the char length of the token. + /// + public int Length { + get; + private set; + } + + + public TokenRole(string token) : base (token, CSharpTokenNode.Null) + { + this.Token = token; + this.Length = token.Length; + + bool found = false; + for (int i = 0; i < Tokens.Count; i++) { + var existingToken = Tokens [i]; + if (existingToken == token) { + TokenIndex = (uint)i; + found = true; + break; + } + } + if (!found) { + TokenIndex = (uint)Tokens.Count; + Tokens.Add (token); + TokenLengths.Add (this.Length); + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/Accessor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/Accessor.cs new file mode 100644 index 000000000..8bd18c477 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/Accessor.cs @@ -0,0 +1,117 @@ +// +// PropertyDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// get/set/add/remove + /// + public class Accessor : EntityDeclaration + { + public static readonly new Accessor Null = new NullAccessor (); + sealed class NullAccessor : Accessor + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.Method; } + } + + /// + /// Gets the 'get'/'set'/'add'/'remove' keyword + /// + public CSharpTokenNode Keyword { + get { + for (AstNode child = this.FirstChild; child != null; child = child.NextSibling) { + if (child.Role == PropertyDeclaration.GetKeywordRole || child.Role == PropertyDeclaration.SetKeywordRole + || child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole) + { + return (CSharpTokenNode)child; + } + } + return CSharpTokenNode.Null; + } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAccessor (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAccessor (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitAccessor (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Accessor o = other as Accessor; + return o != null && !o.IsNull && this.MatchAttributesAndModifiers(o, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ConstructorDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ConstructorDeclaration.cs new file mode 100644 index 000000000..23a973a5c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ConstructorDeclaration.cs @@ -0,0 +1,190 @@ +// +// ConstructorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class ConstructorDeclaration : EntityDeclaration + { + public static readonly Role InitializerRole = new Role("Initializer", ConstructorInitializer.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Constructor; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public ConstructorInitializer Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole( InitializerRole, value); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstructorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstructorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstructorDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConstructorDeclaration o = other as ConstructorDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) && this.Parameters.DoMatch(o.Parameters, match) + && this.Initializer.DoMatch(o.Initializer, match) && this.Body.DoMatch(o.Body, match); + } + } + + public enum ConstructorInitializerType { + Any, + Base, + This + } + + public class ConstructorInitializer : AstNode + { + public static readonly TokenRole BaseKeywordRole = new TokenRole ("base"); + public static readonly TokenRole ThisKeywordRole = new TokenRole ("this"); + + public static readonly new ConstructorInitializer Null = new NullConstructorInitializer (); + class NullConstructorInitializer : ConstructorInitializer + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public ConstructorInitializerType ConstructorInitializerType { + get; + set; + } + + public CSharpTokenNode Keyword { + get { + if (ConstructorInitializerType == ConstructorInitializerType.Base) + return GetChildByRole(BaseKeywordRole); + else + return GetChildByRole(ThisKeywordRole); + } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstructorInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstructorInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstructorInitializer (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConstructorInitializer o = other as ConstructorInitializer; + return o != null && !o.IsNull + && (this.ConstructorInitializerType == ConstructorInitializerType.Any || this.ConstructorInitializerType == o.ConstructorInitializerType) + && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/DestructorDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/DestructorDeclaration.cs new file mode 100644 index 000000000..0609e5dc6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/DestructorDeclaration.cs @@ -0,0 +1,76 @@ +// +// DestructorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class DestructorDeclaration : EntityDeclaration + { + public static readonly TokenRole TildeRole = new TokenRole ("~"); + + public CSharpTokenNode TildeToken { + get { return GetChildByRole (TildeRole); } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.Destructor; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDestructorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDestructorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDestructorDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DestructorDeclaration o = other as DestructorDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs new file mode 100644 index 000000000..c02ff21b6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs @@ -0,0 +1,117 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class EntityDeclaration : AstNode + { + public static readonly Role AttributeRole = new Role("Attribute"); + public static readonly Role UnattachedAttributeRole = new Role("UnattachedAttribute"); + public static readonly Role ModifierRole = new Role("Modifier"); + public static readonly Role PrivateImplementationTypeRole = new Role("PrivateImplementationType", AstType.Null); + + public override NodeType NodeType { + get { return NodeType.Member; } + } + + public abstract NRefactory.TypeSystem.SymbolKind SymbolKind { get; } + + public AstNodeCollection Attributes { + get { return base.GetChildrenByRole (AttributeRole); } + } + + public Modifiers Modifiers { + get { return GetModifiers(this); } + set { SetModifiers(this, value); } + } + + public bool HasModifier (Modifiers mod) + { + return (Modifiers & mod) == mod; + } + + public IEnumerable ModifierTokens { + get { return GetChildrenByRole (ModifierRole); } + } + + public virtual string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value, TextLocation.Empty)); + } + } + + public virtual Identifier NameToken { + get { return GetChildByRole (Roles.Identifier); } + set { SetChildByRole (Roles.Identifier, value); } + } + + public virtual AstType ReturnType { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + internal static Modifiers GetModifiers(AstNode node) + { + Modifiers m = 0; + foreach (CSharpModifierToken t in node.GetChildrenByRole (ModifierRole)) { + m |= t.Modifier; + } + return m; + } + + internal static void SetModifiers(AstNode node, Modifiers newValue) + { + Modifiers oldValue = GetModifiers(node); + AstNode insertionPos = node.GetChildrenByRole(AttributeRole).LastOrDefault(); + foreach (Modifiers m in CSharpModifierToken.AllModifiers) { + 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; + } else { + // Modifier already exists + 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(); + } + } + } + } + + protected bool MatchAttributesAndModifiers (EntityDeclaration o, PatternMatching.Match match) + { + return (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) && this.Attributes.DoMatch (o.Attributes, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs new file mode 100644 index 000000000..b7c924ab9 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs @@ -0,0 +1,72 @@ +// +// EnumMemberDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class EnumMemberDeclaration : EntityDeclaration + { + public static readonly Role InitializerRole = new Role("Initializer", Expression.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEnumMemberDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEnumMemberDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEnumMemberDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EnumMemberDeclaration o = other as EnumMemberDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && MatchString(this.Name, o.Name) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EventDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EventDeclaration.cs new file mode 100644 index 000000000..d543f9ea7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EventDeclaration.cs @@ -0,0 +1,152 @@ +// +// EventDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.ComponentModel; + +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class EventDeclaration : EntityDeclaration + { + public static readonly TokenRole EventKeywordRole = new TokenRole ("event"); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Event; } + } + + public CSharpTokenNode EventToken { + get { return GetChildByRole (EventKeywordRole); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + // Hide .Name and .NameToken from users; the actual field names + // are stored in the VariableInitializer. + [EditorBrowsable(EditorBrowsableState.Never)] + public override string Name { + get { return string.Empty; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEventDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEventDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEventDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EventDeclaration o = other as EventDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); + } + } + + public class CustomEventDeclaration : EntityDeclaration + { + public static readonly TokenRole EventKeywordRole = new TokenRole ("event"); + public static readonly TokenRole AddKeywordRole = new TokenRole ("add"); + public static readonly TokenRole RemoveKeywordRole = new TokenRole ("remove"); + + public static readonly Role AddAccessorRole = new Role("AddAccessor", Accessor.Null); + public static readonly Role RemoveAccessorRole = new Role("RemoveAccessor", Accessor.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Event; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor AddAccessor { + get { return GetChildByRole (AddAccessorRole); } + set { SetChildByRole (AddAccessorRole, value); } + } + + public Accessor RemoveAccessor { + get { return GetChildByRole (RemoveAccessorRole); } + set { SetChildByRole (RemoveAccessorRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCustomEventDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCustomEventDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCustomEventDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CustomEventDeclaration o = other as CustomEventDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.AddAccessor.DoMatch(o.AddAccessor, match) && this.RemoveAccessor.DoMatch(o.RemoveAccessor, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FieldDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FieldDeclaration.cs new file mode 100644 index 000000000..de220ecd7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FieldDeclaration.cs @@ -0,0 +1,79 @@ +// +// FieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class FieldDeclaration : EntityDeclaration + { + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + // Hide .Name and .NameToken from users; the actual field names + // are stored in the VariableInitializer. + [EditorBrowsable(EditorBrowsableState.Never)] + public override string Name { + get { return string.Empty; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFieldDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFieldDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFieldDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + FieldDeclaration o = other as FieldDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs new file mode 100644 index 000000000..fea2a2af2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs @@ -0,0 +1,71 @@ +// +// FixedFieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class FixedFieldDeclaration : EntityDeclaration + { + public static readonly TokenRole FixedKeywordRole = new TokenRole ("fixed"); + public static readonly Role VariableRole = new Role ("FixedVariable"); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public CSharpTokenNode FixedToken { + get { return GetChildByRole (FixedKeywordRole); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (VariableRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedFieldDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedFieldDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedFieldDeclaration (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as FixedFieldDeclaration; + return o != null && this.MatchAttributesAndModifiers (o, match) + && this.ReturnType.DoMatch (o.ReturnType, match) && this.Variables.DoMatch (o.Variables, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedVariableInitializer.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedVariableInitializer.cs new file mode 100644 index 000000000..2c320a826 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedVariableInitializer.cs @@ -0,0 +1,105 @@ +// +// FixedFieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// 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 +{ + /// + /// Name [ CountExpression ] + /// + public class FixedVariableInitializer : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public FixedVariableInitializer() + { + } + + public FixedVariableInitializer (string name, Expression initializer = null) + { + this.Name = name; + this.CountExpression = initializer; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public Expression CountExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedVariableInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedVariableInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedVariableInitializer (this, data); + } + + protected internal override bool DoMatch (AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + var o = other as FixedVariableInitializer; + return o != null && MatchString (this.Name, o.Name) && this.CountExpression.DoMatch (o.CountExpression, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs new file mode 100644 index 000000000..56156dd19 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs @@ -0,0 +1,122 @@ +// +// IndexerDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class IndexerDeclaration : EntityDeclaration + { + public static readonly TokenRole ThisKeywordRole = new TokenRole ("this"); + public static readonly Role GetterRole = PropertyDeclaration.GetterRole; + public static readonly Role SetterRole = PropertyDeclaration.SetterRole; + + public override SymbolKind SymbolKind { + get { return SymbolKind.Indexer; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public override string Name { + get { return "Item"; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public CSharpTokenNode ThisToken { + get { return GetChildByRole (ThisKeywordRole); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor Getter { + get { return GetChildByRole(GetterRole); } + set { SetChildByRole(GetterRole, value); } + } + + public Accessor Setter { + get { return GetChildByRole(SetterRole); } + set { SetChildByRole(SetterRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIndexerDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIndexerDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIndexerDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IndexerDeclaration o = other as IndexerDeclaration; + return o != null + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.Parameters.DoMatch(o.Parameters, match) + && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/MethodDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/MethodDeclaration.cs new file mode 100644 index 000000000..90aaa3047 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/MethodDeclaration.cs @@ -0,0 +1,104 @@ +// +// MethodDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class MethodDeclaration : EntityDeclaration + { + public override SymbolKind SymbolKind { + get { return SymbolKind.Method; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole (Roles.Constraint); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public bool IsExtensionMethod { + get { + ParameterDeclaration pd = (ParameterDeclaration)GetChildByRole (Roles.Parameter); + return pd != null && pd.ParameterModifier == ParameterModifier.This; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMethodDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMethodDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMethodDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MethodDeclaration o = other as MethodDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.TypeParameters.DoMatch(o.TypeParameters, match) + && this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match) + && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs new file mode 100644 index 000000000..b4ae52a2a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs @@ -0,0 +1,268 @@ +// +// OperatorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum OperatorType + { + // Values must correspond to Mono.CSharp.Operator.OpType + // due to the casts used in OperatorDeclaration. + + // Unary operators + LogicalNot = Mono.CSharp.Operator.OpType.LogicalNot, + OnesComplement = Mono.CSharp.Operator.OpType.OnesComplement, + Increment = Mono.CSharp.Operator.OpType.Increment, + Decrement = Mono.CSharp.Operator.OpType.Decrement, + True = Mono.CSharp.Operator.OpType.True, + False = Mono.CSharp.Operator.OpType.False, + + // Unary and Binary operators + Addition = Mono.CSharp.Operator.OpType.Addition, + Subtraction = Mono.CSharp.Operator.OpType.Subtraction, + + UnaryPlus = Mono.CSharp.Operator.OpType.UnaryPlus, + UnaryNegation = Mono.CSharp.Operator.OpType.UnaryNegation, + + // Binary operators + Multiply = Mono.CSharp.Operator.OpType.Multiply, + Division = Mono.CSharp.Operator.OpType.Division, + Modulus = Mono.CSharp.Operator.OpType.Modulus, + BitwiseAnd = Mono.CSharp.Operator.OpType.BitwiseAnd, + BitwiseOr = Mono.CSharp.Operator.OpType.BitwiseOr, + ExclusiveOr = Mono.CSharp.Operator.OpType.ExclusiveOr, + LeftShift = Mono.CSharp.Operator.OpType.LeftShift, + RightShift = Mono.CSharp.Operator.OpType.RightShift, + Equality = Mono.CSharp.Operator.OpType.Equality, + Inequality = Mono.CSharp.Operator.OpType.Inequality, + GreaterThan = Mono.CSharp.Operator.OpType.GreaterThan, + LessThan = Mono.CSharp.Operator.OpType.LessThan, + GreaterThanOrEqual = Mono.CSharp.Operator.OpType.GreaterThanOrEqual, + LessThanOrEqual = Mono.CSharp.Operator.OpType.LessThanOrEqual, + + // Implicit and Explicit + Implicit = Mono.CSharp.Operator.OpType.Implicit, + Explicit = Mono.CSharp.Operator.OpType.Explicit + } + + public class OperatorDeclaration : EntityDeclaration + { + public static readonly TokenRole OperatorKeywordRole = new TokenRole ("operator"); + + // Unary operators + public static readonly TokenRole LogicalNotRole = new TokenRole ("!"); + public static readonly TokenRole OnesComplementRole = new TokenRole ("~"); + public static readonly TokenRole IncrementRole = new TokenRole ("++"); + public static readonly TokenRole DecrementRole = new TokenRole ("--"); + public static readonly TokenRole TrueRole = new TokenRole ("true"); + public static readonly TokenRole FalseRole = new TokenRole ("false"); + + // Unary and Binary operators + public static readonly TokenRole AdditionRole = new TokenRole ("+"); + public static readonly TokenRole SubtractionRole = new TokenRole ("-"); + + // Binary operators + public static readonly TokenRole MultiplyRole = new TokenRole ("*"); + public static readonly TokenRole DivisionRole = new TokenRole ("/"); + public static readonly TokenRole ModulusRole = new TokenRole ("%"); + public static readonly TokenRole BitwiseAndRole = new TokenRole ("&"); + public static readonly TokenRole BitwiseOrRole = new TokenRole ("|"); + public static readonly TokenRole ExclusiveOrRole = new TokenRole ("^"); + public static readonly TokenRole LeftShiftRole = new TokenRole ("<<"); + public static readonly TokenRole RightShiftRole = new TokenRole (">>"); + public static readonly TokenRole EqualityRole = new TokenRole ("=="); + public static readonly TokenRole InequalityRole = new TokenRole ("!="); + public static readonly TokenRole GreaterThanRole = new TokenRole (">"); + public static readonly TokenRole LessThanRole = new TokenRole ("<"); + public static readonly TokenRole GreaterThanOrEqualRole = new TokenRole (">="); + public static readonly TokenRole LessThanOrEqualRole = new TokenRole ("<="); + + public static readonly TokenRole ExplicitRole = new TokenRole ("explicit"); + public static readonly TokenRole ImplicitRole = new TokenRole ("implicit"); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Operator; } + } + + OperatorType operatorType; + + public OperatorType OperatorType { + get { return operatorType; } + set { + ThrowIfFrozen(); + operatorType = value; + } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (OperatorKeywordRole); } + } + + public CSharpTokenNode OperatorTypeToken { + get { return GetChildByRole (GetRole (OperatorType)); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + /// + /// Gets the operator type from the method name, or null, if the method does not represent one of the known operator types. + /// + public static OperatorType? GetOperatorType(string methodName) + { + return (OperatorType?)Mono.CSharp.Operator.GetType(methodName); + } + + public static TokenRole GetRole (OperatorType type) + { + switch (type) { + case OperatorType.LogicalNot: + return LogicalNotRole; + case OperatorType.OnesComplement: + return OnesComplementRole; + case OperatorType.Increment: + return IncrementRole; + case OperatorType.Decrement: + return DecrementRole; + case OperatorType.True: + return TrueRole; + case OperatorType.False: + return FalseRole; + + case OperatorType.Addition: + case OperatorType.UnaryPlus: + return AdditionRole; + case OperatorType.Subtraction: + case OperatorType.UnaryNegation: + return SubtractionRole; + + case OperatorType.Multiply: + return MultiplyRole; + case OperatorType.Division: + return DivisionRole; + case OperatorType.Modulus: + return ModulusRole; + case OperatorType.BitwiseAnd: + return BitwiseAndRole; + case OperatorType.BitwiseOr: + return BitwiseOrRole; + case OperatorType.ExclusiveOr: + return ExclusiveOrRole; + case OperatorType.LeftShift: + return LeftShiftRole; + case OperatorType.RightShift: + return RightShiftRole; + case OperatorType.Equality: + return EqualityRole; + case OperatorType.Inequality: + return InequalityRole; + case OperatorType.GreaterThan: + return GreaterThanRole; + case OperatorType.LessThan: + return LessThanRole; + case OperatorType.GreaterThanOrEqual: + return GreaterThanOrEqualRole; + case OperatorType.LessThanOrEqual: + return LessThanOrEqualRole; + + case OperatorType.Implicit: + return ImplicitRole; + case OperatorType.Explicit: + return ExplicitRole; + + default: + throw new System.ArgumentOutOfRangeException (); + } + } + + /// + /// Gets the method name for the operator type. ("op_Addition", "op_Implicit", etc.) + /// + public static string GetName (OperatorType type) + { + return Mono.CSharp.Operator.GetMetadataName ((Mono.CSharp.Operator.OpType)type); + } + + /// + /// Gets the token for the operator type ("+", "implicit", etc.) + /// + public static string GetToken (OperatorType type) + { + return Mono.CSharp.Operator.GetName ((Mono.CSharp.Operator.OpType)type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitOperatorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitOperatorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitOperatorDeclaration (this, data); + } + + public override string Name { + get { return GetName (this.OperatorType); } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + OperatorDeclaration o = other as OperatorDeclaration; + return o != null && this.MatchAttributesAndModifiers (o, match) && this.OperatorType == o.OperatorType + && this.ReturnType.DoMatch (o.ReturnType, match) + && this.Parameters.DoMatch (o.Parameters, match) && this.Body.DoMatch (o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs new file mode 100644 index 000000000..cc69ff1fd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs @@ -0,0 +1,147 @@ +// +// ParameterDeclarationExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum ParameterModifier { + None, + Ref, + Out, + Params, + This + } + + public class ParameterDeclaration : AstNode + { + public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; + public static readonly TokenRole RefModifierRole = new TokenRole("ref"); + public static readonly TokenRole OutModifierRole = new TokenRole("out"); + public static readonly TokenRole ParamsModifierRole = new TokenRole("params"); + public static readonly TokenRole ThisModifierRole = new TokenRole("this"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstNodeCollection Attributes { + get { return GetChildrenByRole (AttributeRole); } + } + + ParameterModifier parameterModifier; + + public ParameterModifier ParameterModifier { + get { return parameterModifier; } + set { + ThrowIfFrozen(); + parameterModifier = value; + } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression DefaultExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitParameterDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitParameterDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitParameterDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ParameterDeclaration o = other as ParameterDeclaration; + return o != null && this.Attributes.DoMatch(o.Attributes, match) && this.ParameterModifier == o.ParameterModifier + && this.Type.DoMatch(o.Type, match) && MatchString(this.Name, o.Name) + && this.DefaultExpression.DoMatch(o.DefaultExpression, match); + } + + public ParameterDeclaration() + { + } + + public ParameterDeclaration(AstType type, string name, ParameterModifier modifier = ParameterModifier.None) + { + Type = type; + Name = name; + ParameterModifier = modifier; + } + + public ParameterDeclaration(string name, ParameterModifier modifier = ParameterModifier.None) + { + Name = name; + ParameterModifier = modifier; + } + + public new ParameterDeclaration Clone() + { + return (ParameterDeclaration)base.Clone(); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/PropertyDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/PropertyDeclaration.cs new file mode 100644 index 000000000..1f137e0c9 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/PropertyDeclaration.cs @@ -0,0 +1,92 @@ +// +// PropertyDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class PropertyDeclaration : EntityDeclaration + { + public static readonly TokenRole GetKeywordRole = new TokenRole ("get"); + public static readonly TokenRole SetKeywordRole = new TokenRole ("set"); + public static readonly Role GetterRole = new Role("Getter", Accessor.Null); + public static readonly Role SetterRole = new Role("Setter", Accessor.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Property; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor Getter { + get { return GetChildByRole(GetterRole); } + set { SetChildByRole(GetterRole, value); } + } + + public Accessor Setter { + get { return GetChildByRole(SetterRole); } + set { SetChildByRole(SetterRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPropertyDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPropertyDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPropertyDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PropertyDeclaration o = other as PropertyDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/VariableInitializer.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/VariableInitializer.cs new file mode 100644 index 000000000..dbf4bbe3d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/VariableInitializer.cs @@ -0,0 +1,174 @@ +// +// VariableInitializer.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public class VariableInitializer : AstNode + { + #region Null + public new static readonly VariableInitializer Null = new NullVariableInitializer (); + + sealed class NullVariableInitializer : VariableInitializer + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator VariableInitializer(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : VariableInitializer, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public VariableInitializer() + { + } + + public VariableInitializer(string name, Expression initializer = null) + { + this.Name = name; + this.Initializer = initializer; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Initializer { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitVariableInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitVariableInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitVariableInitializer (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + VariableInitializer o = other as VariableInitializer; + return o != null && MatchString(this.Name, o.Name) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs new file mode 100644 index 000000000..ae0ba716e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs @@ -0,0 +1,289 @@ +// Copyright (c) 2010-2013 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.Runtime.Serialization; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Serializable] + public class CSharpProjectContent : IProjectContent + { + string assemblyName; + string fullAssemblyName; + string projectFileName; + string location; + Dictionary unresolvedFiles; + List assemblyReferences; + CompilerSettings compilerSettings; + + public CSharpProjectContent() + { + this.unresolvedFiles = new Dictionary(Platform.FileNameComparer); + this.assemblyReferences = new List(); + this.compilerSettings = new CompilerSettings(); + compilerSettings.Freeze(); + } + + 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); + this.assemblyReferences = new List(pc.assemblyReferences); + this.compilerSettings = pc.compilerSettings; + } + + public IEnumerable Files { + get { return unresolvedFiles.Values; } + } + + public IEnumerable AssemblyReferences { + get { return assemblyReferences; } + } + + public string ProjectFileName { + get { return projectFileName; } + } + + public string AssemblyName { + get { return assemblyName; } + } + + public string FullAssemblyName { + get { return fullAssemblyName; } + } + + public string Location { + get { return location; } + } + + public CompilerSettings CompilerSettings { + get { return compilerSettings; } + } + + object IProjectContent.CompilerSettings { + get { return compilerSettings; } + } + + public IEnumerable AssemblyAttributes { + get { + return this.Files.SelectMany(f => f.AssemblyAttributes); + } + } + + public IEnumerable ModuleAttributes { + get { + return this.Files.SelectMany(f => f.ModuleAttributes); + } + } + + public IEnumerable TopLevelTypeDefinitions { + get { + return this.Files.SelectMany(f => f.TopLevelTypeDefinitions); + } + } + + public IUnresolvedFile GetFile(string fileName) + { + IUnresolvedFile file; + if (unresolvedFiles.TryGetValue(fileName, out file)) + return file; + else + return null; + } + + public virtual ICompilation CreateCompilation() + { + var solutionSnapshot = new DefaultSolutionSnapshot(); + ICompilation compilation = new SimpleCompilation(solutionSnapshot, this, assemblyReferences); + solutionSnapshot.AddCompilation(this, compilation); + return compilation; + } + + public virtual ICompilation CreateCompilation(ISolutionSnapshot solutionSnapshot) + { + return new SimpleCompilation(solutionSnapshot, this, assemblyReferences); + } + + protected virtual CSharpProjectContent Clone() + { + 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.fullAssemblyName = newAssemblyName; + int pos = newAssemblyName != null ? newAssemblyName.IndexOf(',') : -1; + pc.assemblyName = pos < 0 ? newAssemblyName : newAssemblyName.Substring(0, pos); + return pc; + } + + public IProjectContent SetProjectFileName(string newProjectFileName) + { + CSharpProjectContent pc = Clone(); + pc.projectFileName = newProjectFileName; + return pc; + } + + public IProjectContent SetLocation(string newLocation) + { + CSharpProjectContent pc = Clone(); + pc.location = newLocation; + return pc; + } + + public IProjectContent SetCompilerSettings(object compilerSettings) + { + if (!(compilerSettings is CompilerSettings)) + throw new ArgumentException("Settings must be an instance of " + typeof(CompilerSettings).FullName, "compilerSettings"); + CSharpProjectContent pc = Clone(); + pc.compilerSettings = (CompilerSettings)compilerSettings; + pc.compilerSettings.Freeze(); + return pc; + } + + public IProjectContent AddAssemblyReferences(IEnumerable references) + { + return AddAssemblyReferences(references.ToArray()); + } + + public IProjectContent AddAssemblyReferences(params IAssemblyReference[] references) + { + CSharpProjectContent pc = Clone(); + pc.assemblyReferences.AddRange(references); + return pc; + } + + public IProjectContent RemoveAssemblyReferences(IEnumerable references) + { + return RemoveAssemblyReferences(references.ToArray()); + } + + public IProjectContent RemoveAssemblyReferences(params IAssemblyReference[] references) + { + CSharpProjectContent pc = Clone(); + foreach (var r in references) + pc.assemblyReferences.Remove(r); + return pc; + } + + /// + /// Adds the specified files to the project content. + /// If a file with the same name already exists, updated the existing file. + /// + public IProjectContent AddOrUpdateFiles(IEnumerable newFiles) + { + CSharpProjectContent pc = Clone(); + foreach (var file in newFiles) { + pc.unresolvedFiles[file.FileName] = file; + } + return pc; + } + + /// + /// Adds the specified files to the project content. + /// If a file with the same name already exists, this method updates the existing file. + /// + public IProjectContent AddOrUpdateFiles(params IUnresolvedFile[] newFiles) + { + return AddOrUpdateFiles((IEnumerable)newFiles); + } + + /// + /// Removes the files with the specified names. + /// + public IProjectContent RemoveFiles(IEnumerable fileNames) + { + CSharpProjectContent pc = Clone(); + foreach (var fileName in fileNames) { + pc.unresolvedFiles.Remove(fileName); + } + return pc; + } + + /// + /// Removes the files with the specified names. + /// + public IProjectContent RemoveFiles(params string[] fileNames) + { + return RemoveFiles((IEnumerable)fileNames); + } + + [Obsolete("Use RemoveFiles/AddOrUpdateFiles instead")] + public IProjectContent UpdateProjectContent(IUnresolvedFile oldFile, IUnresolvedFile newFile) + { + if (oldFile == null && newFile == null) + return this; + if (oldFile != null && newFile != null) { + if (!Platform.FileNameComparer.Equals(oldFile.FileName, newFile.FileName)) + throw new ArgumentException("When both oldFile and newFile are specified, they must use the same file name."); + } + CSharpProjectContent pc = Clone(); + if (newFile == null) + pc.unresolvedFiles.Remove(oldFile.FileName); + else + pc.unresolvedFiles[newFile.FileName] = newFile; + return pc; + } + + [Obsolete("Use RemoveFiles/AddOrUpdateFiles instead")] + public IProjectContent UpdateProjectContent(IEnumerable oldFiles, IEnumerable newFiles) + { + CSharpProjectContent pc = Clone(); + if (oldFiles != null) { + foreach (var oldFile in oldFiles) { + pc.unresolvedFiles.Remove(oldFile.FileName); + } + } + if (newFiles != null) { + foreach (var newFile in newFiles) { + pc.unresolvedFiles.Add(newFile.FileName, newFile); + } + } + return pc; + } + + IAssembly IAssemblyReference.Resolve(ITypeResolveContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + var cache = context.Compilation.CacheManager; + IAssembly asm = (IAssembly)cache.GetShared(this); + if (asm != null) { + return asm; + } else { + asm = new CSharpAssembly(context.Compilation, this); + return (IAssembly)cache.GetOrAddShared(this, asm); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/CombineQueryExpressions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/CombineQueryExpressions.cs new file mode 100644 index 000000000..5311d9a24 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/CombineQueryExpressions.cs @@ -0,0 +1,216 @@ +// +// CombineQueryExpressions.cs +// +// Modified by Luís Reis (Copyright (C) 2013) +// +// Copyright header of the original version follows: +// +// Copyright (c) 2011 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.Linq; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Combines query expressions and removes transparent identifiers. + /// + public class CombineQueryExpressions + { + static readonly InvocationExpression castPattern = new InvocationExpression { + Target = new MemberReferenceExpression { + Target = new AnyNode("inExpr"), + MemberName = "Cast", + TypeArguments = { new AnyNode("targetType") } + }}; + + public string CombineQuery(AstNode node, AstNode rootQuery = null) + { + if (rootQuery == null) { + rootQuery = node; + } + + QueryExpression query = node as QueryExpression; + if (query != null) { + string continuationIdentifier = null; + + foreach (var clause in query.Clauses) { + var continuation = clause as QueryContinuationClause; + if (continuation != null) { + CombineQuery(continuation.PrecedingQuery); + } + + var from = clause as QueryFromClause; + if (from != null) { + continuationIdentifier = CombineQuery(from.Expression, rootQuery); + } + } + + QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); + QueryExpression innerQuery = fromClause.Expression as QueryExpression; + if (innerQuery != null) { + continuationIdentifier = continuationIdentifier ?? ((QueryFromClause)innerQuery.Clauses.First()).Identifier; + + string transparentIdentifier; + if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery, continuationIdentifier, out transparentIdentifier)) { + RemoveTransparentIdentifierReferences(rootQuery, transparentIdentifier); + } else if (fromClause.Type.IsNull) { + QueryContinuationClause continuation = new QueryContinuationClause(); + continuation.PrecedingQuery = innerQuery.Detach(); + continuation.Identifier = fromClause.Identifier; + fromClause.ReplaceWith(continuation); + } + + return transparentIdentifier; + } else { + Match m = castPattern.Match(fromClause.Expression); + if (m.Success) { + fromClause.Type = m.Get("targetType").Single().Detach(); + fromClause.Expression = m.Get("inExpr").Single().Detach(); + } + } + } + + return null; + } + + static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause { + Expression = new AnonymousTypeCreateExpression { + Initializers = { + new AnyNode("nae1"), + new AnyNode("nae2") + } + } + }; + + bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery, string continuationIdentifier, out string transparentIdentifier) + { + transparentIdentifier = fromClause.Identifier; + + Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last()); + if (!match.Success) + return false; + QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last(); + Expression nae1 = match.Get("nae1").SingleOrDefault(); + string nae1Name = ExtractExpressionName(ref nae1); + if (nae1Name == null) + return false; + + Expression nae2 = match.Get("nae2").SingleOrDefault(); + string nae2Name = ExtractExpressionName(ref nae2); + if (nae1Name == null) + return false; + + bool introduceLetClause = true; + var nae1Identifier = nae1 as IdentifierExpression; + var nae2Identifier = nae2 as IdentifierExpression; + if (nae1Identifier != null && nae2Identifier != null && nae1Identifier.Identifier == nae1Name && nae2Identifier.Identifier == nae2Name) { + introduceLetClause = false; + } + + if (nae1Name != continuationIdentifier) { + if (nae2Name == continuationIdentifier) { + //Members are in reversed order + string tempName = nae1Name; + Expression tempNae = nae1; + + nae1Name = nae2Name; + nae1 = nae2; + nae2Name = tempName; + nae2 = tempNae; + } else { + return false; + } + } + + if (introduceLetClause && innerQuery.Clauses.OfType().Any(from => from.Identifier == nae2Name)) { + return false; + } + if (introduceLetClause && innerQuery.Clauses.OfType().Any(join => join.JoinIdentifier == nae2Name)) { + return false; + } + + // from * in (from x in ... select new { x = x, y = expr }) ... + // => + // from x in ... let y = expr ... + fromClause.Remove(); + selectClause.Remove(); + // Move clauses from innerQuery to query + QueryClause insertionPos = null; + foreach (var clause in innerQuery.Clauses) { + query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); + } + if (introduceLetClause) { + query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = nae2Name, Expression = nae2.Detach() }); + } + return true; + } + + /// + /// Removes all occurrences of transparent identifiers + /// + void RemoveTransparentIdentifierReferences(AstNode node, string transparentIdentifier) + { + foreach (AstNode child in node.Children) { + RemoveTransparentIdentifierReferences(child, transparentIdentifier); + } + MemberReferenceExpression mre = node as MemberReferenceExpression; + if (mre != null) { + IdentifierExpression ident = mre.Target as IdentifierExpression; + if (ident != null && ident.Identifier == transparentIdentifier) { + IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName); + mre.TypeArguments.MoveTo(newIdent.TypeArguments); + newIdent.CopyAnnotationsFrom(mre); + newIdent.RemoveAnnotations(); // remove the reference to the property of the anonymous type + mre.ReplaceWith(newIdent); + return; + } else if (mre.MemberName == transparentIdentifier) { + var newVar = mre.Target.Detach(); + newVar.CopyAnnotationsFrom(mre); + newVar.RemoveAnnotations(); // remove the reference to the property of the anonymous type + mre.ReplaceWith(newVar); + return; + } + } + } + + string ExtractExpressionName(ref Expression expr) + { + NamedExpression namedExpr = expr as NamedExpression; + if (namedExpr != null) { + expr = namedExpr.Expression; + return namedExpr.Name; + } + + IdentifierExpression identifier = expr as IdentifierExpression; + if (identifier != null) { + return identifier.Identifier; + } + + MemberReferenceExpression memberRef = expr as MemberReferenceExpression; + if (memberRef != null) { + return memberRef.MemberName; + } + + return null; + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs new file mode 100644 index 000000000..28c9ea02a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -0,0 +1,3807 @@ +// +// CSharpCompletionEngine.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com) +// +// 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.Text; +using ICSharpCode.NRefactory.Completion; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public enum EditorBrowsableBehavior + { + Ignore, + Normal, + IncludeAdvanced + } + + public class CompletionEngineCache + { + public List namespaces; + public ICompletionData[] importCompletion; + } + + public class CSharpCompletionEngine : CSharpCompletionEngineBase + { + internal ICompletionDataFactory factory; + + #region Additional input properties + + public CSharpFormattingOptions FormattingPolicy { get; set; } + + public string EolMarker { get; set; } + + public string IndentString { get; set; } + + public bool AutomaticallyAddImports { get; set; } + + public bool IncludeKeywordsInCompletionList { get; set; } + + public EditorBrowsableBehavior EditorBrowsableBehavior { get; set; } + + public CompletionEngineCache CompletionEngineCache { get; set; } + + #endregion + + #region Result properties + + public bool AutoCompleteEmptyMatch; + /// + /// The auto complete empty match on curly bracket. (only taken into account when AutoCompleteEmptyMatch is true ) + /// + public bool AutoCompleteEmptyMatchOnCurlyBracket = true; + public bool AutoSelect; + public string DefaultCompletionString; + public bool CloseOnSquareBrackets; + public readonly List PossibleDelegates = new List(); + + #endregion + + public CSharpCompletionEngine(IDocument document, ICompletionContextProvider completionContextProvider, ICompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx) : base(content, completionContextProvider, ctx) + { + if (document == null) { + throw new ArgumentNullException("document"); + } + if (factory == null) { + throw new ArgumentNullException("factory"); + } + this.document = document; + this.factory = factory; + // Set defaults for additional input properties + this.FormattingPolicy = FormattingOptionsFactory.CreateMono(); + this.EolMarker = Environment.NewLine; + this.IncludeKeywordsInCompletionList = true; + EditorBrowsableBehavior = EditorBrowsableBehavior.IncludeAdvanced; + this.IndentString = "\t"; + } + + public bool TryGetCompletionWord(int offset, out int startPos, out int wordLength) + { + startPos = wordLength = 0; + int pos = offset - 1; + while (pos >= 0) { + char c = document.GetCharAt(pos); + if (!char.IsLetterOrDigit(c) && c != '_') + break; + pos--; + } + if (pos == -1) + return false; + + pos++; + startPos = pos; + + while (pos < document.TextLength) { + char c = document.GetCharAt(pos); + if (!char.IsLetterOrDigit(c) && c != '_') + break; + pos++; + } + wordLength = pos - startPos; + return true; + } + + public IEnumerable GetCompletionData(int offset, bool controlSpace) + { + this.AutoCompleteEmptyMatch = true; + this.AutoSelect = true; + this.DefaultCompletionString = null; + SetOffset(offset); + if (offset > 0) { + char lastChar = document.GetCharAt(offset - 1); + bool isComplete = false; + var result = MagicKeyCompletion(lastChar, controlSpace, out isComplete) ?? Enumerable.Empty(); + if (!isComplete && controlSpace && char.IsWhiteSpace(lastChar)) { + offset -= 2; + while (offset >= 0 && char.IsWhiteSpace(document.GetCharAt(offset))) { + offset--; + } + if (offset > 0) { + var nonWsResult = MagicKeyCompletion( + document.GetCharAt(offset), + controlSpace, + out isComplete + ); + if (nonWsResult != null) { + var text = new HashSet(result.Select(r => r.CompletionText)); + result = result.Concat(nonWsResult.Where(r => !text.Contains(r.CompletionText))); + } + } + } + + return result; + } + return Enumerable.Empty(); + } + + /// + /// Gets the types that needs to be imported via using or full type name. + /// + public IEnumerable GetImportCompletionData(int offset) + { + var generalLookup = new MemberLookup(null, Compilation.MainAssembly); + SetOffset(offset); + + // flatten usings + var namespaces = new List(); + for (var n = ctx.CurrentUsingScope; n != null; n = n.Parent) { + namespaces.Add(n.Namespace); + foreach (var u in n.Usings) + namespaces.Add(u); + } + + foreach (var type in Compilation.GetAllTypeDefinitions ()) { + if (!generalLookup.IsAccessible(type, false)) + continue; + if (namespaces.Any(n => n.FullName == type.Namespace)) + continue; + bool useFullName = false; + foreach (var ns in namespaces) { + if (ns.GetTypeDefinition(type.Name, type.TypeParameterCount) != null) { + useFullName = true; + break; + } + } + yield return factory.CreateImportCompletionData(type, useFullName, false); + } + } + + IEnumerable GenerateNameProposals(AstType type) + { + if (type is PrimitiveType) { + var pt = (PrimitiveType)type; + switch (pt.Keyword) { + case "object": + yield return "o"; + yield return "obj"; + break; + case "bool": + yield return "b"; + yield return "pred"; + break; + case "double": + case "float": + case "decimal": + yield return "d"; + yield return "f"; + yield return "m"; + break; + default: + yield return "i"; + yield return "j"; + yield return "k"; + break; + } + yield break; + } + string name; + if (type is SimpleType) { + name = ((SimpleType)type).Identifier; + } else if (type is MemberType) { + name = ((MemberType)type).MemberName; + } else { + yield break; + } + + var names = WordParser.BreakWords(name); + + var possibleName = new StringBuilder(); + for (int i = 0; i < names.Count; i++) { + possibleName.Length = 0; + for (int j = i; j < names.Count; j++) { + if (string.IsNullOrEmpty(names [j])) { + continue; + } + if (j == i) { + names [j] = Char.ToLower(names [j] [0]) + names [j].Substring(1); + } + possibleName.Append(names [j]); + } + yield return possibleName.ToString(); + } + } + + IEnumerable HandleMemberReferenceCompletion(ExpressionResult expr) + { + if (expr == null) + return null; + + // do not auto select . (but ..) (0.ToString() is valid) + if (expr.Node is PrimitiveExpression) { + var pexpr = (PrimitiveExpression)expr.Node; + if (!(pexpr.Value is string || pexpr.Value is char) && !pexpr.LiteralValue.Contains('.')) { + AutoSelect = false; + } + } + var resolveResult = ResolveExpression(expr); + + if (resolveResult == null) { + return null; + } + if (expr.Node is AstType) { + + // check for namespace names + if (expr.Node.AncestorsAndSelf + .TakeWhile(n => n is AstType) + .Any(m => m.Role == NamespaceDeclaration.NamespaceNameRole)) + return null; + + // need to look at paren.parent because of "catch (.A" expression + if (expr.Node.Parent != null && expr.Node.Parent.Parent is CatchClause) + return HandleCatchClauseType(expr); + return CreateTypeAndNamespaceCompletionData( + location, + resolveResult.Result, + expr.Node, + resolveResult.Resolver + ); + } + + + return CreateCompletionData( + location, + resolveResult.Result, + expr.Node, + resolveResult.Resolver + ); + } + + bool IsInPreprocessorDirective() + { + var text = GetMemberTextToCaret().Item1; + var miniLexer = new MiniLexer(text); + miniLexer.Parse(); + return miniLexer.IsInPreprocessorDirective; + } + + IEnumerable HandleObjectInitializer(SyntaxTree unit, AstNode n) + { + var p = n.Parent; + while (p != null && !(p is ObjectCreateExpression)) { + p = p.Parent; + } + var parent = n.Parent as ArrayInitializerExpression; + if (parent == null) + return null; + if (parent.IsSingleElement) + parent = (ArrayInitializerExpression)parent.Parent; + if (p != null) { + var contextList = new CompletionDataWrapper(this); + var initializerResult = ResolveExpression(p); + IType initializerType = null; + + if (initializerResult.Result is DynamicInvocationResolveResult) { + var dr = (DynamicInvocationResolveResult)initializerResult.Result; + var constructor = (dr.Target as MethodGroupResolveResult).Methods.FirstOrDefault(); + if (constructor != null) + initializerType = constructor.DeclaringType; + } else { + initializerType = initializerResult != null ? initializerResult.Result.Type : null; + } + + + if (initializerType != null && initializerType.Kind != TypeKind.Unknown) { + // check 3 cases: + // 1) New initalizer { xpr + // 2) Object initializer { prop = val1, field = val2, xpr + // 3) Array initializer { new Foo (), a, xpr + // in case 1 all object/array initializer options should be given - in the others not. + + AstNode prev = null; + if (parent.Elements.Count > 1) { + prev = parent.Elements.First(); + if (prev is ArrayInitializerExpression && ((ArrayInitializerExpression)prev).IsSingleElement) + prev = ((ArrayInitializerExpression)prev).Elements.FirstOrDefault(); + } + + if (prev != null && !(prev is NamedExpression)) { + AddContextCompletion(contextList, GetState(), n); + // case 3) + return contextList.Result; + } + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); + var list = typeof(System.Collections.IList).ToTypeReference().Resolve(Compilation); + var list1 = typeof(System.Collections.Generic.IList<>).ToTypeReference().Resolve(Compilation); + bool isProtectedAllowed = ctx.CurrentTypeDefinition != null && initializerType.GetDefinition() != null ? + ctx.CurrentTypeDefinition.IsDerivedFrom(initializerType.GetDefinition()) : + false; + foreach (var m in initializerType.GetMembers (m => m.SymbolKind == SymbolKind.Field)) { + var f = m as IField; + if (f != null && (f.IsReadOnly || f.IsConst)) + continue; + if (lookup.IsAccessible(m, isProtectedAllowed)) { + var data = contextList.AddMember(m); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + } + + foreach (IProperty m in initializerType.GetMembers (m => m.SymbolKind == SymbolKind.Property)) { + if (m.CanSet && lookup.IsAccessible(m.Setter, isProtectedAllowed) || + m.CanGet && lookup.IsAccessible(m.Getter, isProtectedAllowed) && m.ReturnType.GetDefinition() != null && + (m.ReturnType.GetDefinition().IsDerivedFrom(list.GetDefinition()) || m.ReturnType.GetDefinition().IsDerivedFrom(list1.GetDefinition()))) { + var data = contextList.AddMember(m); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + } + + if (prev != null && (prev is NamedExpression)) { + // case 2) + return contextList.Result; + } + + // case 1) + + // check if the object is a list, if not only provide object initalizers + if (initializerType.Kind != TypeKind.Array && list != null) { + var def = initializerType.GetDefinition(); + if (def != null && !def.IsDerivedFrom(list.GetDefinition()) && !def.IsDerivedFrom(list1.GetDefinition())) + return contextList.Result; + } + + AddContextCompletion(contextList, GetState(), n); + return contextList.Result; + } + } + return null; + } + + static readonly DateTime curDate = DateTime.Now; + + IEnumerable GenerateNumberFormatitems(bool isFloatingPoint) + { + yield return factory.CreateFormatItemCompletionData("D", "decimal", 123); + yield return factory.CreateFormatItemCompletionData("D5", "decimal", 123); + yield return factory.CreateFormatItemCompletionData("C", "currency", 123); + yield return factory.CreateFormatItemCompletionData("C0", "currency", 123); + yield return factory.CreateFormatItemCompletionData("E", "exponential", 1.23E4); + yield return factory.CreateFormatItemCompletionData("E2", "exponential", 1.234); + yield return factory.CreateFormatItemCompletionData("e2", "exponential", 1.234); + yield return factory.CreateFormatItemCompletionData("F", "fixed-point", 123.45); + yield return factory.CreateFormatItemCompletionData("F1", "fixed-point", 123.45); + yield return factory.CreateFormatItemCompletionData("G", "general", 1.23E+56); + yield return factory.CreateFormatItemCompletionData("g2", "general", 1.23E+56); + yield return factory.CreateFormatItemCompletionData("N", "number", 12345.68); + yield return factory.CreateFormatItemCompletionData("N1", "number", 12345.68); + yield return factory.CreateFormatItemCompletionData("P", "percent", 12.34); + yield return factory.CreateFormatItemCompletionData("P1", "percent", 12.34); + yield return factory.CreateFormatItemCompletionData("R", "round-trip", 0.1230000001); + yield return factory.CreateFormatItemCompletionData("X", "hexadecimal", 1234); + yield return factory.CreateFormatItemCompletionData("x8", "hexadecimal", 1234); + yield return factory.CreateFormatItemCompletionData("0000", "custom", 123); + yield return factory.CreateFormatItemCompletionData("####", "custom", 123); + yield return factory.CreateFormatItemCompletionData("##.###", "custom", 1.23); + yield return factory.CreateFormatItemCompletionData("##.000", "custom", 1.23); + yield return factory.CreateFormatItemCompletionData("## 'items'", "custom", 12); + } + + IEnumerable GenerateDateTimeFormatitems() + { + yield return factory.CreateFormatItemCompletionData("D", "long date", curDate); + yield return factory.CreateFormatItemCompletionData("d", "short date", curDate); + yield return factory.CreateFormatItemCompletionData("F", "full date long", curDate); + yield return factory.CreateFormatItemCompletionData("f", "full date short", curDate); + yield return factory.CreateFormatItemCompletionData("G", "general long", curDate); + yield return factory.CreateFormatItemCompletionData("g", "general short", curDate); + yield return factory.CreateFormatItemCompletionData("M", "month", curDate); + yield return factory.CreateFormatItemCompletionData("O", "ISO 8601", curDate); + yield return factory.CreateFormatItemCompletionData("R", "RFC 1123", curDate); + yield return factory.CreateFormatItemCompletionData("s", "sortable", curDate); + yield return factory.CreateFormatItemCompletionData("T", "long time", curDate); + yield return factory.CreateFormatItemCompletionData("t", "short time", curDate); + yield return factory.CreateFormatItemCompletionData("U", "universal full", curDate); + yield return factory.CreateFormatItemCompletionData("u", "universal sortable", curDate); + yield return factory.CreateFormatItemCompletionData("Y", "year month", curDate); + yield return factory.CreateFormatItemCompletionData("yy-MM-dd", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("yyyy MMMMM dd", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("yy-MMM-dd ddd", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("yyyy-M-d dddd", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("hh:mm:ss t z", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("hh:mm:ss tt zz", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("HH:mm:ss tt zz", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("HH:m:s tt zz", "custom", curDate); + + } + + [Flags] + enum TestEnum + { + EnumCaseName = 0, + Flag1 = 1, + Flag2 = 2, + Flags + + } + + IEnumerable GenerateEnumFormatitems() + { + yield return factory.CreateFormatItemCompletionData("G", "string value", TestEnum.EnumCaseName); + yield return factory.CreateFormatItemCompletionData("F", "flags value", TestEnum.Flags); + yield return factory.CreateFormatItemCompletionData("D", "integer value", TestEnum.Flags); + yield return factory.CreateFormatItemCompletionData("X", "hexadecimal", TestEnum.Flags); + } + + IEnumerable GenerateTimeSpanFormatitems() + { + yield return factory.CreateFormatItemCompletionData("c", "invariant", new TimeSpan(0, 1, 23, 456)); + yield return factory.CreateFormatItemCompletionData("G", "general long", new TimeSpan(0, 1, 23, 456)); + yield return factory.CreateFormatItemCompletionData("g", "general short", new TimeSpan(0, 1, 23, 456)); + } + + static Guid defaultGuid = Guid.NewGuid(); + + IEnumerable GenerateGuidFormatitems() + { + yield return factory.CreateFormatItemCompletionData("N", "digits", defaultGuid); + yield return factory.CreateFormatItemCompletionData("D", "hypens", defaultGuid); + yield return factory.CreateFormatItemCompletionData("B", "braces", defaultGuid); + yield return factory.CreateFormatItemCompletionData("P", "parentheses", defaultGuid); + } + + int GetFormatItemNumber() + { + int number = 0; + var o = offset - 2; + while (o > 0) { + char ch = document.GetCharAt(o); + if (ch == '{') + return number; + if (!char.IsDigit(ch)) + break; + number = number * 10 + ch - '0'; + o--; + } + return -1; + } + + IEnumerable HandleStringFormatItems() + { + var formatArgument = GetFormatItemNumber(); + if (formatArgument < 0) + return Enumerable.Empty(); + var followUp = new StringBuilder(); + + var o = offset; + while (o < document.TextLength) { + char ch = document.GetCharAt(o); + followUp.Append(ch); + o++; + if (ch == ';') + break; + } + var unit = ParseStub(followUp.ToString(), false); + + var invoke = unit.GetNodeAt(location); + + if (invoke != null) { + var resolveResult = ResolveExpression(new ExpressionResult(invoke, unit)); + var invokeResult = resolveResult.Result as InvocationResolveResult; + if (invokeResult != null) { + var arg = formatArgument + 1; // First argument is the format string + if (arg < invoke.Arguments.Count) { + var invokeArgument = ResolveExpression(new ExpressionResult(invoke.Arguments.ElementAt(arg), unit)); + if (invokeArgument != null) { + var provider = GetFormatCompletionData(invokeArgument.Result.Type); + if (provider != null) + return provider; + if (!invokeArgument.Result.Type.IsKnownType(KnownTypeCode.Object)) + return Enumerable.Empty(); + } + } + } + } + return HandleStringFormatItemsFallback(); + } + + IEnumerable HandleStringFormatItemsFallback() + { + var unit = ParseStub("a}\");", false); + + var invoke = unit.GetNodeAt(location); + + if (invoke == null) + return Enumerable.Empty(); + + var resolveResult = ResolveExpression(new ExpressionResult(invoke, unit)); + var invokeResult = resolveResult.Result as CSharpInvocationResolveResult; + if (invokeResult == null) + return Enumerable.Empty(); + + Expression fmtArgumets; + IList args; + if (FormatStringHelper.TryGetFormattingParameters(invokeResult, invoke, out fmtArgumets, out args, null)) { + return GenerateNumberFormatitems(false) + .Concat(GenerateDateTimeFormatitems()) + .Concat(GenerateTimeSpanFormatitems()) + .Concat(GenerateEnumFormatitems()) + .Concat(GenerateGuidFormatitems()); + } + return Enumerable.Empty(); + + } + + IEnumerable GetFormatCompletionData(IType type) + { + if (type.Namespace != "System") + return null; + switch (type.Name) { + case "Int64": + case "UInt64": + case "Int32": + case "UInt32": + case "Int16": + case "UInt16": + case "Byte": + case "SByte": + return GenerateNumberFormatitems(false); + case "Single": + case "Double": + case "Decimal": + return GenerateNumberFormatitems(true); + case "Enum": + return GenerateEnumFormatitems(); + case "DateTime": + return GenerateDateTimeFormatitems(); + case "TimeSpan": + return GenerateTimeSpanFormatitems(); + case "Guid": + return GenerateGuidFormatitems(); + } + return null; + } + + IEnumerable HandleToStringFormatItems() + { + var unit = ParseStub("\");", false); + + var invoke = unit.GetNodeAt(location); + if (invoke == null) + return Enumerable.Empty(); + + var resolveResult = ResolveExpression(new ExpressionResult(invoke, unit)); + var invokeResult = resolveResult.Result as InvocationResolveResult; + if (invokeResult == null) + return Enumerable.Empty(); + if (invokeResult.Member.Name == "ToString") + return GetFormatCompletionData(invokeResult.Member.DeclaringType) ?? Enumerable.Empty(); + return Enumerable.Empty(); + } + + IEnumerable MagicKeyCompletion(char completionChar, bool controlSpace, out bool isComplete) + { + isComplete = false; + ExpressionResolveResult resolveResult; + switch (completionChar) { + // Magic key completion + case ':': + var text = GetMemberTextToCaret(); + var lexer = new MiniLexer(text.Item1); + lexer.Parse(); + if (lexer.IsInSingleComment || + lexer.IsInChar || + lexer.IsInMultiLineComment || + lexer.IsInPreprocessorDirective) { + return Enumerable.Empty(); + } + + if (lexer.IsInString || lexer.IsInVerbatimString) + return HandleStringFormatItems(); + return HandleMemberReferenceCompletion(GetExpressionBeforeCursor()); + case '"': + text = GetMemberTextToCaret(); + lexer = new MiniLexer(text.Item1); + lexer.Parse(); + if (lexer.IsInSingleComment || + lexer.IsInChar || + lexer.IsInMultiLineComment || + lexer.IsInPreprocessorDirective) { + return Enumerable.Empty(); + } + + if (lexer.IsInString || lexer.IsInVerbatimString) + return HandleToStringFormatItems(); + return Enumerable.Empty(); + case '.': + if (IsInsideCommentStringOrDirective()) { + return Enumerable.Empty(); + } + return HandleMemberReferenceCompletion(GetExpressionBeforeCursor()); + case '#': + if (!IsInPreprocessorDirective()) + return null; + return GetDirectiveCompletionData(); + // XML doc completion + case '<': + if (IsInsideDocComment()) { + return GetXmlDocumentationCompletionData(); + } + if (controlSpace) { + return DefaultControlSpaceItems(ref isComplete); + } + return null; + case '>': + if (!IsInsideDocComment()) { + if (offset > 2 && document.GetCharAt(offset - 2) == '-' && !IsInsideCommentStringOrDirective()) { + return HandleMemberReferenceCompletion(GetExpressionBeforeCursor()); + } + return null; + } + return null; + + // Parameter completion + case '(': + if (IsInsideCommentStringOrDirective()) { + return null; + } + var invoke = GetInvocationBeforeCursor(true); + if (invoke == null) { + if (controlSpace) + return DefaultControlSpaceItems(ref isComplete, invoke); + return null; + } + if (invoke.Node is TypeOfExpression) { + return CreateTypeList(); + } + var invocationResult = ResolveExpression(invoke); + if (invocationResult == null) { + return null; + } + var methodGroup = invocationResult.Result as MethodGroupResolveResult; + if (methodGroup != null) { + return CreateParameterCompletion( + methodGroup, + invocationResult.Resolver, + invoke.Node, + invoke.Unit, + 0, + controlSpace + ); + } + + if (controlSpace) { + return DefaultControlSpaceItems(ref isComplete, invoke); + } + return null; + case '=': + return controlSpace ? DefaultControlSpaceItems(ref isComplete) : null; + case ',': + int cpos2; + if (!GetParameterCompletionCommandOffset(out cpos2)) { + return null; + } + // completionContext = CompletionWidget.CreateCodeCompletionContext (cpos2); + // int currentParameter2 = MethodParameterDataProvider.GetCurrentParameterIndex (CompletionWidget, completionContext) - 1; + // return CreateParameterCompletion (CreateResolver (), location, ExpressionContext.MethodBody, provider.Methods, currentParameter); + break; + + // Completion on space: + case ' ': + int tokenIndex = offset; + string token = GetPreviousToken(ref tokenIndex, false); + if (IsInsideCommentStringOrDirective()) { + return null; + } + // check propose name, for context (but only in control space context) + //IType isAsType = null; + var isAsExpression = GetExpressionAt(offset); + if (controlSpace && isAsExpression != null && isAsExpression.Node is VariableDeclarationStatement && token != "new") { + var parent = isAsExpression.Node as VariableDeclarationStatement; + var proposeNameList = new CompletionDataWrapper(this); + if (parent.Variables.Count != 1) + return DefaultControlSpaceItems(ref isComplete, isAsExpression, controlSpace); + + foreach (var possibleName in GenerateNameProposals (parent.Type)) { + if (possibleName.Length > 0) { + proposeNameList.Result.Add(factory.CreateLiteralCompletionData(possibleName.ToString())); + } + } + + AutoSelect = false; + AutoCompleteEmptyMatch = false; + isComplete = true; + return proposeNameList.Result; + } + // int tokenIndex = offset; + // string token = GetPreviousToken (ref tokenIndex, false); + // if (result.ExpressionContext == ExpressionContext.ObjectInitializer) { + // resolver = CreateResolver (); + // ExpressionContext exactContext = new NewCSharpExpressionFinder (dom).FindExactContextForObjectInitializer (document, resolver.Unit, Document.FileName, resolver.CallingType); + // IReturnType objectInitializer = ((ExpressionContext.TypeExpressionContext)exactContext).UnresolvedType; + // if (objectInitializer != null && objectInitializer.ArrayDimensions == 0 && objectInitializer.PointerNestingLevel == 0 && (token == "{" || token == ",")) + // return CreateCtrlSpaceCompletionData (completionContext, result); + // } + if (token == "=") { + int j = tokenIndex; + string prevToken = GetPreviousToken(ref j, false); + if (prevToken == "=" || prevToken == "+" || prevToken == "-" || prevToken == "!") { + token = prevToken + token; + tokenIndex = j; + } + } + switch (token) { + case "(": + case ",": + int cpos; + if (!GetParameterCompletionCommandOffset(out cpos)) { + break; + } + int currentParameter = GetCurrentParameterIndex(cpos - 1, this.offset) - 1; + if (currentParameter < 0) { + return null; + } + invoke = GetInvocationBeforeCursor(token == "("); + if (invoke == null) { + return null; + } + invocationResult = ResolveExpression(invoke); + if (invocationResult == null) { + return null; + } + methodGroup = invocationResult.Result as MethodGroupResolveResult; + if (methodGroup != null) { + return CreateParameterCompletion( + methodGroup, + invocationResult.Resolver, + invoke.Node, + invoke.Unit, + currentParameter, + controlSpace); + } + return null; + case "=": + case "==": + case "!=": + GetPreviousToken(ref tokenIndex, false); + var expressionOrVariableDeclaration = GetExpressionAt(tokenIndex); + if (expressionOrVariableDeclaration == null) { + return null; + } + resolveResult = ResolveExpression(expressionOrVariableDeclaration); + if (resolveResult == null) { + return null; + } + if (resolveResult.Result.Type.Kind == TypeKind.Enum) { + var wrapper = new CompletionDataWrapper(this); + AddContextCompletion( + wrapper, + resolveResult.Resolver, + expressionOrVariableDeclaration.Node); + AddEnumMembers(wrapper, resolveResult.Result.Type, resolveResult.Resolver); + AutoCompleteEmptyMatch = false; + return wrapper.Result; + } + // + // if (resolvedType.FullName == DomReturnType.Bool.FullName) { + // CompletionDataList completionList = new ProjectDomCompletionDataList (); + // CompletionDataCollector cdc = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); + // completionList.AutoCompleteEmptyMatch = false; + // cdc.Add ("true", "md-keyword"); + // cdc.Add ("false", "md-keyword"); + // resolver.AddAccessibleCodeCompletionData (result.ExpressionContext, cdc); + // return completionList; + // } + // if (resolvedType.ClassType == ClassType.Delegate && token == "=") { + // CompletionDataList completionList = new ProjectDomCompletionDataList (); + // string parameterDefinition = AddDelegateHandlers (completionList, resolvedType); + // string varName = GetPreviousMemberReferenceExpression (tokenIndex); + // completionList.Add (new EventCreationCompletionData (document, varName, resolvedType, null, parameterDefinition, resolver.CallingMember, resolvedType)); + // + // CompletionDataCollector cdc = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); + // resolver.AddAccessibleCodeCompletionData (result.ExpressionContext, cdc); + // foreach (var data in completionList) { + // if (data is MemberCompletionData) + // ((MemberCompletionData)data).IsDelegateExpected = true; + // } + // return completionList; + // } + return null; + case "+=": + case "-=": + var curTokenIndex = tokenIndex; + GetPreviousToken(ref tokenIndex, false); + + expressionOrVariableDeclaration = GetExpressionAt(tokenIndex); + if (expressionOrVariableDeclaration == null) { + return null; + } + + resolveResult = ResolveExpression(expressionOrVariableDeclaration); + if (resolveResult == null) { + return null; + } + + + var mrr = resolveResult.Result as MemberResolveResult; + if (mrr != null) { + var evt = mrr.Member as IEvent; + if (evt == null) { + return null; + } + var delegateType = evt.ReturnType; + if (delegateType.Kind != TypeKind.Delegate) { + return null; + } + + var wrapper = new CompletionDataWrapper(this); + if (currentType != null) { + // bool includeProtected = DomType.IncludeProtected (dom, typeFromDatabase, resolver.CallingType); + foreach (var method in ctx.CurrentTypeDefinition.Methods) { + if (MatchDelegate(delegateType, method) /* && method.IsAccessibleFrom (dom, resolver.CallingType, resolver.CallingMember, includeProtected) &&*/) { + wrapper.AddMember(method); + // data.SetText (data.CompletionText + ";"); + } + } + } + if (token == "+=") { + string parameterDefinition = AddDelegateHandlers( + wrapper, + delegateType, + optDelegateName: GuessEventHandlerMethodName(curTokenIndex, (currentType == null) ? null : currentType.Name) + ); + } + + return wrapper.Result; + } + return null; + case ":": + if (currentMember == null) { + token = GetPreviousToken(ref tokenIndex, false); + token = GetPreviousToken(ref tokenIndex, false); + if (token == "enum") + return HandleEnumContext(); + var wrapper = new CompletionDataWrapper(this); + AddTypesAndNamespaces( + wrapper, + GetState(), + null, + t => { + if (currentType != null && currentType.ReflectionName.Equals(t.ReflectionName)) + return null; + var def = t.GetDefinition(); + if (def != null && t.Kind != TypeKind.Interface && (def.IsSealed ||def.IsStatic)) + return null; + return t; + } + ); + return wrapper.Result; + } + return null; + } + + var keywordCompletion = HandleKeywordCompletion(tokenIndex, token); + if (keywordCompletion == null && controlSpace) { + goto default; + } + return keywordCompletion; + // Automatic completion + default: + if (IsInsideCommentStringOrDirective()) { + tokenIndex = offset; + token = GetPreviousToken(ref tokenIndex, false); + if (IsInPreprocessorDirective() && (token.Length == 1 && char.IsLetter(completionChar) || controlSpace)) { + while (token != null && document.GetCharAt(tokenIndex - 1) != '#') { + token = GetPreviousToken(ref tokenIndex, false); + } + if (token != null) + return HandleKeywordCompletion(tokenIndex, token); + } + return null; + } + char prevCh = offset > 2 ? document.GetCharAt(offset - 2) : ';'; + char nextCh = offset < document.TextLength ? document.GetCharAt(offset) : ' '; + const string allowedChars = ";,.[](){}+-*/%^?:&|~!<>="; + + if ((!Char.IsWhiteSpace(nextCh) && allowedChars.IndexOf(nextCh) < 0) || !(Char.IsWhiteSpace(prevCh) || allowedChars.IndexOf(prevCh) >= 0)) { + if (!controlSpace) + return null; + } + + if (IsInLinqContext(offset)) { + if (!controlSpace && !(char.IsLetter(completionChar) || completionChar == '_')) { + return null; + } + tokenIndex = offset; + token = GetPreviousToken(ref tokenIndex, false); + // token last typed + if (!char.IsWhiteSpace(completionChar) && !linqKeywords.Contains(token)) { + token = GetPreviousToken(ref tokenIndex, false); + } + // token last typed + + if (linqKeywords.Contains(token)) { + if (token == "from") { + // after from no auto code completion. + return null; + } + return DefaultControlSpaceItems(ref isComplete); + } + var dataList = new CompletionDataWrapper(this); + AddKeywords(dataList, linqKeywords); + return dataList.Result; + } + if (currentType != null && currentType.Kind == TypeKind.Enum) { + if (!char.IsLetter(completionChar)) + return null; + return HandleEnumContext(); + } + var contextList = new CompletionDataWrapper(this); + var identifierStart = GetExpressionAtCursor(); + if (!(char.IsLetter(completionChar) || completionChar == '_') && (!controlSpace || identifierStart == null)) { + return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(ref isComplete, identifierStart) : null; + } + + if (identifierStart != null) { + if (identifierStart.Node is TypeParameterDeclaration) { + return null; + } + + if (identifierStart.Node is MemberReferenceExpression) { + return HandleMemberReferenceCompletion( + new ExpressionResult( + ((MemberReferenceExpression)identifierStart.Node).Target, + identifierStart.Unit + ) + ); + } + + if (identifierStart.Node is Identifier) { + if (identifierStart.Node.Parent is GotoStatement) + return null; + + // May happen in variable names + return controlSpace ? DefaultControlSpaceItems(ref isComplete, identifierStart) : null; + } + if (identifierStart.Node is VariableInitializer && location <= ((VariableInitializer)identifierStart.Node).NameToken.EndLocation) { + return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(ref isComplete, identifierStart) : null; + } + if (identifierStart.Node is CatchClause) { + if (((CatchClause)identifierStart.Node).VariableNameToken.IsInside(location)) { + return null; + } + } + if (identifierStart.Node is AstType && identifierStart.Node.Parent is CatchClause) { + return HandleCatchClauseType(identifierStart); + } + + var pDecl = identifierStart.Node as ParameterDeclaration; + if (pDecl != null && pDecl.Parent is LambdaExpression) { + return null; + } + } + + + // 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") { + // after these always follows a name + return null; + } + var keywordresult = HandleKeywordCompletion(tokenIndex, token); + if (keywordresult != null) { + return keywordresult; + } + + if ((!Char.IsWhiteSpace(nextCh) && allowedChars.IndexOf(nextCh) < 0) || !(Char.IsWhiteSpace(prevCh) || allowedChars.IndexOf(prevCh) >= 0)) { + if (controlSpace) + return DefaultControlSpaceItems(ref isComplete, identifierStart); + } + + int prevTokenIndex = tokenIndex; + var prevToken2 = GetPreviousToken(ref prevTokenIndex, false); + if (prevToken2 == "delegate") { + // after these always follows a name + return null; + } + + if (identifierStart == null && !string.IsNullOrEmpty(token) && !IsInsideCommentStringOrDirective() && (prevToken2 == ";" || prevToken2 == "{" || prevToken2 == "}")) { + char last = token [token.Length - 1]; + if (char.IsLetterOrDigit(last) || last == '_' || token == ">") { + return HandleKeywordCompletion(tokenIndex, token); + } + } + if (identifierStart == null) { + var accCtx = HandleAccessorContext(); + if (accCtx != null) { + return accCtx; + } + return DefaultControlSpaceItems(ref isComplete, null, controlSpace); + } + CSharpResolver csResolver; + AstNode n = identifierStart.Node; + if (n.Parent is NamedArgumentExpression) + n = n.Parent; + + if (n != null && n.Parent is AnonymousTypeCreateExpression) { + AutoSelect = false; + } + + // new { b$ } + if (n is IdentifierExpression && n.Parent is AnonymousTypeCreateExpression) + return null; + + // Handle foreach (type name _ + if (n is IdentifierExpression) { + var prev = n.GetPrevNode() as ForeachStatement; + while (prev != null && prev.EmbeddedStatement is ForeachStatement) + prev = (ForeachStatement)prev.EmbeddedStatement; + if (prev != null && prev.InExpression.IsNull) { + if (IncludeKeywordsInCompletionList) + contextList.AddCustom("in"); + return contextList.Result; + } + } + // Handle object/enumerable initialzer expressions: "new O () { P$" + if (n is IdentifierExpression && n.Parent is ArrayInitializerExpression && !(n.Parent.Parent is ArrayCreateExpression)) { + var result = HandleObjectInitializer(identifierStart.Unit, n); + if (result != null) + return result; + } + + if (n != null && n.Parent is InvocationExpression || + n.Parent is ParenthesizedExpression && n.Parent.Parent is InvocationExpression) { + if (n.Parent is ParenthesizedExpression) + n = n.Parent; + var invokeParent = (InvocationExpression)n.Parent; + var invokeResult = ResolveExpression( + invokeParent.Target + ); + var mgr = invokeResult != null ? invokeResult.Result as MethodGroupResolveResult : null; + if (mgr != null) { + int idx = 0; + foreach (var arg in invokeParent.Arguments) { + if (arg == n) { + break; + } + idx++; + } + + foreach (var method in mgr.Methods) { + if (idx < method.Parameters.Count && method.Parameters [idx].Type.Kind == TypeKind.Delegate) { + AutoSelect = false; + AutoCompleteEmptyMatch = false; + } + foreach (var p in method.Parameters) { + contextList.AddNamedParameterVariable(p); + } + } + idx++; + foreach (var list in mgr.GetEligibleExtensionMethods (true)) { + foreach (var method in list) { + if (idx < method.Parameters.Count && method.Parameters [idx].Type.Kind == TypeKind.Delegate) { + AutoSelect = false; + AutoCompleteEmptyMatch = false; + } + } + } + } + } + + if (n != null && n.Parent is ObjectCreateExpression) { + var invokeResult = ResolveExpression(n.Parent); + var mgr = invokeResult != null ? invokeResult.Result as ResolveResult : null; + if (mgr != null) { + foreach (var constructor in mgr.Type.GetConstructors ()) { + foreach (var p in constructor.Parameters) { + contextList.AddVariable(p); + } + } + } + } + + if (n is IdentifierExpression) { + var bop = n.Parent as BinaryOperatorExpression; + Expression evaluationExpr = null; + + if (bop != null && bop.Right == n && (bop.Operator == BinaryOperatorType.Equality || bop.Operator == BinaryOperatorType.InEquality)) { + evaluationExpr = bop.Left; + } + // check for compare to enum case + if (evaluationExpr != null) { + resolveResult = ResolveExpression(evaluationExpr); + if (resolveResult != null && resolveResult.Result.Type.Kind == TypeKind.Enum) { + var wrapper = new CompletionDataWrapper(this); + AddContextCompletion( + wrapper, + resolveResult.Resolver, + evaluationExpr + ); + AddEnumMembers(wrapper, resolveResult.Result.Type, resolveResult.Resolver); + AutoCompleteEmptyMatch = false; + return wrapper.Result; + } + } + } + + if (n is Identifier && n.Parent is ForeachStatement) { + if (controlSpace) { + return DefaultControlSpaceItems(ref isComplete); + } + return null; + } + + if (n is ArrayInitializerExpression) { + // check for new [] {...} expression -> no need to resolve the type there + var parent = n.Parent as ArrayCreateExpression; + if (parent != null && parent.Type.IsNull) { + return DefaultControlSpaceItems(ref isComplete); + } + + var initalizerResult = ResolveExpression(n.Parent); + + var concreteNode = identifierStart.Unit.GetNodeAt(location); + // check if we're on the right side of an initializer expression + if (concreteNode != null && concreteNode.Parent != null && concreteNode.Parent.Parent != null && concreteNode.Identifier != "a" && concreteNode.Parent.Parent is NamedExpression) { + return DefaultControlSpaceItems(ref isComplete); + } + if (initalizerResult != null && initalizerResult.Result.Type.Kind != TypeKind.Unknown) { + + foreach (var property in initalizerResult.Result.Type.GetProperties ()) { + if (!property.IsPublic) { + continue; + } + var data = contextList.AddMember(property); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + foreach (var field in initalizerResult.Result.Type.GetFields ()) { + if (!field.IsPublic) { + continue; + } + var data = contextList.AddMember(field); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + return contextList.Result; + } + return DefaultControlSpaceItems(ref isComplete); + } + + if (IsAttributeContext(n)) { + // add attribute targets + if (currentType == null) { + contextList.AddCustom("assembly"); + contextList.AddCustom("module"); + contextList.AddCustom("type"); + } else { + contextList.AddCustom("param"); + contextList.AddCustom("field"); + contextList.AddCustom("property"); + contextList.AddCustom("method"); + contextList.AddCustom("event"); + } + contextList.AddCustom("return"); + } + if (n is MemberType) { + resolveResult = ResolveExpression( + ((MemberType)n).Target + ); + return CreateTypeAndNamespaceCompletionData( + location, + resolveResult.Result, + ((MemberType)n).Target, + resolveResult.Resolver + ); + } + if (n != null/* && !(identifierStart.Item2 is TypeDeclaration)*/) { + csResolver = new CSharpResolver(ctx); + var nodes = new List(); + nodes.Add(n); + if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) { + nodes.Add(n.Parent); + } + var astResolver = CompletionContextProvider.GetResolver(csResolver, identifierStart.Unit); + astResolver.ApplyNavigator(new NodeListResolveVisitorNavigator(nodes)); + try { + csResolver = astResolver.GetResolverStateBefore(n); + } catch (Exception) { + csResolver = GetState(); + } + // add attribute properties. + if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) { + var rr = ResolveExpression(n.Parent); + if (rr != null) + AddAttributeProperties(contextList, rr.Result); + } + } else { + csResolver = GetState(); + } + // identifier has already started with the first letter + offset--; + AddContextCompletion( + contextList, + csResolver, + identifierStart.Node + ); + return contextList.Result; + // if (stub.Parent is BlockStatement) + + // result = FindExpression (dom, completionContext, -1); + // if (result == null) + // return null; + // else if (result.ExpressionContext != ExpressionContext.IdentifierExpected) { + // triggerWordLength = 1; + // bool autoSelect = true; + // IType returnType = null; + // if ((prevCh == ',' || prevCh == '(') && GetParameterCompletionCommandOffset (out cpos)) { + // ctx = CompletionWidget.CreateCodeCompletionContext (cpos); + // NRefactoryParameterDataProvider dataProvider = ParameterCompletionCommand (ctx) as NRefactoryParameterDataProvider; + // if (dataProvider != null) { + // int i = dataProvider.GetCurrentParameterIndex (CompletionWidget, ctx) - 1; + // foreach (var method in dataProvider.Methods) { + // if (i < method.Parameters.Count) { + // returnType = dom.GetType (method.Parameters [i].ReturnType); + // autoSelect = returnType == null || returnType.ClassType != ClassType.Delegate; + // break; + // } + // } + // } + // } + // // Bug 677531 - Auto-complete doesn't always highlight generic parameter in method signature + // //if (result.ExpressionContext == ExpressionContext.TypeName) + // // autoSelect = false; + // CompletionDataList dataList = CreateCtrlSpaceCompletionData (completionContext, result); + // AddEnumMembers (dataList, returnType); + // dataList.AutoSelect = autoSelect; + // return dataList; + // } else { + // result = FindExpression (dom, completionContext, 0); + // tokenIndex = offset; + // + // // check foreach case, unfortunately the expression finder is too dumb to handle full type names + // // should be overworked if the expression finder is replaced with a mcs ast based analyzer. + // var possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // starting letter + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // varname + // + // // read return types to '(' token + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // varType + // if (possibleForeachToken == ">") { + // while (possibleForeachToken != null && possibleForeachToken != "(") { + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); + // } + // } else { + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // ( + // if (possibleForeachToken == ".") + // while (possibleForeachToken != null && possibleForeachToken != "(") + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); + // } + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // foreach + // + // if (possibleForeachToken == "foreach") { + // result.ExpressionContext = ExpressionContext.ForeachInToken; + // } else { + // return null; + // // result.ExpressionContext = ExpressionContext.IdentifierExpected; + // } + // result.Expression = ""; + // result.Region = DomRegion.Empty; + // + // return CreateCtrlSpaceCompletionData (completionContext, result); + // } + // break; + } + return null; + + } + + IEnumerable HandleCatchClauseType(ExpressionResult identifierStart) + { + Func typePred = delegate (IType type) { + if (type.GetAllBaseTypes().Any(t => t.ReflectionName == "System.Exception")) + return type; + return null; + }; + if (identifierStart.Node.Parent is CatchClause) { + var wrapper = new CompletionDataWrapper(this); + AddTypesAndNamespaces( + wrapper, + GetState(), + identifierStart.Node, + typePred, + m => false + ); + return wrapper.Result; + } + + var resolveResult = ResolveExpression(identifierStart); + return CreateCompletionData( + location, + resolveResult.Result, + identifierStart.Node, + resolveResult.Resolver, + typePred + ); + } + + string[] validEnumBaseTypes = { + "byte", + "sbyte", + "short", + "int", + "long", + "ushort", + "uint", + "ulong" + }; + + IEnumerable HandleEnumContext() + { + var syntaxTree = ParseStub("a", false); + if (syntaxTree == null) { + return null; + } + + var curType = syntaxTree.GetNodeAt(location); + if (curType == null || curType.ClassType != ClassType.Enum) { + syntaxTree = ParseStub("a {}", false); + var node = syntaxTree.GetNodeAt(location); + if (node != null) { + var wrapper = new CompletionDataWrapper(this); + AddKeywords(wrapper, validEnumBaseTypes); + return wrapper.Result; + } + } + + var member = syntaxTree.GetNodeAt(location); + if (member != null && member.NameToken.EndLocation < location) { + if (currentMember == null && currentType != null) { + foreach (var a in currentType.Members) + if (a.Region.Begin < location && (currentMember == null || a.Region.Begin > currentMember.Region.Begin)) + currentMember = a; + } + bool isComplete = false; + return DefaultControlSpaceItems(ref isComplete); + } + + var attribute = syntaxTree.GetNodeAt(location); + if (attribute != null) { + var contextList = new CompletionDataWrapper(this); + var astResolver = CompletionContextProvider.GetResolver(GetState(), syntaxTree); + var csResolver = astResolver.GetResolverStateBefore(attribute); + AddContextCompletion( + contextList, + csResolver, + attribute + ); + return contextList.Result; + } + return null; + } + + bool IsInLinqContext(int offset) + { + string token; + while (null != (token = GetPreviousToken(ref offset, true)) && !IsInsideCommentStringOrDirective()) { + + if (token == "from") { + return !IsInsideCommentStringOrDirective(offset); + } + if (token == ";" || token == "{") { + return false; + } + } + return false; + } + + IEnumerable HandleAccessorContext() + { + var unit = ParseStub("get; }", false); + var node = unit.GetNodeAt(location, cn => !(cn is CSharpTokenNode)); + if (node is Accessor) { + node = node.Parent; + } + var contextList = new CompletionDataWrapper(this); + if (node is PropertyDeclaration || node is IndexerDeclaration) { + if (IncludeKeywordsInCompletionList) { + contextList.AddCustom("get"); + contextList.AddCustom("set"); + AddKeywords(contextList, accessorModifierKeywords); + } + } else if (node is CustomEventDeclaration) { + if (IncludeKeywordsInCompletionList) { + contextList.AddCustom("add"); + contextList.AddCustom("remove"); + } + } else { + return null; + } + + return contextList.Result; + } + + class IfVisitor :DepthFirstAstVisitor + { + TextLocation loc; + ICompletionContextProvider completionContextProvider; + public bool IsValid; + + public IfVisitor(TextLocation loc, ICompletionContextProvider completionContextProvider) + { + this.loc = loc; + this.completionContextProvider = completionContextProvider; + + this.IsValid = true; + } + + void Check(string argument) + { + // TODO: evaluate #if epressions + if (argument.Any(c => !(char.IsLetterOrDigit(c) || c == '_'))) + return; + IsValid &= completionContextProvider.ConditionalSymbols.Contains(argument); + } + + Stack ifStack = new Stack(); + + public override void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + if (preProcessorDirective.Type == PreProcessorDirectiveType.If) { + ifStack.Push(preProcessorDirective); + } else if (preProcessorDirective.Type == PreProcessorDirectiveType.Endif) { + if (ifStack.Count == 0) + return; + var ifDirective = ifStack.Pop(); + if (ifDirective.StartLocation < loc && loc < preProcessorDirective.EndLocation) { + Check(ifDirective.Argument); + } + + } + + base.VisitPreProcessorDirective(preProcessorDirective); + } + + public void End() + { + while (ifStack.Count > 0) { + Check(ifStack.Pop().Argument); + } + } + } + + IEnumerable DefaultControlSpaceItems(ref bool isComplete, ExpressionResult xp = null, bool controlSpace = true) + { + var wrapper = new CompletionDataWrapper(this); + if (offset >= document.TextLength) { + offset = document.TextLength - 1; + } + while (offset > 1 && char.IsWhiteSpace(document.GetCharAt(offset))) { + offset--; + } + location = document.GetLocation(offset); + + if (xp == null) { + xp = GetExpressionAtCursor(); + } + AstNode node; + SyntaxTree unit; + ExpressionResolveResult rr; + if (xp != null) { + node = xp.Node; + rr = ResolveExpression(node); + unit = xp.Unit; + } else { + unit = ParseStub("foo", false); + node = unit.GetNodeAt( + location.Line, + location.Column + 2, + n => n is Expression || n is AstType || n is NamespaceDeclaration || n is Attribute + ); + rr = ResolveExpression(node); + } + var ifvisitor = new IfVisitor(location, CompletionContextProvider); + unit.AcceptVisitor(ifvisitor); + ifvisitor.End(); + if (!ifvisitor.IsValid) + return null; + // namespace name case + var ns = node as NamespaceDeclaration; + if (ns != null) { + var last = ns.NamespaceName; + if (last != null && location < last.EndLocation) + return null; + } + if (node is Identifier && node.Parent is ForeachStatement) { + var foreachStmt = (ForeachStatement)node.Parent; + foreach (var possibleName in GenerateNameProposals (foreachStmt.VariableType)) { + if (possibleName.Length > 0) { + wrapper.Result.Add(factory.CreateLiteralCompletionData(possibleName.ToString())); + } + } + + AutoSelect = false; + AutoCompleteEmptyMatch = false; + isComplete = true; + return wrapper.Result; + } + + if (node is Identifier && node.Parent is ParameterDeclaration) { + if (!controlSpace) { + return null; + } + // Try Parameter name case + var param = node.Parent as ParameterDeclaration; + if (param != null) { + foreach (var possibleName in GenerateNameProposals (param.Type)) { + if (possibleName.Length > 0) { + wrapper.Result.Add(factory.CreateLiteralCompletionData(possibleName.ToString())); + } + } + AutoSelect = false; + AutoCompleteEmptyMatch = false; + isComplete = true; + return wrapper.Result; + } + } + var pDecl = node as ParameterDeclaration; + if (pDecl != null && pDecl.Parent is LambdaExpression) { + return null; + } + /* if (Unit != null && (node == null || node is TypeDeclaration)) { + var constructor = Unit.GetNodeAt( + location.Line, + location.Column - 3 + ); + if (constructor != null && !constructor.ColonToken.IsNull && constructor.Initializer.IsNull) { + wrapper.AddCustom("this"); + wrapper.AddCustom("base"); + return wrapper.Result; + } + }*/ + + var initializer = node != null ? node.Parent as ArrayInitializerExpression : null; + if (initializer != null) { + var result = HandleObjectInitializer(unit, initializer); + if (result != null) + return result; + } + CSharpResolver csResolver = null; + if (rr != null) { + csResolver = rr.Resolver; + } + + if (csResolver == null) { + if (node != null) { + csResolver = GetState(); + //var astResolver = new CSharpAstResolver (csResolver, node, xp != null ? xp.Item1 : CSharpUnresolvedFile); + + try { + //csResolver = astResolver.GetResolverStateBefore (node); + Console.WriteLine(csResolver.LocalVariables.Count()); + } catch (Exception e) { + Console.WriteLine("E!!!" + e); + } + + } else { + csResolver = GetState(); + } + } + + if (node is Attribute) { + // add attribute properties. + var astResolver = CompletionContextProvider.GetResolver(csResolver, unit); + var resolved = astResolver.Resolve(node); + AddAttributeProperties(wrapper, resolved); + } + + + if (node == null) { + // try lambda + unit = ParseStub("foo) => {}", true); + var pd = unit.GetNodeAt( + location.Line, + location.Column + ); + if (pd != null) { + var astResolver = unit != null ? CompletionContextProvider.GetResolver(GetState(), unit) : null; + var parameterType = astResolver.Resolve(pd.Type); + // Type is always a name context -> return null + if (parameterType != null && !parameterType.IsError) + return null; + } + } + + AddContextCompletion(wrapper, csResolver, node); + + return wrapper.Result; + } + + static void AddAttributeProperties(CompletionDataWrapper wrapper, ResolveResult resolved) + { + if (resolved == null || resolved.Type.Kind == TypeKind.Unknown) + return; + + foreach (var property in resolved.Type.GetProperties (p => p.Accessibility == Accessibility.Public)) { + var data = wrapper.AddMember(property); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + foreach (var field in resolved.Type.GetFields (p => p.Accessibility == Accessibility.Public)) { + var data = wrapper.AddMember(field); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + foreach (var constructor in resolved.Type.GetConstructors (p => p.Accessibility == Accessibility.Public)) { + foreach (var p in constructor.Parameters) { + wrapper.AddNamedParameterVariable(p); + } + } + } + + 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)) { + continue; + } + wrapper.AddVariable(variable); + } + } + + if (state.CurrentMember is IParameterizedMember && !(node is AstType)) { + var param = (IParameterizedMember)state.CurrentMember; + foreach (var p in param.Parameters) { + wrapper.AddVariable(p); + } + } + + if (state.CurrentMember is IMethod) { + var method = (IMethod)state.CurrentMember; + foreach (var p in method.TypeParameters) { + wrapper.AddTypeParameter(p); + } + } + + Func typePred = null; + if (IsAttributeContext(node)) { + var attribute = Compilation.FindType(KnownTypeCode.Attribute); + typePred = t => t.GetAllBaseTypeDefinitions().Any(bt => bt.Equals(attribute)) ? t : null; + } + if (node != null && node.Role == Roles.BaseType) { + typePred = t => { + var def = t.GetDefinition(); + if (def != null && t.Kind != TypeKind.Interface && (def.IsSealed || def.IsStatic)) + return null; + return t; + }; + } + + if (node != null && !(node is NamespaceDeclaration) || 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); + if (LanguageVersion.Major >= 5) + AddKeywords(wrapper, new [] { "await" }); + AddKeywords(wrapper, expressionLevelKeywords); + if (node == null || node is TypeDeclaration) + AddKeywords(wrapper, typeLevelKeywords); + } else if (currentType != null) { + AddKeywords(wrapper, typeLevelKeywords); + } else { + if (!isInGlobalDelegate && !(node is Attribute)) + AddKeywords(wrapper, globalLevelKeywords); + } + var prop = currentMember as IUnresolvedProperty; + if (prop != null && prop.Setter != null && prop.Setter.Region.IsInside(location)) { + wrapper.AddCustom("value"); + } + if (currentMember is IUnresolvedEvent) { + wrapper.AddCustom("value"); + } + + if (IsInSwitchContext(node)) { + if (IncludeKeywordsInCompletionList) + wrapper.AddCustom("case"); + } + } else { + if (((AstType)node).Parent is ParameterDeclaration) { + AddKeywords(wrapper, parameterTypePredecessorKeywords); + } + } + + 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)) { + if (IncludeKeywordsInCompletionList) { + wrapper.AddCustom("var"); + wrapper.AddCustom("dynamic"); + } + } + wrapper.Result.AddRange(factory.CreateCodeTemplateCompletionData()); + if (node != null && node.Role == Roles.Argument) { + var resolved = ResolveExpression(node.Parent); + var invokeResult = resolved != null ? resolved.Result as CSharpInvocationResolveResult : null; + if (invokeResult != null) { + int argNum = 0; + foreach (var arg in node.Parent.Children.Where (c => c.Role == Roles.Argument)) { + if (arg == node) { + break; + } + argNum++; + } + var param = argNum < invokeResult.Member.Parameters.Count ? invokeResult.Member.Parameters [argNum] : null; + if (param != null && param.Type.Kind == TypeKind.Enum) { + AddEnumMembers(wrapper, param.Type, state); + } + } + } + + if (node is Expression) { + var root = node; + while (root.Parent != null) + root = root.Parent; + var astResolver = CompletionContextProvider.GetResolver(state, root); + foreach (var type in TypeGuessing.GetValidTypes(astResolver, (Expression)node)) { + if (type.Kind == TypeKind.Enum) { + AddEnumMembers(wrapper, type, state); + } else if (type.Kind == TypeKind.Delegate) { + AddDelegateHandlers(wrapper, type, false, true); + AutoSelect = false; + AutoCompleteEmptyMatch = false; + } + } + } + + // Add 'this' keyword for first parameter (extension method case) + if (node != null && node.Parent is ParameterDeclaration && + node.Parent.PrevSibling != null && node.Parent.PrevSibling.Role == Roles.LPar && IncludeKeywordsInCompletionList) { + wrapper.AddCustom("this"); + } + } + + static bool IsInSwitchContext(AstNode node) + { + var n = node; + while (n != null && !(n is EntityDeclaration)) { + if (n is SwitchStatement) { + return true; + } + if (n is BlockStatement) { + return false; + } + n = n.Parent; + } + return false; + } + + static bool ListEquals(List curNamespaces, List oldNamespaces) + { + if (oldNamespaces == null || curNamespaces.Count != oldNamespaces.Count) + return false; + for (int i = 0; i < curNamespaces.Count; i++) { + if (curNamespaces [i].FullName != oldNamespaces [i].FullName) { + return false; + } + } + return true; + } + + void AddTypesAndNamespaces(CompletionDataWrapper wrapper, CSharpResolver state, AstNode node, Func typePred = null, Predicate memberPred = null, Action callback = null, bool onlyAddConstructors = false) + { + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); + + if (currentType != null) { + for (var ct = ctx.CurrentTypeDefinition; ct != null; ct = ct.DeclaringTypeDefinition) { + foreach (var nestedType in ct.GetNestedTypes ()) { + if (!lookup.IsAccessible(nestedType.GetDefinition(), true)) + continue; + if (onlyAddConstructors) { + if (!nestedType.GetConstructors().Any(c => lookup.IsAccessible(c, true))) + continue; + } + + if (typePred == null) { + if (onlyAddConstructors) + wrapper.AddConstructors(nestedType, false, IsAttributeContext(node)); + else + wrapper.AddType(nestedType, false, IsAttributeContext(node)); + continue; + } + + var type = typePred(nestedType); + if (type != null) { + var a2 = onlyAddConstructors ? wrapper.AddConstructors(type, false, IsAttributeContext(node)) : wrapper.AddType(type, false, IsAttributeContext(node)); + if (a2 != null && callback != null) { + callback(a2, type); + } + } + continue; + } + } + + if (this.currentMember != null && !(node is AstType)) { + 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 (m => currentMember.IsStatic ? m.IsStatic : true)) { + if (member is IMethod && ((IMethod)member).FullName == "System.Object.Finalize") { + continue; + } + if (member.SymbolKind == SymbolKind.Operator) { + continue; + } + if (member.IsExplicitInterfaceImplementation) { + continue; + } + if (!lookup.IsAccessible(member, isProtectedAllowed)) { + continue; + } + if (memberPred == null || memberPred(member)) { + wrapper.AddMember(member); + } + } + var declaring = def.DeclaringTypeDefinition; + while (declaring != null) { + foreach (var member in declaring.GetMembers (m => m.IsStatic)) { + if (memberPred == null || memberPred(member)) { + wrapper.AddMember(member); + } + } + declaring = declaring.DeclaringTypeDefinition; + } + } + } + if (ctx.CurrentTypeDefinition != null) { + foreach (var p in ctx.CurrentTypeDefinition.TypeParameters) { + wrapper.AddTypeParameter(p); + } + } + } + var scope = ctx.CurrentUsingScope; + + for (var n = scope; n != null; n = n.Parent) { + foreach (var pair in n.UsingAliases) { + wrapper.AddAlias(pair.Key); + } + foreach (var alias in n.ExternAliases) { + wrapper.AddAlias(alias); + } + foreach (var u in n.Usings) { + foreach (var type in u.Types) { + if (!lookup.IsAccessible(type, false)) + continue; + + IType addType = typePred != null ? typePred(type) : type; + + if (onlyAddConstructors && addType != null) { + if (!addType.GetConstructors().Any(c => lookup.IsAccessible(c, true))) + continue; + } + + if (addType != null) { + var a = onlyAddConstructors ? wrapper.AddConstructors(addType, false, IsAttributeContext(node)) : wrapper.AddType(addType, false, IsAttributeContext(node)); + if (a != null && callback != null) { + callback(a, type); + } + } + } + } + + foreach (var type in n.Namespace.Types) { + if (!lookup.IsAccessible(type, false)) + continue; + IType addType = typePred != null ? typePred(type) : type; + if (onlyAddConstructors && addType != null) { + if (!addType.GetConstructors().Any(c => lookup.IsAccessible(c, true))) + continue; + } + + if (addType != null) { + var a2 = onlyAddConstructors ? wrapper.AddConstructors(addType, false, IsAttributeContext(node)) : wrapper.AddType(addType, false); + if (a2 != null && callback != null) { + callback(a2, type); + } + } + } + } + + for (var n = scope; n != null; n = n.Parent) { + foreach (var curNs in n.Namespace.ChildNamespaces) { + wrapper.AddNamespace(lookup, curNs); + } + } + + if (node is AstType && node.Parent is Constraint && IncludeKeywordsInCompletionList) { + wrapper.AddCustom("new()"); + } + + if (AutomaticallyAddImports) { + state = GetState(); + ICompletionData[] importData; + + var namespaces = new List(); + for (var n = ctx.CurrentUsingScope; n != null; n = n.Parent) { + namespaces.Add(n.Namespace); + foreach (var u in n.Usings) + namespaces.Add(u); + } + + if (this.CompletionEngineCache != null && ListEquals(namespaces, CompletionEngineCache.namespaces)) { + importData = CompletionEngineCache.importCompletion; + } else { + // flatten usings + var importList = new List(); + var dict = new Dictionary>(); + foreach (var type in Compilation.GetTopLevelTypeDefinitons ()) { + if (!lookup.IsAccessible(type, false)) + continue; + if (namespaces.Any(n => n.FullName == type.Namespace)) + continue; + bool useFullName = false; + foreach (var ns in namespaces) { + if (ns.GetTypeDefinition(type.Name, type.TypeParameterCount) != null) { + useFullName = true; + break; + } + } + + if (onlyAddConstructors) { + if (!type.GetConstructors().Any(c => lookup.IsAccessible(c, true))) + continue; + } + var data = factory.CreateImportCompletionData(type, useFullName, onlyAddConstructors); + Dictionary createdDict; + if (!dict.TryGetValue(type.Name, out createdDict)) { + createdDict = new Dictionary(); + dict.Add(type.Name, createdDict); + } + ICompletionData oldData; + if (!createdDict.TryGetValue(type.Namespace, out oldData)) { + importList.Add(data); + createdDict.Add(type.Namespace, data); + } else { + oldData.AddOverload(data); + } + } + + importData = importList.ToArray(); + if (CompletionEngineCache != null) { + CompletionEngineCache.namespaces = namespaces; + CompletionEngineCache.importCompletion = importData; + } + } + foreach (var data in importData) { + wrapper.Result.Add(data); + } + + + } + + } + + IEnumerable HandleKeywordCompletion(int wordStart, string word) + { + if (IsInsideCommentStringOrDirective()) { + if (IsInPreprocessorDirective()) { + if (word == "if" || word == "elif") { + if (wordStart > 0 && document.GetCharAt(wordStart - 1) == '#') { + return factory.CreatePreProcessorDefinesCompletionData(); + } + } + } + return null; + } + switch (word) { + case "namespace": + return null; + case "using": + if (currentType != null) { + return null; + } + var wrapper = new CompletionDataWrapper(this); + AddTypesAndNamespaces(wrapper, GetState(), null, t => null); + return wrapper.Result; + case "case": + return CreateCaseCompletionData(location); + // case ",": + // case ":": + // if (result.ExpressionContext == ExpressionContext.InheritableType) { + // IType cls = NRefactoryResolver.GetTypeAtCursor (Document.CompilationUnit, Document.FileName, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); + // CompletionDataList completionList = new ProjectDomCompletionDataList (); + // List namespaceList = GetUsedNamespaces (); + // var col = new CSharpTextEditorCompletion.CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, null, location); + // bool isInterface = false; + // HashSet baseTypeNames = new HashSet (); + // if (cls != null) { + // baseTypeNames.Add (cls.Name); + // if (cls.ClassType == ClassType.Struct) + // isInterface = true; + // } + // int tokenIndex = offset; + // + // // Search base types " : [Type1, ... ,TypeN,] " + // string token = null; + // do { + // token = GetPreviousToken (ref tokenIndex, false); + // if (string.IsNullOrEmpty (token)) + // break; + // token = token.Trim (); + // if (Char.IsLetterOrDigit (token [0]) || token [0] == '_') { + // IType baseType = dom.SearchType (Document.CompilationUnit, cls, result.Region.Start, token); + // if (baseType != null) { + // if (baseType.ClassType != ClassType.Interface) + // isInterface = true; + // baseTypeNames.Add (baseType.Name); + // } + // } + // } while (token != ":"); + // foreach (object o in dom.GetNamespaceContents (namespaceList, true, true)) { + // IType type = o as IType; + // if (type != null && (type.IsStatic || type.IsSealed || baseTypeNames.Contains (type.Name) || isInterface && type.ClassType != ClassType.Interface)) { + // continue; + // } + // if (o is Namespace && !namespaceList.Any (ns => ns.StartsWith (((Namespace)o).FullName))) + // continue; + // col.Add (o); + // } + // // Add inner classes + // Stack innerStack = new Stack (); + // innerStack.Push (cls); + // while (innerStack.Count > 0) { + // IType curType = innerStack.Pop (); + // if (curType == null) + // continue; + // foreach (IType innerType in curType.InnerTypes) { + // if (innerType != cls) + // // don't add the calling class as possible base type + // col.Add (innerType); + // } + // if (curType.DeclaringType != null) + // innerStack.Push (curType.DeclaringType); + // } + // return completionList; + // } + // break; + case "is": + case "as": + if (currentType == null) { + return null; + } + IType isAsType = null; + var isAsExpression = GetExpressionAt(wordStart); + if (isAsExpression != null) { + var parent = isAsExpression.Node.Parent; + if (parent is VariableInitializer) { + parent = parent.Parent; + } + var varDecl = parent as VariableDeclarationStatement; + if (varDecl != null) { + ExpressionResolveResult resolved; + if (varDecl.Type.IsVar()) { + resolved = null; + } else { + resolved = ResolveExpression(parent); + } + if (resolved != null) { + isAsType = resolved.Result.Type; + } + } + } + var isAsWrapper = new CompletionDataWrapper(this); + var def = isAsType != null ? isAsType.GetDefinition() : null; + AddTypesAndNamespaces( + isAsWrapper, + GetState(), + null, + t => t.GetDefinition() == null || def == null || t.GetDefinition().IsDerivedFrom(def) ? t : null, + m => false); + AddKeywords(isAsWrapper, primitiveTypesKeywords); + return isAsWrapper.Result; + // { + // CompletionDataList completionList = new ProjectDomCompletionDataList (); + // ExpressionResult expressionResult = FindExpression (dom, completionContext, wordStart - document.Caret.Offset); + // NRefactoryResolver resolver = CreateResolver (); + // ResolveResult resolveResult = resolver.Resolve (expressionResult, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); + // if (resolveResult != null && resolveResult.ResolvedType != null) { + // CompletionDataCollector col = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); + // IType foundType = null; + // if (word == "as") { + // ExpressionContext exactContext = new NewCSharpExpressionFinder (dom).FindExactContextForAsCompletion (document, Document.CompilationUnit, Document.FileName, resolver.CallingType); + // if (exactContext is ExpressionContext.TypeExpressionContext) { + // foundType = resolver.SearchType (((ExpressionContext.TypeExpressionContext)exactContext).Type); + // AddAsCompletionData (col, foundType); + // } + // } + // + // if (foundType == null) + // foundType = resolver.SearchType (resolveResult.ResolvedType); + // + // if (foundType != null) { + // if (foundType.ClassType == ClassType.Interface) + // foundType = resolver.SearchType (DomReturnType.Object); + // + // foreach (IType type in dom.GetSubclasses (foundType)) { + // if (type.IsSpecialName || type.Name.StartsWith ("<")) + // continue; + // AddAsCompletionData (col, type); + // } + // } + // List namespaceList = GetUsedNamespaces (); + // foreach (object o in dom.GetNamespaceContents (namespaceList, true, true)) { + // if (o is IType) { + // IType type = (IType)o; + // if (type.ClassType != ClassType.Interface || type.IsSpecialName || type.Name.StartsWith ("<")) + // continue; + // // if (foundType != null && !dom.GetInheritanceTree (foundType).Any (x => x.FullName == type.FullName)) + // // continue; + // AddAsCompletionData (col, type); + // continue; + // } + // if (o is Namespace) + // continue; + // col.Add (o); + // } + // return completionList; + // } + // result.ExpressionContext = ExpressionContext.TypeName; + // return CreateCtrlSpaceCompletionData (completionContext, result); + // } + case "override": + // Look for modifiers, in order to find the beginning of the declaration + int firstMod = wordStart; + int i = wordStart; + for (int n = 0; n < 3; n++) { + string mod = GetPreviousToken(ref i, true); + if (mod == "public" || mod == "protected" || mod == "private" || mod == "internal" || mod == "sealed") { + firstMod = i; + } else if (mod == "static") { + // static methods are not overridable + return null; + } else { + break; + } + } + if (!IsLineEmptyUpToEol()) { + return null; + } + if (currentType != null && (currentType.Kind == TypeKind.Class || currentType.Kind == TypeKind.Struct)) { + string modifiers = document.GetText(firstMod, wordStart - firstMod); + return GetOverrideCompletionData(currentType, modifiers); + } + return null; + case "partial": + // Look for modifiers, in order to find the beginning of the declaration + firstMod = wordStart; + i = wordStart; + for (int n = 0; n < 3; n++) { + string mod = GetPreviousToken(ref i, true); + if (mod == "public" || mod == "protected" || mod == "private" || mod == "internal" || mod == "sealed") { + firstMod = i; + } else if (mod == "static") { + // static methods are not overridable + return null; + } else { + break; + } + } + if (!IsLineEmptyUpToEol()) { + return null; + } + var state = GetState(); + + if (state.CurrentTypeDefinition != null && (state.CurrentTypeDefinition.Kind == TypeKind.Class || state.CurrentTypeDefinition.Kind == TypeKind.Struct)) { + string modifiers = document.GetText(firstMod, wordStart - firstMod); + return GetPartialCompletionData(state.CurrentTypeDefinition, modifiers); + } + return null; + + case "public": + case "protected": + case "private": + case "internal": + case "sealed": + case "static": + var accessorContext = HandleAccessorContext(); + if (accessorContext != null) { + return accessorContext; + } + return null; + case "new": + int j = offset - 4; + // string token = GetPreviousToken (ref j, true); + + IType hintType = null; + var expressionOrVariableDeclaration = GetNewExpressionAt(j); + if (expressionOrVariableDeclaration == null) + return null; + var astResolver = CompletionContextProvider.GetResolver(GetState(), expressionOrVariableDeclaration.Node.Ancestors.FirstOrDefault(n => n is EntityDeclaration || n is SyntaxTree)); + hintType = TypeGuessing.GetValidTypes( + astResolver, + expressionOrVariableDeclaration.Node + ).FirstOrDefault(); + + return CreateConstructorCompletionData(hintType); + case "yield": + var yieldDataList = new CompletionDataWrapper(this); + DefaultCompletionString = "return"; + if (IncludeKeywordsInCompletionList) { + yieldDataList.AddCustom("break"); + yieldDataList.AddCustom("return"); + } + return yieldDataList.Result; + case "in": + var inList = new CompletionDataWrapper(this); + + var expr = GetExpressionAtCursor(); + if (expr == null) + return null; + var rr = ResolveExpression(expr); + + AddContextCompletion( + inList, + rr != null ? rr.Resolver : GetState(), + expr.Node + ); + return inList.Result; + } + return null; + } + + bool IsLineEmptyUpToEol() + { + var line = document.GetLineByNumber(location.Line); + for (int j = offset; j < line.EndOffset; j++) { + char ch = document.GetCharAt(j); + if (!char.IsWhiteSpace(ch)) { + return false; + } + } + return true; + } + + string GetLineIndent(int lineNr) + { + var line = document.GetLineByNumber(lineNr); + for (int j = line.Offset; j < line.EndOffset; j++) { + char ch = document.GetCharAt(j); + if (!char.IsWhiteSpace(ch)) { + return document.GetText(line.Offset, j - line.Offset); + } + } + return ""; + } + // static CSharpAmbience amb = new CSharpAmbience(); + class Category : CompletionCategory + { + public Category(string displayText, string icon) : base(displayText, icon) + { + } + + public override int CompareTo(CompletionCategory other) + { + return 0; + } + } + + IEnumerable CreateConstructorCompletionData(IType hintType) + { + var wrapper = new CompletionDataWrapper(this); + var state = GetState(); + Func pred = null; + Action typeCallback = null; + var inferredTypesCategory = new Category("Inferred Types", null); + var derivedTypesCategory = new Category("Derived Types", null); + + if (hintType != null && (hintType.Kind != TypeKind.TypeParameter || IsTypeParameterInScope(hintType))) { + if (hintType.Kind != TypeKind.Unknown) { + var lookup = new MemberLookup( + ctx.CurrentTypeDefinition, + Compilation.MainAssembly + ); + typeCallback = (data, t) => { + //check if type is in inheritance tree. + if (hintType.GetDefinition() != null && + t.GetDefinition() != null && + t.GetDefinition().IsDerivedFrom(hintType.GetDefinition())) { + data.CompletionCategory = derivedTypesCategory; + } + }; + pred = t => { + if (t.Kind == TypeKind.Interface && hintType.Kind != TypeKind.Array) { + return null; + } + // check for valid constructors + if (t.GetConstructors().Any()) { + bool isProtectedAllowed = currentType != null ? + currentType.Resolve(ctx).GetDefinition().IsDerivedFrom(t.GetDefinition()) : false; + if (!t.GetConstructors().Any(m => lookup.IsAccessible(m, isProtectedAllowed))) { + return null; + } + } + + // check derived types + var typeDef = t.GetDefinition(); + var hintDef = hintType.GetDefinition(); + if (typeDef != null && hintDef != null && typeDef.IsDerivedFrom(hintDef)) { + var newType = wrapper.AddType(t, true); + if (newType != null) { + newType.CompletionCategory = inferredTypesCategory; + } + } + + // check type inference + var typeInference = new TypeInference(Compilation); + typeInference.Algorithm = TypeInferenceAlgorithm.ImprovedReturnAllResults; + + var inferedType = typeInference.FindTypeInBounds(new [] { t }, new [] { hintType }); + if (inferedType != SpecialType.UnknownType) { + var newType = wrapper.AddType(inferedType, true); + if (newType != null) { + newType.CompletionCategory = inferredTypesCategory; + } + return null; + } + return t; + }; + if (!(hintType.Kind == TypeKind.Interface && hintType.Kind != TypeKind.Array)) { + var hint = wrapper.AddType(hintType, true); + if (hint != null) { + DefaultCompletionString = hint.DisplayText; + hint.CompletionCategory = derivedTypesCategory; + } + } + 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 ArrayType(ctx.Compilation, arg, 1); + wrapper.AddType(array, true); + } + } + } else { + var hint = wrapper.AddType(hintType, true); + if (hint != null) { + DefaultCompletionString = hint.DisplayText; + hint.CompletionCategory = derivedTypesCategory; + } + } + } + AddTypesAndNamespaces(wrapper, state, null, pred, m => false, typeCallback, true); + if (hintType == null || hintType == SpecialType.UnknownType) { + AddKeywords(wrapper, primitiveTypesKeywords.Where(k => k != "void")); + } + + CloseOnSquareBrackets = true; + AutoCompleteEmptyMatch = true; + AutoCompleteEmptyMatchOnCurlyBracket = false; + return wrapper.Result; + } + + bool IsTypeParameterInScope(IType hintType) + { + var tp = hintType as ITypeParameter; + var ownerName = tp.Owner.ReflectionName; + if (currentMember != null && ownerName == currentMember.ReflectionName) + return true; + var ot = currentType; + while (ot != null) { + if (ownerName == ot.ReflectionName) + return true; + ot = ot.DeclaringTypeDefinition; + } + return false; + } + + IEnumerable GetOverrideCompletionData(IUnresolvedTypeDefinition type, string modifiers) + { + var wrapper = new CompletionDataWrapper(this); + var alreadyInserted = new List(); + //bool addedVirtuals = false; + + int declarationBegin = offset; + int j = declarationBegin; + for (int i = 0; i < 3; i++) { + switch (GetPreviousToken(ref j, true)) { + case "public": + case "protected": + case "private": + case "internal": + case "sealed": + case "override": + case "partial": + case "async": + declarationBegin = j; + break; + case "static": + return null; // don't add override completion for static members + } + } + AddVirtuals( + alreadyInserted, + wrapper, + modifiers, + type.Resolve(ctx), + declarationBegin + ); + return wrapper.Result; + } + + IEnumerable GetPartialCompletionData(ITypeDefinition type, string modifiers) + { + var wrapper = new CompletionDataWrapper(this); + int declarationBegin = offset; + int j = declarationBegin; + for (int i = 0; i < 3; i++) { + switch (GetPreviousToken(ref j, true)) { + case "public": + case "protected": + case "private": + case "internal": + case "sealed": + case "override": + case "partial": + case "async": + declarationBegin = j; + break; + case "static": + return null; // don't add override completion for static members + } + } + + var methods = new List(); + + foreach (var part in type.Parts) { + foreach (var method in part.Methods) { + if (method.BodyRegion.IsEmpty) { + if (GetImplementation(type, method) != null) { + continue; + } + methods.Add(method); + } + } + } + + foreach (var method in methods) { + wrapper.Add(factory.CreateNewPartialCompletionData( + declarationBegin, + method.DeclaringTypeDefinition, + method + ) + ); + } + + return wrapper.Result; + } + + IMethod GetImplementation(ITypeDefinition type, IUnresolvedMethod method) + { + foreach (var cur in type.Methods) { + if (cur.Name == method.Name && cur.Parameters.Count == method.Parameters.Count && !cur.BodyRegion.IsEmpty) { + bool equal = true; + /* for (int i = 0; i < cur.Parameters.Count; i++) { + if (!cur.Parameters [i].Type.Equals (method.Parameters [i].Type)) { + equal = false; + break; + } + }*/ + if (equal) { + return cur; + } + } + } + return null; + } + + protected virtual void AddVirtuals(List alreadyInserted, CompletionDataWrapper col, string modifiers, IType curType, int declarationBegin) + { + if (curType == null) { + return; + } + foreach (var m in curType.GetMembers ().Reverse ()) { + if (curType.Kind != TypeKind.Interface && !m.IsOverridable) { + continue; + } + // filter out the "Finalize" methods, because finalizers should be done with destructors. + if (m is IMethod && m.Name == "Finalize") { + continue; + } + + var data = factory.CreateNewOverrideCompletionData( + declarationBegin, + currentType, + m + ); + // check if the member is already implemented + bool foundMember = curType.GetMembers().Any(cm => SignatureComparer.Ordinal.Equals( + cm, + m + ) && cm.DeclaringTypeDefinition == curType.GetDefinition() + ); + if (foundMember) { + continue; + } + if (alreadyInserted.Any(cm => SignatureComparer.Ordinal.Equals(cm, m))) + continue; + alreadyInserted.Add(m); + data.CompletionCategory = col.GetCompletionCategory(m.DeclaringTypeDefinition); + col.Add(data); + } + } + + void AddKeywords(CompletionDataWrapper wrapper, IEnumerable keywords) + { + if (!IncludeKeywordsInCompletionList) + return; + foreach (string keyword in keywords) { + if (wrapper.Result.Any(data => data.DisplayText == keyword)) + continue; + wrapper.AddCustom(keyword); + } + } + + public string GuessEventHandlerMethodName(int tokenIndex, string surroundingTypeName) + { + var names = new List(); + + string eventName = GetPreviousToken(ref tokenIndex, false); + string result = GetPreviousToken(ref tokenIndex, false); + if (result != ".") { + if (surroundingTypeName == null) { + eventName = "Handle" + eventName; + } else { + names.Add(surroundingTypeName); + } + } + while (result == ".") { + result = GetPreviousToken(ref tokenIndex, false); + if (result == "this") { + if (names.Count == 0) { + if (surroundingTypeName == null) { + eventName = "Handle" + eventName; + } else { + names.Add(surroundingTypeName); + } + } + } else if (result != null) { + string trimmedName = result.Trim(); + if (trimmedName.Length == 0) { + break; + } + names.Insert(0, trimmedName); + } + result = GetPreviousToken(ref tokenIndex, false); + } + if (!string.IsNullOrEmpty(eventName)) { + names.Add(eventName); + } + result = String.Join("_", names.ToArray()); + foreach (char ch in result) { + if (!char.IsLetterOrDigit(ch) && ch != '_') { + result = ""; + break; + } + } + return result; + } + + bool MatchDelegate(IType delegateType, IMethod method) + { + if (method.SymbolKind != SymbolKind.Method) + return false; + var delegateMethod = delegateType.GetDelegateInvokeMethod(); + if (delegateMethod == null || delegateMethod.Parameters.Count != method.Parameters.Count) { + return false; + } + + for (int i = 0; i < delegateMethod.Parameters.Count; i++) { + if (!delegateMethod.Parameters [i].Type.Equals(method.Parameters [i].Type)) { + return false; + } + } + return true; + } + + string AddDelegateHandlers(CompletionDataWrapper completionList, IType delegateType, bool addSemicolon = true, bool addDefault = true, string optDelegateName = null) + { + IMethod delegateMethod = delegateType.GetDelegateInvokeMethod(); + PossibleDelegates.Add(delegateMethod); + var thisLineIndent = GetLineIndent(location.Line); + string delegateEndString = EolMarker + thisLineIndent + "}" + (addSemicolon ? ";" : ""); + //bool containsDelegateData = completionList.Result.Any(d => d.DisplayText.StartsWith("delegate(")); + if (addDefault && !completionList.AnonymousDelegateAdded) { + completionList.AnonymousDelegateAdded = true; + var oldDelegate = completionList.Result.FirstOrDefault(cd => cd.DisplayText == "delegate"); + if (oldDelegate != null) + completionList.Result.Remove(oldDelegate); + completionList.AddCustom( + "delegate", + "Creates anonymous delegate.", + "delegate {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString + ).DisplayFlags |= DisplayFlags.MarkedBold; + if (LanguageVersion.Major >= 5) { + completionList.AddCustom( + "async delegate", + "Creates anonymous async delegate.", + "async delegate {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString + ).DisplayFlags |= DisplayFlags.MarkedBold; + } + } + var sb = new StringBuilder("("); + var sbWithoutTypes = new StringBuilder("("); + var state = GetState(); + var builder = new TypeSystemAstBuilder(state); + + for (int k = 0; k < delegateMethod.Parameters.Count; k++) { + + if (k > 0) { + sb.Append(", "); + sbWithoutTypes.Append(", "); + } + var convertedParameter = builder.ConvertParameter(delegateMethod.Parameters [k]); + if (convertedParameter.ParameterModifier == ParameterModifier.Params) + convertedParameter.ParameterModifier = ParameterModifier.None; + sb.Append(convertedParameter.ToString(FormattingPolicy)); + sbWithoutTypes.Append(delegateMethod.Parameters [k].Name); + } + + sb.Append(")"); + sbWithoutTypes.Append(")"); + var signature = sb.ToString(); + if (!completionList.HasAnonymousDelegateAdded(signature)) { + completionList.AddAnonymousDelegateAdded(signature); + + completionList.AddCustom( + "delegate" + signature, + "Creates anonymous delegate.", + "delegate" + signature + " {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString + ).DisplayFlags |= DisplayFlags.MarkedBold; + if (LanguageVersion.Major >= 5) { + completionList.AddCustom( + "async delegate" + signature, + "Creates anonymous async delegate.", + "async delegate" + signature + " {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString + ).DisplayFlags |= DisplayFlags.MarkedBold; + } + if (!completionList.Result.Any(data => data.DisplayText == sb.ToString())) { + completionList.AddCustom( + signature, + "Creates typed lambda expression.", + signature + " => |" + (addSemicolon ? ";" : "") + ).DisplayFlags |= DisplayFlags.MarkedBold; + if (LanguageVersion.Major >= 5) { + completionList.AddCustom( + "async " + signature, + "Creates typed async lambda expression.", + "async " + signature + " => |" + (addSemicolon ? ";" : "") + ).DisplayFlags |= DisplayFlags.MarkedBold; + } + + 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 ? ";" : "") + ).DisplayFlags |= DisplayFlags.MarkedBold; + if (LanguageVersion.Major >= 5) { + completionList.AddCustom( + "async " + sbWithoutTypes, + "Creates async lambda expression.", + "async " + sbWithoutTypes + " => |" + (addSemicolon ? ";" : "") + ).DisplayFlags |= DisplayFlags.MarkedBold; + } + } + } + + } + + string varName = optDelegateName ?? "Handle" + delegateType.Name; + + var ecd = factory.CreateEventCreationCompletionData(varName, delegateType, null, signature, currentMember, currentType); + ecd.DisplayFlags |= DisplayFlags.MarkedBold; + completionList.Add(ecd); + + return sb.ToString(); + } + + bool IsAccessibleFrom(IEntity member, ITypeDefinition calledType, IMember currentMember, bool includeProtected) + { + if (currentMember == null) { + return member.IsStatic || member.IsPublic; + } + // if (currentMember is MonoDevelop.Projects.Dom.BaseResolveResult.BaseMemberDecorator) + // return member.IsPublic | member.IsProtected; + // if (member.IsStatic && !IsStatic) + // return false; + if (member.IsPublic || calledType != null && calledType.Kind == TypeKind.Interface && !member.IsProtected) { + return true; + } + if (member.DeclaringTypeDefinition != null) { + if (member.DeclaringTypeDefinition.Kind == TypeKind.Interface) { + return IsAccessibleFrom( + member.DeclaringTypeDefinition, + calledType, + currentMember, + includeProtected + ); + } + + if (member.IsProtected && !(member.DeclaringTypeDefinition.IsProtectedOrInternal && !includeProtected)) { + return includeProtected; + } + } + if (member.IsInternal || member.IsProtectedAndInternal || member.IsProtectedOrInternal) { + //var type1 = member is ITypeDefinition ? (ITypeDefinition)member : member.DeclaringTypeDefinition; + //var type2 = currentMember is ITypeDefinition ? (ITypeDefinition)currentMember : currentMember.DeclaringTypeDefinition; + bool result = true; + // easy case, projects are the same + /* // if (type1.ProjectContent == type2.ProjectContent) { + // result = true; + // } else + if (type1.ProjectContent != null) { + // maybe type2 hasn't project dom set (may occur in some cases), check if the file is in the project + //TODO !! + // result = type1.ProjectContent.Annotation ().GetProjectFile (type2.Region.FileName) != null; + result = false; + } else if (type2.ProjectContent != null) { + //TODO!! + // result = type2.ProjectContent.Annotation ().GetProjectFile (type1.Region.FileName) != null; + result = false; + } else { + // should never happen ! + result = true; + }*/ + return member.IsProtectedAndInternal ? includeProtected && result : result; + } + + if (!(currentMember is IType) && (currentMember.DeclaringTypeDefinition == null || member.DeclaringTypeDefinition == null)) { + return false; + } + + // inner class + var declaringType = currentMember.DeclaringTypeDefinition; + while (declaringType != null) { + if (declaringType.ReflectionName == currentMember.DeclaringType.ReflectionName) { + return true; + } + declaringType = declaringType.DeclaringTypeDefinition; + } + + + return currentMember.DeclaringTypeDefinition != null && member.DeclaringTypeDefinition.FullName == currentMember.DeclaringTypeDefinition.FullName; + } + + static bool IsAttributeContext(AstNode node) + { + AstNode n = node; + while (n is AstType) { + n = n.Parent; + } + return n is Attribute; + } + + IEnumerable CreateTypeAndNamespaceCompletionData(TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state) + { + if (resolveResult == null || resolveResult.IsError) { + return null; + } + var exprParent = resolvedNode.GetParent(); + var unit = exprParent != null ? exprParent.GetParent() : null; + + var astResolver = unit != null ? CompletionContextProvider.GetResolver(state, unit) : null; + IType hintType = exprParent != null && astResolver != null ? + TypeGuessing.GetValidTypes(astResolver, exprParent).FirstOrDefault() : + null; + var result = new CompletionDataWrapper(this); + var lookup = new MemberLookup( + ctx.CurrentTypeDefinition, + Compilation.MainAssembly + ); + if (resolveResult is NamespaceResolveResult) { + var nr = (NamespaceResolveResult)resolveResult; + if (!(resolvedNode.Parent is UsingDeclaration || resolvedNode.Parent != null && resolvedNode.Parent.Parent is UsingDeclaration)) { + foreach (var cl in nr.Namespace.Types) { + if (hintType != null && hintType.Kind != TypeKind.Array && cl.Kind == TypeKind.Interface) { + continue; + } + if (!lookup.IsAccessible(cl, false)) + continue; + result.AddType(cl, false, IsAttributeContext(resolvedNode)); + } + } + foreach (var ns in nr.Namespace.ChildNamespaces) { + result.AddNamespace(lookup, ns); + } + } else if (resolveResult is TypeResolveResult) { + var type = resolveResult.Type; + foreach (var nested in type.GetNestedTypes ()) { + if (hintType != null && hintType.Kind != TypeKind.Array && nested.Kind == TypeKind.Interface) { + continue; + } + var def = nested.GetDefinition(); + if (def != null && !lookup.IsAccessible(def, false)) + continue; + result.AddType(nested, false); + } + } + return result.Result; + } + + IEnumerable CreateTypeList() + { + foreach (var cl in Compilation.RootNamespace.Types) { + yield return factory.CreateTypeCompletionData(cl, false, false, false); + } + + foreach (var ns in Compilation.RootNamespace.ChildNamespaces) { + yield return factory.CreateNamespaceCompletionData(ns); + } + } + + void CreateParameterForInvocation(CompletionDataWrapper result, IMethod method, CSharpResolver state, int parameter, HashSet addedEnums, HashSet addedDelegates) + { + if (method.Parameters.Count <= parameter) { + return; + } + var resolvedType = method.Parameters [parameter].Type; + if (resolvedType.Kind == TypeKind.Enum) { + if (addedEnums.Contains(resolvedType.ReflectionName)) { + return; + } + addedEnums.Add(resolvedType.ReflectionName); + AddEnumMembers(result, resolvedType, state); + return; + } + + if (resolvedType.Kind == TypeKind.Delegate) { + if (addedDelegates.Contains(resolvedType.ReflectionName)) + return; + AddDelegateHandlers(result, resolvedType, false, true, "Handle" + method.Parameters [parameter].Type.Name + method.Parameters [parameter].Name); + } + } + + IEnumerable CreateParameterCompletion(MethodGroupResolveResult resolveResult, CSharpResolver state, AstNode invocation, SyntaxTree unit, int parameter, bool controlSpace) + { + var result = new CompletionDataWrapper(this); + var addedEnums = new HashSet(); + var addedDelegates = new HashSet(); + + foreach (var method in resolveResult.Methods) { + CreateParameterForInvocation(result, method, state, parameter, addedEnums, addedDelegates); + } + foreach (var methods in resolveResult.GetEligibleExtensionMethods (true)) { + foreach (var method in methods) { + if (resolveResult.Methods.Contains(method)) + continue; + CreateParameterForInvocation(result, new ReducedExtensionMethod(method), state, parameter, addedEnums, addedDelegates); + } + } + + foreach (var method in resolveResult.Methods) { + if (parameter < method.Parameters.Count && method.Parameters [parameter].Type.Kind == TypeKind.Delegate) { + AutoSelect = false; + AutoCompleteEmptyMatch = false; + } + foreach (var p in method.Parameters) { + result.AddNamedParameterVariable(p); + } + } + + if (!controlSpace) { + if (addedEnums.Count + addedDelegates.Count == 0) { + return Enumerable.Empty(); + } + AutoCompleteEmptyMatch = false; + AutoSelect = false; + } + AddContextCompletion(result, state, invocation); + + // resolver.AddAccessibleCodeCompletionData (ExpressionContext.MethodBody, cdc); + // if (addedDelegates.Count > 0) { + // foreach (var data in result.Result) { + // if (data is MemberCompletionData) + // ((MemberCompletionData)data).IsDelegateExpected = true; + // } + // } + return result.Result; + } + + void AddEnumMembers(CompletionDataWrapper completionList, IType resolvedType, CSharpResolver state) + { + if (resolvedType.Kind != TypeKind.Enum) { + return; + } + var type = completionList.AddEnumMembers(resolvedType, state); + if (type != null) + DefaultCompletionString = type.DisplayText; + } + + IEnumerable CreateCompletionData(TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state, Func typePred = null) + { + if (resolveResult == null /* || resolveResult.IsError*/) { + return null; + } + + var lookup = new MemberLookup( + ctx.CurrentTypeDefinition, + Compilation.MainAssembly + ); + + if (resolveResult is NamespaceResolveResult) { + var nr = (NamespaceResolveResult)resolveResult; + var namespaceContents = new CompletionDataWrapper(this); + + foreach (var cl in nr.Namespace.Types) { + if (!lookup.IsAccessible(cl, false)) + continue; + IType addType = typePred != null ? typePred(cl) : cl; + if (addType != null) + namespaceContents.AddType(addType, false); + } + + foreach (var ns in nr.Namespace.ChildNamespaces) { + namespaceContents.AddNamespace(lookup, ns); + } + return namespaceContents.Result; + } + IType type = resolveResult.Type; + + if (type.Namespace == "System" && type.Name == "Void") + return null; + + if (resolvedNode.Parent is PointerReferenceExpression && (type is PointerType)) { + resolveResult = new OperatorResolveResult(((PointerType)type).ElementType, System.Linq.Expressions.ExpressionType.Extension, resolveResult); + } + + //var typeDef = resolveResult.Type.GetDefinition(); + var result = new CompletionDataWrapper(this); + bool includeStaticMembers = false; + + if (resolveResult is LocalResolveResult) { + if (resolvedNode is IdentifierExpression) { + var mrr = (LocalResolveResult)resolveResult; + includeStaticMembers = mrr.Variable.Name == mrr.Type.Name; + } + } + if (resolveResult is TypeResolveResult && type.Kind == TypeKind.Enum) { + foreach (var field in type.GetFields ()) { + if (!lookup.IsAccessible(field, false)) + continue; + result.AddMember(field); + } + return result.Result; + } + + bool isProtectedAllowed = lookup.IsProtectedAccessAllowed(resolveResult); + bool skipNonStaticMembers = (resolveResult is TypeResolveResult); + + if (resolveResult is MemberResolveResult && resolvedNode is IdentifierExpression) { + var mrr = (MemberResolveResult)resolveResult; + includeStaticMembers = mrr.Member.Name == mrr.Type.Name; + + TypeResolveResult trr; + if (state.IsVariableReferenceWithSameType( + resolveResult, + ((IdentifierExpression)resolvedNode).Identifier, + out trr + )) { + if (currentMember != null && mrr.Member.IsStatic ^ currentMember.IsStatic) { + skipNonStaticMembers = true; + + if (trr.Type.Kind == TypeKind.Enum) { + foreach (var field in trr.Type.GetFields ()) { + if (lookup.IsAccessible(field, false)) + result.AddMember(field); + } + return result.Result; + } + } + } + // ADD Aliases + var scope = ctx.CurrentUsingScope; + + for (var n = scope; n != null; n = n.Parent) { + foreach (var pair in n.UsingAliases) { + if (pair.Key == mrr.Member.Name) { + foreach (var r in CreateCompletionData (location, pair.Value, resolvedNode, state)) { + if (r is IEntityCompletionData && ((IEntityCompletionData)r).Entity is IMember) { + result.AddMember((IMember)((IEntityCompletionData)r).Entity); + } else { + result.Add(r); + } + } + } + } + } + + + } + if (resolveResult is TypeResolveResult && (resolvedNode is IdentifierExpression || resolvedNode is MemberReferenceExpression)) { + includeStaticMembers = true; + } + + // Console.WriteLine ("type:" + type +"/"+type.GetType ()); + // Console.WriteLine ("current:" + ctx.CurrentTypeDefinition); + // Console.WriteLine ("IS PROT ALLOWED:" + isProtectedAllowed + " static: "+ includeStaticMembers); + // Console.WriteLine (resolveResult); + // Console.WriteLine ("node:" + resolvedNode); + // Console.WriteLine (currentMember != null ? currentMember.IsStatic : "currentMember == null"); + + 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.SymbolKind == SymbolKind.Indexer || member.SymbolKind == SymbolKind.Operator || member.SymbolKind == SymbolKind.Constructor || member.SymbolKind == SymbolKind.Destructor) { + continue; + } + if (resolvedNode is BaseReferenceExpression && member.IsAbstract) { + continue; + } + if (member is IType) { + if (resolveResult is TypeResolveResult || includeStaticMembers) { + if (!lookup.IsAccessible(member, isProtectedAllowed)) + continue; + result.AddType((IType)member, false); + continue; + } + } + bool memberIsStatic = member.IsStatic; + if (!includeStaticMembers && memberIsStatic && !(resolveResult is TypeResolveResult)) { + // Console.WriteLine ("skip static member: " + member.FullName); + continue; + } + + var field = member as IField; + if (field != null) { + memberIsStatic |= field.IsConst; + } + if (!memberIsStatic && skipNonStaticMembers) { + continue; + } + + if (member is IMethod && ((IMethod)member).FullName == "System.Object.Finalize") { + continue; + } + if (member.SymbolKind == SymbolKind.Operator) { + continue; + } + + if (member is IMember) { + result.AddMember((IMember)member); + } + } + } + + if (!(resolveResult is TypeResolveResult || includeStaticMembers)) { + foreach (var meths in state.GetExtensionMethods (type)) { + foreach (var m in meths) { + if (!lookup.IsAccessible(m, isProtectedAllowed)) + continue; + result.AddMember(new ReducedExtensionMethod(m)); + } + } + } + + // IEnumerable objects = resolveResult.CreateResolveResult (dom, resolver != null ? resolver.CallingMember : null); + // CompletionDataCollector col = new CompletionDataCollector (this, dom, result, Document.CompilationUnit, resolver != null ? resolver.CallingType : null, location); + // col.HideExtensionParameter = !resolveResult.StaticResolve; + // col.NamePrefix = expressionResult.Expression; + // bool showOnlyTypes = expressionResult.Contexts.Any (ctx => ctx == ExpressionContext.InheritableType || ctx == ExpressionContext.Constraints); + // if (objects != null) { + // foreach (object obj in objects) { + // if (expressionResult.ExpressionContext != null && expressionResult.ExpressionContext.FilterEntry (obj)) + // continue; + // if (expressionResult.ExpressionContext == ExpressionContext.NamespaceNameExcepted && !(obj is Namespace)) + // continue; + // if (showOnlyTypes && !(obj is IType)) + // continue; + // CompletionData data = col.Add (obj); + // if (data != null && expressionResult.ExpressionContext == ExpressionContext.Attribute && data.CompletionText != null && data.CompletionText.EndsWith ("Attribute")) { + // string newText = data.CompletionText.Substring (0, data.CompletionText.Length - "Attribute".Length); + // data.SetText (newText); + // } + // } + // } + + return result.Result; + } + + IEnumerable CreateCaseCompletionData(TextLocation location) + { + var unit = ParseStub("a: break;"); + if (unit == null) { + return null; + } + var s = unit.GetNodeAt(location); + if (s == null) { + return null; + } + + var offset = document.GetOffset(s.Expression.StartLocation); + var expr = GetExpressionAt(offset); + if (expr == null) { + return null; + } + + var resolveResult = ResolveExpression(expr); + if (resolveResult == null || resolveResult.Result.Type.Kind != TypeKind.Enum) { + return null; + } + var wrapper = new CompletionDataWrapper(this); + AddEnumMembers(wrapper, resolveResult.Result.Type, resolveResult.Resolver); + AutoCompleteEmptyMatch = false; + return wrapper.Result; + } + + #region Parsing methods + + ExpressionResult GetExpressionBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null) { + baseUnit = ParseStub("a", false); + var type = baseUnit.GetNodeAt(location); + if (type == null) { + baseUnit = ParseStub("a;", false); + type = baseUnit.GetNodeAt(location); + } + + if (type == null) { + baseUnit = ParseStub("A a;", false); + type = baseUnit.GetNodeAt(location); + } + if (type != null) { + return new ExpressionResult((AstNode)type.Target, baseUnit); + } + } + + baseUnit = ParseStub("ToString()", false); + var curNode = baseUnit.GetNodeAt(location); + // hack for local variable declaration missing ';' issue - remove that if it works. + if (curNode is EntityDeclaration || baseUnit.GetNodeAt(location) == null && baseUnit.GetNodeAt(location) == null) { + baseUnit = ParseStub("a"); + curNode = baseUnit.GetNodeAt(location); + } + + // Hack for handle object initializer continuation expressions + if (curNode is EntityDeclaration || baseUnit.GetNodeAt(location) == null && baseUnit.GetNodeAt(location) == null) { + baseUnit = ParseStub("a};"); + } + var mref = baseUnit.GetNodeAt(location); + if (currentMember == null && currentType == null) { + if (mref != null) { + return new ExpressionResult((AstNode)mref.Target, baseUnit); + } + return null; + } + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + if (mref == null) { + var type = baseUnit.GetNodeAt(location); + if (type != null) { + return new ExpressionResult((AstNode)type.Target, baseUnit); + } + + var pref = baseUnit.GetNodeAt(location); + if (pref != null) { + return new ExpressionResult((AstNode)pref.Target, baseUnit); + } + } + + if (mref == null) { + baseUnit = ParseStub("A a;", false); + var type = baseUnit.GetNodeAt(location); + if (type != null) { + return new ExpressionResult((AstNode)type.Target, baseUnit); + } + } + + AstNode expr = null; + if (mref != null) { + expr = mref.Target; + } else { + Expression tref = baseUnit.GetNodeAt(location); + MemberType memberType = tref != null ? ((TypeReferenceExpression)tref).Type as MemberType : null; + if (memberType == null) { + memberType = baseUnit.GetNodeAt(location); + if (memberType != null) { + if (memberType.Parent is ObjectCreateExpression) { + var mt = memberType.Target.Clone(); + memberType.ReplaceWith(mt); + expr = mt; + goto exit; + } else { + tref = baseUnit.GetNodeAt(location); + if (tref == null) { + tref = new TypeReferenceExpression(memberType.Clone()); + memberType.Parent.AddChild(tref, Roles.Expression); + } + if (tref is ObjectCreateExpression) { + expr = memberType.Target.Clone(); + expr.AddAnnotation(new ObjectCreateExpression()); + } + } + } + } + + if (memberType == null) { + return null; + } + if (expr == null) { + expr = memberType.Target.Clone(); + } + tref.ReplaceWith(expr); + } + exit: + return new ExpressionResult((AstNode)expr, baseUnit); + } + + ExpressionResult GetExpressionAtCursor() + { + // TextLocation memberLocation; + // if (currentMember != null) { + // memberLocation = currentMember.Region.Begin; + // } else if (currentType != null) { + // memberLocation = currentType.Region.Begin; + // } else { + // memberLocation = location; + // } + var baseUnit = ParseStub("a"); + var tmpUnit = baseUnit; + AstNode expr = baseUnit.GetNodeAt( + location, + n => n is IdentifierExpression || n is MemberReferenceExpression + ); + + if (expr == null) { + expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); + } + if (expr == null) + expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); + // try insertStatement + if (expr == null && baseUnit.GetNodeAt(location.Line, location.Column) != null) { + tmpUnit = baseUnit = ParseStub("a();", false); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column + 1 + ); + } + + if (expr == null) { + baseUnit = ParseStub("()"); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + if (expr == null) { + expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); + } + } + + if (expr == null) { + baseUnit = ParseStub("a", false); + expr = baseUnit.GetNodeAt( + location, + n => n is IdentifierExpression || n is MemberReferenceExpression || n is CatchClause + ); + } + + // try statement + if (expr == null) { + expr = tmpUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + baseUnit = tmpUnit; + } + + if (expr == null) { + var block = tmpUnit.GetNodeAt(location); + var node = block != null ? block.Statements.LastOrDefault() : null; + + var forStmt = node != null ? node.PrevSibling as ForStatement : null; + if (forStmt != null && forStmt.EmbeddedStatement.IsNull) { + expr = forStmt; + var id = new IdentifierExpression("stub"); + forStmt.EmbeddedStatement = new BlockStatement() { Statements = { new ExpressionStatement(id) } }; + expr = id; + baseUnit = tmpUnit; + } + } + + if (expr == null) { + var forStmt = tmpUnit.GetNodeAt( + location.Line, + location.Column - 3 + ); + if (forStmt != null && forStmt.EmbeddedStatement.IsNull) { + forStmt.VariableNameToken = Identifier.Create("stub"); + expr = forStmt.VariableNameToken; + baseUnit = tmpUnit; + } + } + if (expr == null) { + expr = tmpUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + baseUnit = tmpUnit; + } + + // try parameter declaration type + if (expr == null) { + baseUnit = ParseStub(">", false, "{}"); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + } + + // try parameter declaration method + if (expr == null) { + baseUnit = ParseStub("> ()", false, "{}"); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + } + + // try expression in anonymous type "new { sample = x$" case + if (expr == null) { + baseUnit = ParseStub("a", false); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column + ); + if (expr != null) { + expr = baseUnit.GetNodeAt(location.Line, location.Column) ?? expr; + } + if (expr == null) { + expr = baseUnit.GetNodeAt(location.Line, location.Column); + } + } + + // try lambda + if (expr == null) { + baseUnit = ParseStub("foo) => {}", false); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column + ); + } + + if (expr == null) + return null; + return new ExpressionResult(expr, baseUnit); + } + + ExpressionResult GetExpressionAt(int offset) + { + var parser = new CSharpParser(); + var text = GetMemberTextToCaret(); + + int closingBrackets = 0, generatedLines = 0; + var sb = CreateWrapper("a;", false, "", text.Item1, text.Item2, ref closingBrackets, ref generatedLines); + + var completionUnit = parser.Parse(sb.ToString()); + var offsetLocation = document.GetLocation(offset); + var loc = new TextLocation(offsetLocation.Line - text.Item2.Line + generatedLines + 1, offsetLocation.Column); + + var expr = completionUnit.GetNodeAt( + loc, + n => n is Expression || n is VariableDeclarationStatement + ); + if (expr == null) + return null; + return new ExpressionResult(expr, completionUnit); + } + + ExpressionResult GetNewExpressionAt(int offset) + { + var parser = new CSharpParser(); + var text = GetMemberTextToCaret(); + int closingBrackets = 0, generatedLines = 0; + var sb = CreateWrapper("a ();", false, "", text.Item1, text.Item2, ref closingBrackets, ref generatedLines); + + var completionUnit = parser.Parse(sb.ToString()); + var offsetLocation = document.GetLocation(offset); + var loc = new TextLocation(offsetLocation.Line - text.Item2.Line + generatedLines + 1, offsetLocation.Column); + + var expr = completionUnit.GetNodeAt(loc, n => n is Expression); + if (expr == null) { + // try without ";" + sb = CreateWrapper("a ()", false, "", text.Item1, text.Item2, ref closingBrackets, ref generatedLines); + completionUnit = parser.Parse(sb.ToString()); + + expr = completionUnit.GetNodeAt(loc, n => n is Expression); + if (expr == null) { + return null; + } + } + return new ExpressionResult(expr, completionUnit); + } + + #endregion + + #region Helper methods + + string GetPreviousToken(ref int i, bool allowLineChange) + { + char c; + if (i <= 0) { + return null; + } + + do { + c = document.GetCharAt(--i); + } while (i > 0 && char.IsWhiteSpace(c) && (allowLineChange ? true : c != '\n')); + + if (i == 0) { + return null; + } + + if (!char.IsLetterOrDigit(c)) { + return new string(c, 1); + } + + int endOffset = i + 1; + + do { + c = document.GetCharAt(i - 1); + if (!(char.IsLetterOrDigit(c) || c == '_')) { + break; + } + + i--; + } while (i > 0); + + return document.GetText(i, endOffset - i); + } + + #endregion + + #region Preprocessor + + IEnumerable GetDirectiveCompletionData() + { + yield return factory.CreateLiteralCompletionData("if"); + yield return factory.CreateLiteralCompletionData("else"); + yield return factory.CreateLiteralCompletionData("elif"); + yield return factory.CreateLiteralCompletionData("endif"); + yield return factory.CreateLiteralCompletionData("define"); + yield return factory.CreateLiteralCompletionData("undef"); + yield return factory.CreateLiteralCompletionData("warning"); + yield return factory.CreateLiteralCompletionData("error"); + yield return factory.CreateLiteralCompletionData("pragma"); + yield return factory.CreateLiteralCompletionData("line"); + yield return factory.CreateLiteralCompletionData("line hidden"); + yield return factory.CreateLiteralCompletionData("line default"); + yield return factory.CreateLiteralCompletionData("region"); + yield return factory.CreateLiteralCompletionData("endregion"); + } + + #endregion + + #region Xml Comments + + static readonly List commentTags = new List(new string[] { + "c", + "code", + "example", + "exception", + "include", + "list", + "listheader", + "item", + "term", + "description", + "para", + "param", + "paramref", + "permission", + "remarks", + "returns", + "see", + "seealso", + "summary", + "value" + } + ); + + public static IEnumerable CommentTags { + get { + return commentTags; + } + } + + string GetLastClosingXmlCommentTag() + { + var line = document.GetLineByNumber(location.Line); + + restart: + string lineText = document.GetText(line); + if (!lineText.Trim().StartsWith("///", StringComparison.Ordinal)) + return null; + int startIndex = Math.Min(location.Column - 1, lineText.Length - 1) - 1; + while (startIndex > 0 && lineText [startIndex] != '<') { + --startIndex; + if (lineText [startIndex] == '/') { + // already closed. + startIndex = -1; + break; + } + } + if (startIndex < 0 && line.PreviousLine != null) { + line = line.PreviousLine; + goto restart; + } + + if (startIndex >= 0) { + int endIndex = startIndex; + while (endIndex + 1 < lineText.Length && lineText [endIndex] != '>' && !char.IsWhiteSpace(lineText [endIndex])) { + endIndex++; + } + string tag = endIndex - startIndex - 1 > 0 ? lineText.Substring( + startIndex + 1, + endIndex - startIndex - 1 + ) : null; + if (!string.IsNullOrEmpty(tag) && commentTags.IndexOf(tag) >= 0) { + return tag; + } + } + return null; + } + + IEnumerable GetXmlDocumentationCompletionData() + { + var closingTag = GetLastClosingXmlCommentTag(); + if (closingTag != null) { + yield return factory.CreateLiteralCompletionData( + "/" + closingTag + ">" + ); + } + + yield return factory.CreateXmlDocCompletionData( + "c", + "Set text in a code-like font" + ); + yield return factory.CreateXmlDocCompletionData( + "code", + "Set one or more lines of source code or program output" + ); + yield return factory.CreateXmlDocCompletionData( + "example", + "Indicate an example" + ); + yield return factory.CreateXmlDocCompletionData( + "exception", + "Identifies the exceptions a method can throw", + "exception cref=\"|\"> +// +// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com) +// +// 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.Text; + +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.CSharp.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + /// + /// Acts as a common base between code completion and parameter completion. + /// + public class CSharpCompletionEngineBase + { + protected IDocument document; + protected int offset; + protected TextLocation location; + protected IUnresolvedTypeDefinition currentType; + protected IUnresolvedMember currentMember; + + #region Input properties + public CSharpTypeResolveContext ctx { get; private set; } + + public IProjectContent ProjectContent { get; private set; } + + ICompilation compilation; + + protected ICompilation Compilation { + get { + if (compilation == null) + compilation = ProjectContent.Resolve (ctx).Compilation; + return compilation; + } + } + + Version languageVersion = new Version (5, 0); + public Version LanguageVersion { + get { + return languageVersion; + } + set { + languageVersion = value; + } + } + #endregion + + protected CSharpCompletionEngineBase(IProjectContent content, ICompletionContextProvider completionContextProvider, CSharpTypeResolveContext ctx) + { + if (content == null) + throw new ArgumentNullException("content"); + if (ctx == null) + throw new ArgumentNullException("ctx"); + if (completionContextProvider == null) + throw new ArgumentNullException("completionContextProvider"); + + this.ProjectContent = content; + this.CompletionContextProvider = completionContextProvider; + this.ctx = ctx; + } + + + public ICompletionContextProvider CompletionContextProvider { + get; + private set; + } + + public void SetOffset (int offset) + { + Reset (); + + this.offset = offset; + this.location = document.GetLocation (offset); + CompletionContextProvider.GetCurrentMembers (offset, out currentType, out currentMember); + } + + public bool GetParameterCompletionCommandOffset (out int cpos) + { + // Start calculating the parameter offset from the beginning of the + // current member, instead of the beginning of the file. + cpos = offset - 1; + var mem = currentMember; + if (mem == null || (mem is IType) || IsInsideCommentStringOrDirective ()) { + return false; + } + int startPos = document.GetOffset (mem.Region.BeginLine, mem.Region.BeginColumn); + int parenDepth = 0; + int chevronDepth = 0; + Stack indexStack = new Stack (); + while (cpos > startPos) { + char c = document.GetCharAt (cpos); + if (c == ')') { + parenDepth++; + } + if (c == '>') { + chevronDepth++; + } + if (c == '}') { + if (indexStack.Count > 0) { + parenDepth = indexStack.Pop (); + } else { + parenDepth = 0; + } + chevronDepth = 0; + } + if (indexStack.Count == 0 && (parenDepth == 0 && c == '(' || chevronDepth == 0 && c == '<')) { + int p = GetCurrentParameterIndex (startPos, cpos + 1); + if (p != -1) { + cpos++; + return true; + } else { + return false; + } + } + if (c == '(') { + parenDepth--; + } + if (c == '<') { + chevronDepth--; + } + if (c == '{') { + indexStack.Push (parenDepth); + chevronDepth = 0; + } + cpos--; + } + return false; + } + + public int GetCurrentParameterIndex(int triggerOffset, int endOffset) + { + List list; + return GetCurrentParameterIndex (triggerOffset, endOffset, out list); + } + + public int GetCurrentParameterIndex (int triggerOffset, int endOffset, out List usedNamedParameters) + { + usedNamedParameters =new List (); + var parameter = new Stack (); + var bracketStack = new Stack> (); + bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; + var word = new StringBuilder (); + bool foundCharAfterOpenBracket = false; + for (int i = triggerOffset; i < endOffset; i++) { + char ch = document.GetCharAt (i); + char nextCh = i + 1 < document.TextLength ? document.GetCharAt (i + 1) : '\0'; + if (ch == ':') { + usedNamedParameters.Add (word.ToString ()); + word.Length = 0; + } else if (char.IsLetterOrDigit (ch) || ch =='_') { + word.Append (ch); + } else if (char.IsWhiteSpace (ch)) { + + } else { + word.Length = 0; + } + if (!char.IsWhiteSpace(ch) && parameter.Count > 0) + foundCharAfterOpenBracket = true; + + switch (ch) { + case '{': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + bracketStack.Push (parameter); + parameter = new Stack (); + break; + case '[': + case '(': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + parameter.Push (0); + break; + case '}': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (bracketStack.Count > 0) { + parameter = bracketStack.Pop (); + } else { + return -1; + } + break; + case ']': + case ')': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (parameter.Count > 0) { + parameter.Pop (); + } else { + return -1; + } + break; + case '<': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + parameter.Push (0); + break; + case '=': + if (nextCh == '>') { + i++; + continue; + } + break; + case '>': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (parameter.Count > 0) { + parameter.Pop (); + } + break; + case ',': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (parameter.Count > 0) { + parameter.Push (parameter.Pop () + 1); + } + break; + case '/': + if (inString || inChar || inVerbatimString) { + break; + } + if (nextCh == '/') { + i++; + inSingleComment = true; + } + if (nextCh == '*') { + inMultiLineComment = true; + } + break; + case '*': + if (inString || inChar || inVerbatimString || inSingleComment) { + break; + } + if (nextCh == '/') { + i++; + inMultiLineComment = false; + } + break; + case '@': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (nextCh == '"') { + i++; + inVerbatimString = true; + } + break; + case '\\': + if (inString || inChar) { + i++; + } + break; + case '"': + if (inSingleComment || inMultiLineComment || inChar) { + break; + } + if (inVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + inVerbatimString = false; + break; + } + inString = !inString; + break; + case '\'': + if (inSingleComment || inMultiLineComment || inString || inVerbatimString) { + break; + } + inChar = !inChar; + break; + default: + if (NewLine.IsNewLine(ch)) { + inSingleComment = false; + inString = false; + inChar = false; + } + break; + } + } + if (parameter.Count != 1 || bracketStack.Count > 0) { + return -1; + } + if (!foundCharAfterOpenBracket) + return 0; + return parameter.Pop() + 1; + } + + #region Context helper methods + public class MiniLexer + { + readonly string text; + + public bool IsFistNonWs = true; + public bool IsInSingleComment = false; + public bool IsInString = false; + public bool IsInVerbatimString = false; + public bool IsInChar = false; + public bool IsInMultiLineComment = false; + public bool IsInPreprocessorDirective = false; + + public MiniLexer(string text) + { + this.text = text; + } + + /// + /// Parsing all text and calling act delegate on almost every character. + /// Skipping begining of comments, begining of verbatim strings and escaped characters. + /// + /// Return true to abort parsing. Integer argument represent offset in text. + /// True if aborted. + public bool Parse(Func act = null) + { + return Parse(0, text.Length, act); + } + + + /// + /// Parsing text from start to start+length and calling act delegate on almost every character. + /// Skipping begining of comments, begining of verbatim strings and escaped characters. + /// + /// Start offset. + /// Lenght to parse. + /// Return true to abort parsing. Integer argument represent offset in text. + /// True if aborted. + public bool Parse(int start, int length, Func act = null) + { + for (int i = start; i < length; i++) { + char ch = text [i]; + char nextCh = i + 1 < text.Length ? text [i + 1] : '\0'; + switch (ch) { + case '#': + if (IsFistNonWs) + IsInPreprocessorDirective = true; + break; + case '/': + if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment || IsInMultiLineComment) + break; + if (nextCh == '/') { + i++; + IsInSingleComment = true; + IsInPreprocessorDirective = false; + } + if (nextCh == '*' && !IsInPreprocessorDirective) { + IsInMultiLineComment = true; + i++; + } + break; + case '*': + if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment) + break; + if (nextCh == '/') { + i++; + IsInMultiLineComment = false; + } + break; + case '@': + if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment || IsInMultiLineComment) + break; + if (nextCh == '"') { + i++; + IsInVerbatimString = true; + } + break; + case '\n': + case '\r': + IsInSingleComment = false; + IsInString = false; + IsInChar = false; + IsFistNonWs = true; + IsInPreprocessorDirective = false; + break; + case '\\': + if (IsInString || IsInChar) + i++; + break; + case '"': + if (IsInSingleComment || IsInMultiLineComment || IsInChar) + break; + if (IsInVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + IsInVerbatimString = false; + break; + } + IsInString = !IsInString; + break; + case '\'': + if (IsInSingleComment || IsInMultiLineComment || IsInString || IsInVerbatimString) + break; + IsInChar = !IsInChar; + break; + } + if (act != null) + if (act (ch, i)) + return true; + IsFistNonWs &= ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; + } + return false; + } + } + + + protected bool IsInsideCommentStringOrDirective(int offset) + { + var lexer = new MiniLexer(document.Text); + lexer.Parse(0, offset); + return + lexer.IsInSingleComment || + lexer.IsInString || + lexer.IsInVerbatimString || + lexer.IsInChar || + lexer.IsInMultiLineComment || + lexer.IsInPreprocessorDirective; + } + + + protected bool IsInsideCommentStringOrDirective() + { + var text = GetMemberTextToCaret(); + var lexer = new MiniLexer(text.Item1); + lexer.Parse(); + return + lexer.IsInSingleComment || + lexer.IsInString || + lexer.IsInVerbatimString || + lexer.IsInChar || + lexer.IsInMultiLineComment || + lexer.IsInPreprocessorDirective; + } + + protected bool IsInsideDocComment () + { + var text = GetMemberTextToCaret (); + bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; + bool singleLineIsDoc = false; + + for (int i = 0; i < text.Item1.Length - 1; i++) { + char ch = text.Item1 [i]; + char nextCh = text.Item1 [i + 1]; + + switch (ch) { + case '/': + if (inString || inChar || inVerbatimString) + break; + if (nextCh == '/') { + i++; + inSingleComment = true; + singleLineIsDoc = i + 1 < text.Item1.Length && text.Item1 [i + 1] == '/'; + if (singleLineIsDoc) { + i++; + } + } + if (nextCh == '*') + inMultiLineComment = true; + break; + case '*': + if (inString || inChar || inVerbatimString || inSingleComment) + break; + if (nextCh == '/') { + i++; + inMultiLineComment = false; + } + break; + case '@': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + if (nextCh == '"') { + i++; + inVerbatimString = true; + } + break; + case '\n': + case '\r': + inSingleComment = false; + inString = false; + inChar = false; + break; + case '\\': + if (inString || inChar) + i++; + break; + case '"': + if (inSingleComment || inMultiLineComment || inChar) + break; + if (inVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + inVerbatimString = false; + break; + } + inString = !inString; + break; + case '\'': + if (inSingleComment || inMultiLineComment || inString || inVerbatimString) + break; + inChar = !inChar; + break; + } + } + + return inSingleComment && singleLineIsDoc; + } + + protected CSharpResolver GetState () + { + return new CSharpResolver (ctx); + /*var state = new CSharpResolver (ctx); + + state.CurrentMember = currentMember; + state.CurrentTypeDefinition = currentType; + state.CurrentUsingScope = CSharpUnresolvedFile.GetUsingScope (location); + if (state.CurrentMember != null) { + var node = Unit.GetNodeAt (location); + if (node == null) + return state; + var navigator = new NodeListResolveVisitorNavigator (new[] { node }); + var visitor = new ResolveVisitor (state, CSharpUnresolvedFile, navigator); + Unit.AcceptVisitor (visitor, null); + try { + var newState = visitor.GetResolverStateBefore (node); + if (newState != null) + state = newState; + } catch (Exception) { + } + } + + return state;*/ + } + #endregion + + #region Basic parsing/resolving functions + static Stack> GetBracketStack (string memberText) + { + var bracketStack = new Stack> (); + + bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; + + for (int i = 0; i < memberText.Length; i++) { + char ch = memberText [i]; + char nextCh = i + 1 < memberText.Length ? memberText [i + 1] : '\0'; + switch (ch) { + case '(': + case '[': + case '{': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + bracketStack.Push (Tuple.Create (ch, i)); + break; + case ')': + case ']': + case '}': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + if (bracketStack.Count > 0) + bracketStack.Pop (); + break; + case '/': + if (inString || inChar || inVerbatimString) + break; + if (nextCh == '/') { + i++; + inSingleComment = true; + } + if (nextCh == '*') + inMultiLineComment = true; + break; + case '*': + if (inString || inChar || inVerbatimString || inSingleComment) + break; + if (nextCh == '/') { + i++; + inMultiLineComment = false; + } + break; + case '@': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + if (nextCh == '"') { + i++; + inVerbatimString = true; + } + break; + case '\\': + if (inString || inChar) + i++; + break; + case '"': + if (inSingleComment || inMultiLineComment || inChar) + break; + if (inVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + inVerbatimString = false; + break; + } + inString = !inString; + break; + case '\'': + if (inSingleComment || inMultiLineComment || inString || inVerbatimString) + break; + inChar = !inChar; + break; + default : + if (NewLine.IsNewLine(ch)) { + inSingleComment = false; + inString = false; + inChar = false; + } + break; + } + } + return bracketStack; + } + + public static void AppendMissingClosingBrackets (StringBuilder wrapper, bool appendSemicolon) + { + var memberText = wrapper.ToString(); + var bracketStack = GetBracketStack(memberText); + bool didAppendSemicolon = !appendSemicolon; + //char lastBracket = '\0'; + while (bracketStack.Count > 0) { + var t = bracketStack.Pop (); + switch (t.Item1) { + case '(': + wrapper.Append (')'); + if (appendSemicolon) + didAppendSemicolon = false; + //lastBracket = ')'; + break; + case '[': + wrapper.Append (']'); + if (appendSemicolon) + didAppendSemicolon = false; + //lastBracket = ']'; + break; + case '<': + wrapper.Append ('>'); + if (appendSemicolon) + didAppendSemicolon = false; + //lastBracket = '>'; + break; + case '{': + int o = t.Item2 - 1; + if (!didAppendSemicolon) { + didAppendSemicolon = true; + wrapper.Append (';'); + } + + bool didAppendCatch = false; + while (o >= "try".Length) { + char ch = memberText [o]; + if (!char.IsWhiteSpace (ch)) { + if (ch == 'y' && memberText [o - 1] == 'r' && memberText [o - 2] == 't' && (o - 3 < 0 || !char.IsLetterOrDigit(memberText [o - 3]))) { + wrapper.Append ("} catch {}"); + didAppendCatch = true; + } + break; + } + o--; + } + if (!didAppendCatch) + wrapper.Append ('}'); + break; + } + } + if (!didAppendSemicolon) + wrapper.Append (';'); + } + + protected StringBuilder CreateWrapper(string continuation, bool appendSemicolon, string afterContinuation, string memberText, TextLocation memberLocation, ref int closingBrackets, ref int generatedLines) + { + var wrapper = new StringBuilder(); + bool wrapInClass = memberLocation != new TextLocation(1, 1); + if (wrapInClass) { + wrapper.Append("class Stub {"); + wrapper.AppendLine(); + closingBrackets++; + generatedLines++; + } + wrapper.Append(memberText); + wrapper.Append(continuation); + AppendMissingClosingBrackets(wrapper, appendSemicolon); + wrapper.Append(afterContinuation); + if (closingBrackets > 0) { + wrapper.Append(new string('}', closingBrackets)); + } + return wrapper; + } + + protected SyntaxTree ParseStub(string continuation, bool appendSemicolon = true, string afterContinuation = null) + { + var mt = GetMemberTextToCaret(); + if (mt == null) { + return null; + } + + string memberText = mt.Item1; + var memberLocation = mt.Item2; + int closingBrackets = 1; + int generatedLines = 0; + var wrapper = CreateWrapper(continuation, appendSemicolon, afterContinuation, memberText, memberLocation, ref closingBrackets, ref generatedLines); + var parser = new CSharpParser (); + foreach (var sym in CompletionContextProvider.ConditionalSymbols) + parser.CompilerSettings.ConditionalSymbols.Add (sym); + parser.InitialLocation = new TextLocation(memberLocation.Line - generatedLines, 1); + var result = parser.Parse(wrapper.ToString ()); + return result; + } + + protected virtual void Reset () + { + memberText = null; + } + + Tuple memberText; + protected Tuple GetMemberTextToCaret() + { + if (memberText == null) + memberText = CompletionContextProvider.GetMemberTextToCaret(offset, currentType, currentMember); + return memberText; + } + + protected ExpressionResult GetInvocationBeforeCursor(bool afterBracket) + { + SyntaxTree baseUnit; + baseUnit = ParseStub("a", false); + + var section = baseUnit.GetNodeAt(location.Line, location.Column - 2); + var attr = section != null ? section.Attributes.LastOrDefault() : null; + if (attr != null) { + return new ExpressionResult((AstNode)attr, baseUnit); + } + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + var mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); + AstNode expr = null; + if (mref is InvocationExpression) { + expr = ((InvocationExpression)mref).Target; + } else if (mref is ObjectCreateExpression) { + expr = mref; + } else { + baseUnit = ParseStub(")};", false); + mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); + if (mref is InvocationExpression) { + expr = ((InvocationExpression)mref).Target; + } else if (mref is ObjectCreateExpression) { + expr = mref; + } + } + + if (expr == null) { + // work around for missing ';' bug in mcs: + baseUnit = ParseStub("a", true); + + section = baseUnit.GetNodeAt(location.Line, location.Column - 2); + attr = section != null ? section.Attributes.LastOrDefault() : null; + if (attr != null) { + return new ExpressionResult((AstNode)attr, baseUnit); + } + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); + expr = null; + if (mref is InvocationExpression) { + expr = ((InvocationExpression)mref).Target; + } else if (mref is ObjectCreateExpression) { + expr = mref; + } + } + + if (expr == null) { + return null; + } + return new ExpressionResult ((AstNode)expr, baseUnit); + } + + public class ExpressionResult + { + public AstNode Node { get; private set; } + public SyntaxTree Unit { get; private set; } + + + public ExpressionResult (AstNode item2, SyntaxTree item3) + { + this.Node = item2; + this.Unit = item3; + } + + public override string ToString () + { + return string.Format ("[ExpressionResult: Node={0}, Unit={1}]", Node, Unit); + } + } + + protected ExpressionResolveResult ResolveExpression (ExpressionResult tuple) + { + return ResolveExpression (tuple.Node); + } + + protected class ExpressionResolveResult + { + public ResolveResult Result { get; set; } + public CSharpResolver Resolver { get; set; } + public CSharpAstResolver AstResolver { get; set; } + + public ExpressionResolveResult(ResolveResult item1, CSharpResolver item2, CSharpAstResolver item3) + { + this.Result = item1; + this.Resolver = item2; + this.AstResolver = item3; + } + } + + protected ExpressionResolveResult ResolveExpression(AstNode expr) + { + if (expr == null) { + return null; + } + AstNode resolveNode; + if (expr is Expression || expr is AstType) { + resolveNode = expr; + } else if (expr is VariableDeclarationStatement) { + resolveNode = ((VariableDeclarationStatement)expr).Type; + } else { + resolveNode = expr; + } + try { + var root = expr.AncestorsAndSelf.FirstOrDefault(n => n is EntityDeclaration || n is SyntaxTree); + if (root == null) { + return null; + } + var curState = GetState(); + // current member needs to be in the setter because of the 'value' parameter + if (root is Accessor) { + var prop = curState.CurrentMember as IProperty; + if (prop != null && prop.CanSet && (root.Role == IndexerDeclaration.SetterRole || root.Role == PropertyDeclaration.SetterRole)) + curState = curState.WithCurrentMember(prop.Setter); + } + + // Rood should be the 'body' - otherwise the state -> current member isn't correct. + var body = root.Children.FirstOrDefault(r => r.Role == Roles.Body); + if (body != null && body.Contains(expr.StartLocation)) + root = body; + + var csResolver = CompletionContextProvider.GetResolver (curState, root); + var result = csResolver.Resolve(resolveNode); + var state = csResolver.GetResolverStateBefore(resolveNode); + if (state.CurrentMember == null) + state = state.WithCurrentMember(curState.CurrentMember); + if (state.CurrentTypeDefinition == null) + state = state.WithCurrentTypeDefinition(curState.CurrentTypeDefinition); + if (state.CurrentUsingScope == null) + state = state.WithCurrentUsingScope(curState.CurrentUsingScope); + return new ExpressionResolveResult(result, state, csResolver); + } catch (Exception e) { + Console.WriteLine(e); + return null; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs new file mode 100644 index 000000000..e5919834e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs @@ -0,0 +1,408 @@ +// +// CSharpParameterCompletionEngine.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.Completion; +using System.Collections.Generic; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public class CSharpParameterCompletionEngine : CSharpCompletionEngineBase + { + internal IParameterCompletionDataFactory factory; + + public CSharpParameterCompletionEngine(IDocument document, ICompletionContextProvider completionContextProvider, IParameterCompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx) : base (content, completionContextProvider, ctx) + { + if (document == null) { + throw new ArgumentNullException("document"); + } + if (factory == null) { + throw new ArgumentNullException("factory"); + } + this.document = document; + this.factory = factory; + } + + public ExpressionResult GetIndexerBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null && currentType == null) { + return null; + } + baseUnit = ParseStub("x]"); + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + var mref = baseUnit.GetNodeAt(location, n => n is IndexerExpression); + AstNode expr; + if (mref is IndexerExpression) { + expr = ((IndexerExpression)mref).Target; + } else { + return null; + } + + return new ExpressionResult((AstNode)expr, baseUnit); + } + + public ExpressionResult GetConstructorInitializerBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null && currentType == null) { + return null; + } + baseUnit = ParseStub("a) {}", false); + + var expr = baseUnit.GetNodeAt (location); + if (expr == null) { + return null; + } + return new ExpressionResult((AstNode)expr, baseUnit); + } + + public ExpressionResult GetTypeBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null && currentType == null) { + return null; + } + baseUnit = ParseStub("x> a"); + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + var expr = baseUnit.GetNodeAt(location.Line, location.Column + 1); + if (expr == null) + return null; + // '>' position + return new ExpressionResult((AstNode)expr, baseUnit); + } + + public ExpressionResult GetMethodTypeArgumentInvocationBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null && currentType == null) { + return null; + } + baseUnit = ParseStub("x>.A ()"); + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + var expr = baseUnit.GetNodeAt(location.Line, location.Column + 1); + if (expr == null) + return null; + return new ExpressionResult((AstNode)expr, baseUnit); + } + + + + IEnumerable CollectMethods(AstNode resolvedNode, MethodGroupResolveResult resolveResult) + { + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); + bool onlyStatic = false; + if (resolvedNode is IdentifierExpression && currentMember != null && currentMember.IsStatic || resolveResult.TargetResult is TypeResolveResult) { + onlyStatic = true; + } + var methods = new List(); + foreach (var method in resolveResult.Methods) { + if (method.IsConstructor) { + continue; + } + if (!lookup.IsAccessible (method, true)) + continue; + if (onlyStatic && !method.IsStatic) { + continue; + } + if (method.IsShadowing) { + for (int j = 0; j < methods.Count; j++) { + if (ParameterListComparer.Instance.Equals(methods[j].Parameters, method.Parameters)) { + methods.RemoveAt (j); + j--; + } + } + } + methods.Add (method); + } + foreach (var m in methods) { + yield return m; + } + foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) { + foreach (var method in extMethods) { + if (methods.Contains (method)) + continue; + yield return new ReducedExtensionMethod (method); + } + } + } + + IEnumerable GetAccessibleIndexers(IType type) + { + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); + var properties = new List(); + foreach (var property in type.GetProperties ()) { + if (!property.IsIndexer) + continue; + if (!lookup.IsAccessible (property, true)) + continue; + if (property.IsShadowing) { + for (int j = 0; j < properties.Count; j++) { + if (ParameterListComparer.Instance.Equals(properties[j].Parameters, property.Parameters)) { + properties.RemoveAt (j); + j--; + } + } + } + + properties.Add (property); + } + return properties; + } + + public IParameterDataProvider GetParameterDataProvider(int offset, char completionChar) + { + //Ignoring completionChar == '\0' because it usually means moving with arrow keys, tab or enter + //we don't want to trigger on those events but it probably should be handled somewhere else + //since our job is to resolve method and not to decide when to display tooltip or not + if (offset <= 0 || completionChar == '\0') { + return null; + } + SetOffset (offset); + int startOffset; + string text; + if (currentMember == null && currentType == null) { + //In case of attributes parse all file + startOffset = 0; + text = document.Text; + } else { + var memberText = GetMemberTextToCaret (); + text = memberText.Item1; + startOffset = document.GetOffset (memberText.Item2); + } + + var parenStack = new Stack (); + var chevronStack = new Stack (); + var squareStack = new Stack (); + var bracketStack = new Stack (); + + var lex = new MiniLexer (text); + bool failed = lex.Parse ((ch, off) => { + if (lex.IsInString || lex.IsInChar || lex.IsInVerbatimString || lex.IsInSingleComment || lex.IsInMultiLineComment || lex.IsInPreprocessorDirective) + return false; + switch (ch) { + case '(': + parenStack.Push (startOffset + off); + break; + case ')': + if (parenStack.Count == 0) { + return true; + } + parenStack.Pop (); + break; + case '<': + chevronStack.Push (startOffset + off); + break; + case '>': + //Don't abort if we don't have macthing '<' for '>' it could be if (i > 0) Foo($ + if (chevronStack.Count == 0) { + return false; + } + chevronStack.Pop (); + break; + case '[': + squareStack.Push (startOffset + off); + break; + case ']': + if (squareStack.Count == 0) { + return true; + } + squareStack.Pop (); + break; + case '{': + bracketStack.Push (startOffset + off); + break; + case '}': + if (bracketStack.Count == 0) { + return true; + } + bracketStack.Pop (); + break; + } + return false; + }); + if (failed) + return null; + int result = -1; + if (parenStack.Count > 0) + result = parenStack.Pop (); + if (squareStack.Count > 0) + result = Math.Max (result, squareStack.Pop ()); + if (chevronStack.Count > 0) + result = Math.Max (result, chevronStack.Pop ()); + + //If we are inside { bracket we don't want to display anything + if (bracketStack.Count > 0 && bracketStack.Pop () > result) + return null; + if (result == -1) + return null; + SetOffset (result + 1); + ResolveResult resolveResult; + switch (document.GetCharAt (result)) { + case '(': + var invoke = GetInvocationBeforeCursor(true) ?? GetConstructorInitializerBeforeCursor(); + if (invoke == null) { + return null; + } + 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, init); + } else { + var baseType = ctx.CurrentTypeDefinition.DirectBaseTypes.FirstOrDefault(bt => bt.Kind != TypeKind.Interface); + if (baseType == null) { + return null; + } + return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), baseType); + } + } + if (invoke.Node is ObjectCreateExpression) { + var createType = ResolveExpression(((ObjectCreateExpression)invoke.Node).Type); + if (createType.Result.Type.Kind == TypeKind.Unknown) + return null; + return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), createType.Result.Type); + } + + if (invoke.Node is ICSharpCode.NRefactory.CSharp.Attribute) { + var attribute = ResolveExpression(invoke); + if (attribute == null || attribute.Result == null) { + return null; + } + return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), attribute.Result.Type); + } + var invocationExpression = ResolveExpression(invoke); + if (invocationExpression == null || invocationExpression.Result == null || invocationExpression.Result.IsError) { + return null; + } + resolveResult = invocationExpression.Result; + if (resolveResult is MethodGroupResolveResult) { + return factory.CreateMethodDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectMethods(invoke.Node, resolveResult as MethodGroupResolveResult)); + } + if (resolveResult is MemberResolveResult) { + var mr = resolveResult as MemberResolveResult; + if (mr.Member is IMethod) { + 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); + // } + break; + case '<': + invoke = GetMethodTypeArgumentInvocationBeforeCursor(); + if (invoke != null) { + var tExpr2 = ResolveExpression(invoke); + if (tExpr2 != null && tExpr2.Result is MethodGroupResolveResult && !tExpr2.Result.IsError) { + return factory.CreateTypeParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectMethods(invoke.Node, tExpr2.Result as MethodGroupResolveResult)); + } + } + invoke = GetTypeBeforeCursor(); + if (invoke == null || invoke.Node.StartLocation.IsEmpty) { + return null; + } + var tExpr = ResolveExpression(invoke); + if (tExpr == null || tExpr.Result == null || tExpr.Result.IsError) { + return null; + } + + return factory.CreateTypeParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectAllTypes(tExpr.Result.Type)); + case '[': + invoke = GetIndexerBeforeCursor(); + if (invoke == null) { + return null; + } + if (invoke.Node is ArrayCreateExpression) { + return null; + } + var indexerExpression = ResolveExpression(invoke); + if (indexerExpression == null || indexerExpression.Result == null || indexerExpression.Result.IsError) { + return null; + } + return factory.CreateIndexerParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), indexerExpression.Result.Type, GetAccessibleIndexers (indexerExpression.Result.Type), invoke.Node); + } + return null; + } + + IEnumerable CollectAllTypes(IType baseType) + { + var state = GetState(); + for (var n = state.CurrentUsingScope; n != null; n = n.Parent) { + foreach (var u in n.Usings) { + foreach (var type in u.Types) { + if (type.TypeParameterCount > 0 && type.Name == baseType.Name) { + yield return type; + } + } + } + + foreach (var type in n.Namespace.Types) { + if (type.TypeParameterCount > 0 && type.Name == baseType.Name) { + yield return type; + } + } + } + } + + List GetUsedNamespaces() + { + var scope = ctx.CurrentUsingScope; + var result = new List(); + while (scope != null) { + result.Add(scope.Namespace.FullName); + + foreach (var ns in scope.Usings) { + result.Add(ns.FullName); + } + scope = scope.Parent; + } + return result; + } + + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs new file mode 100644 index 000000000..4bf10d937 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs @@ -0,0 +1,328 @@ +// +// CompletionDataWrapper.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.Completion; +using ICSharpCode.NRefactory.TypeSystem; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public class CompletionDataWrapper + { + CSharpCompletionEngine completion; + List result = new List (); + + public List Result { + get { + return result; + } + } + + ICompletionDataFactory Factory { + get { + return completion.factory; + } + } + + internal bool AnonymousDelegateAdded { + get; + set; + } + + public CompletionDataWrapper (CSharpCompletionEngine completion) + { + this.completion = completion; + } + + public void Add (ICompletionData data) + { + result.Add (data); + } + + + public ICompletionData AddCustom (string displayText, string description = null, string completionText = null) + { + var literalCompletionData = Factory.CreateLiteralCompletionData(displayText, description, completionText); + result.Add(literalCompletionData); + return literalCompletionData; + } + + HashSet usedNamespaces = new HashSet (); + + bool IsAccessible(MemberLookup lookup, INamespace ns) + { + if (ns.Types.Any (t => lookup.IsAccessible (t, false))) + return true; + foreach (var child in ns.ChildNamespaces) + if (IsAccessible (lookup, child)) + return true; + return false; + } + + public void AddNamespace (MemberLookup lookup, INamespace ns) + { + if (usedNamespaces.Contains (ns.Name)) + return; + if (!IsAccessible (lookup, ns)) { + usedNamespaces.Add (ns.Name); + return; + } + usedNamespaces.Add (ns.Name); + result.Add (Factory.CreateNamespaceCompletionData (ns)); + } + + public void AddAlias(string alias) + { + result.Add (Factory.CreateLiteralCompletionData (alias)); + } + + Dictionary typeDisplayText = new Dictionary (); + Dictionary addedTypes = new Dictionary (); + + public ICompletionData AddConstructors(IType type, bool showFullName, bool isInAttributeContext = false) + { + return InternalAddType(type, showFullName, isInAttributeContext, true); + } + + public ICompletionData AddType(IType type, bool showFullName, bool isInAttributeContext = false) + { + return InternalAddType(type, showFullName, isInAttributeContext, false); + } + + ICompletionData InternalAddType(IType type, bool showFullName, bool isInAttributeContext, bool addConstrurs) + { + if (type == null) + throw new ArgumentNullException("type"); + if (type.Name == "Void" && type.Namespace == "System" || type.Kind == TypeKind.Unknown) + return null; + if (addedTypes.ContainsKey (type)) + return addedTypes[type]; + usedNamespaces.Add(type.Name); + var def = type.GetDefinition(); + if (def != null && def.ParentAssembly != completion.ctx.CurrentAssembly) { + switch (completion.EditorBrowsableBehavior) { + case EditorBrowsableBehavior.Ignore: + break; + case EditorBrowsableBehavior.Normal: + var state = def.GetEditorBrowsableState(); + if (state != System.ComponentModel.EditorBrowsableState.Always) + return null; + break; + case EditorBrowsableBehavior.IncludeAdvanced: + if (!def.IsBrowsable()) + return null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + ICompletionData usedType; + var data = Factory.CreateTypeCompletionData(type, showFullName, isInAttributeContext, addConstrurs); + var text = data.DisplayText; + if (typeDisplayText.TryGetValue(text, out usedType)) { + usedType.AddOverload(data); + return usedType; + } + typeDisplayText [text] = data; + result.Add(data); + addedTypes[type] = data; + return data; + } + + Dictionary> data = new Dictionary> (); + + public ICompletionData AddVariable(IVariable variable) + { + if (data.ContainsKey(variable.Name)) + return null; + data [variable.Name] = new List(); + var cd = Factory.CreateVariableCompletionData(variable); + result.Add(cd); + return cd; + } + + public ICompletionData AddNamedParameterVariable(IVariable variable) + { + var name = variable.Name + ":"; + if (data.ContainsKey(name)) + return null; + data [name] = new List(); + + var cd = Factory.CreateVariableCompletionData(variable); + cd.CompletionText += ":"; + cd.DisplayText += ":"; + result.Add(cd); + return cd; + } + + public void AddTypeParameter (ITypeParameter variable) + { + if (data.ContainsKey (variable.Name)) + return; + data [variable.Name] = new List (); + result.Add (Factory.CreateVariableCompletionData (variable)); + } + + public void AddTypeImport(ITypeDefinition type, bool useFullName, bool addForTypeCreation) + { + result.Add(Factory.CreateImportCompletionData(type, useFullName, addForTypeCreation)); + } + + public ICompletionData AddMember (IMember member) + { + var newData = Factory.CreateEntityCompletionData (member); + + if (member.ParentAssembly != completion.ctx.CurrentAssembly) { + switch (completion.EditorBrowsableBehavior) { + case EditorBrowsableBehavior.Ignore: + break; + case EditorBrowsableBehavior.Normal: + var state = member.GetEditorBrowsableState(); + if (state != System.ComponentModel.EditorBrowsableState.Always) + return null; + break; + case EditorBrowsableBehavior.IncludeAdvanced: + if (!member.IsBrowsable()) + return null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + string memberKey = newData.DisplayText; + if (memberKey == null) + return null; + + newData.CompletionCategory = GetCompletionCategory (member.DeclaringTypeDefinition); + + List existingData; + data.TryGetValue (memberKey, out existingData); + if (existingData != null) { + if (member.SymbolKind == SymbolKind.Field || member.SymbolKind == SymbolKind.Property || member.SymbolKind == SymbolKind.Event) + return null; + var a = member as IEntity; + foreach (var d in existingData) { + if (!(d is IEntityCompletionData)) + continue; + var b = ((IEntityCompletionData)d).Entity; + if (a == null || b == null || a.SymbolKind == b.SymbolKind) { + d.AddOverload (newData); + return d; + } + } + if (newData != null) { + result.Add (newData); + data [memberKey].Add (newData); + } + } else { + result.Add (newData); + data [memberKey] = new List (); + data [memberKey].Add (newData); + } + return newData; + } + + internal CompletionCategory GetCompletionCategory (IType type) + { + if (type == null) + return null; + if (!completionCategories.ContainsKey (type)) + completionCategories [type] = new TypeCompletionCategory (type); + return completionCategories [type]; + } + + Dictionary completionCategories = new Dictionary (); + class TypeCompletionCategory : CompletionCategory + { + public IType Type { + get; + private set; + } + + public TypeCompletionCategory (IType type) : base (type.FullName, null) + { + this.Type = type; + } + + public override int CompareTo (CompletionCategory other) + { + var compareCategory = other as TypeCompletionCategory; + if (compareCategory == null) + return -1; + int result; + if (Type.ReflectionName == compareCategory.Type.ReflectionName) { + result = 0; + } else if (Type.GetAllBaseTypes().Any(t => t.ReflectionName == compareCategory.Type.ReflectionName)) { + result = -1; + } else if (compareCategory.Type.GetAllBaseTypes().Any(t => t.ReflectionName == Type.ReflectionName)) { + result = 1; + } else { + var d = Type.GetDefinition (); + var ct = compareCategory.Type.GetDefinition(); + if (ct.IsStatic && d.IsStatic) { + result = d.FullName.CompareTo (ct.FullName); + } else if (d.IsStatic) { + result = 1; + }else if (ct.IsStatic) { + result = -1; + } else { + result = 0; + } + } + return result; + } + } + HashSet addedEnums = new HashSet (); + public ICompletionData AddEnumMembers (IType resolvedType, CSharpResolver state) + { + if (addedEnums.Contains (resolvedType)) + return null; + addedEnums.Add (resolvedType); + var result = AddType(resolvedType, true); + foreach (var field in resolvedType.GetFields ()) { + if (field.IsPublic && (field.IsConst || field.IsStatic)) { + Result.Add(Factory.CreateMemberCompletionData(resolvedType, field)); + } + } + return result; + } + HashSet anonymousSignatures = new HashSet (); + + public bool HasAnonymousDelegateAdded(string signature) + { + return anonymousSignatures.Contains(signature); + } + + public void AddAnonymousDelegateAdded(string signature) + { + anonymousSignatures.Add(signature); + } + } +} + + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs new file mode 100644 index 000000000..b6e9822ee --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs @@ -0,0 +1,214 @@ +// +// IMemberProvider.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. +// +// 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 ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public interface ICompletionContextProvider + { + IList ConditionalSymbols { + get; + } + + void GetCurrentMembers (int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember); + + Tuple GetMemberTextToCaret(int caretOffset, IUnresolvedTypeDefinition currentType, IUnresolvedMember currentMember); + + CSharpAstResolver GetResolver (CSharpResolver resolver, AstNode rootNode); + } + + public class DefaultCompletionContextProvider : ICompletionContextProvider + { + readonly IDocument document; + readonly CSharpUnresolvedFile unresolvedFile; + readonly List symbols = new List (); + + public IList ConditionalSymbols { + get { + return symbols; + } + } + + public DefaultCompletionContextProvider (IDocument document, CSharpUnresolvedFile unresolvedFile) + { + if (document == null) + throw new ArgumentNullException("document"); + if (unresolvedFile == null) + throw new ArgumentNullException("unresolvedFile"); + this.document = document; + this.unresolvedFile = unresolvedFile; + } + + public void AddSymbol (string sym) + { + symbols.Add (sym); + } + public void GetCurrentMembers(int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember) + { + //var document = engine.document; + var location = document.GetLocation(offset); + + currentType = null; + + foreach (var type in unresolvedFile.TopLevelTypeDefinitions) { + if (type.Region.Begin < location) + currentType = type; + } + currentType = FindInnerType (currentType, location); + + // location is beyond last reported end region, now we need to check, if the end region changed + if (currentType != null && currentType.Region.End < location) { + if (!IsInsideType (currentType, location)) + currentType = null; + } + currentMember = null; + if (currentType != null) { + foreach (var member in currentType.Members) { + if (member.Region.Begin < location && (currentMember == null || currentMember.Region.Begin < member.Region.Begin)) + currentMember = member; + } + } + + // location is beyond last reported end region, now we need to check, if the end region changed + // NOTE: Enums are a special case, there the "last" field needs to be treated as current member + if (currentMember != null && currentMember.Region.End < location && currentType.Kind != TypeKind.Enum) { + if (!IsInsideType (currentMember, location)) + currentMember = null; + }/* + var stack = GetBracketStack (engine.GetMemberTextToCaret ().Item1); + if (stack.Count == 0) + currentMember = null;*/ + } + + IUnresolvedTypeDefinition FindInnerType (IUnresolvedTypeDefinition parent, TextLocation location) + { + if (parent == null) + return null; + var currentType = parent; + foreach (var type in parent.NestedTypes) { + if (type.Region.Begin < location && location < type.Region.End) + currentType = FindInnerType (type, location); + } + + return currentType; + } + + bool IsInsideType (IUnresolvedEntity currentType, TextLocation location) + { + if (currentType.Region.IsEmpty) + return false; + int startOffset = document.GetOffset (currentType.Region.Begin); + int endOffset = document.GetOffset (location); + //bool foundEndBracket = false; + + var bracketStack = new Stack (); + + bool isInString = false, isInChar = false; + bool isInLineComment = false, isInBlockComment = false; + + for (int i = startOffset; i < endOffset; i++) { + char ch = document.GetCharAt (i); + switch (ch) { + case '(': + case '[': + case '{': + if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) + bracketStack.Push (ch); + break; + case ')': + case ']': + case '}': + if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) + if (bracketStack.Count > 0) + bracketStack.Pop (); + break; + case '/': + if (isInBlockComment) { + if (i > 0 && document.GetCharAt (i - 1) == '*') + isInBlockComment = false; + } else if (!isInString && !isInChar && i + 1 < document.TextLength) { + char nextChar = document.GetCharAt (i + 1); + if (nextChar == '/') + isInLineComment = true; + if (!isInLineComment && nextChar == '*') + isInBlockComment = true; + } + break; + case '"': + if (!(isInChar || isInLineComment || isInBlockComment)) + isInString = !isInString; + break; + case '\'': + if (!(isInString || isInLineComment || isInBlockComment)) + isInChar = !isInChar; + break; + default : + if (NewLine.IsNewLine(ch)) { + isInLineComment = false; + } + break; + } + } + return bracketStack.Any (t => t == '{'); + } + + public Tuple GetMemberTextToCaret(int caretOffset, IUnresolvedTypeDefinition currentType, IUnresolvedMember currentMember) + { + int startOffset; + if (currentMember != null && currentType != null && currentType.Kind != TypeKind.Enum) { + startOffset = document.GetOffset(currentMember.Region.Begin); + } else if (currentType != null) { + startOffset = document.GetOffset(currentType.Region.Begin); + } else { + startOffset = 0; + } + while (startOffset > 0) { + char ch = document.GetCharAt(startOffset - 1); + if (ch != ' ' && ch != '\t') { + break; + } + --startOffset; + } + + return Tuple.Create (document.GetText (startOffset, caretOffset - startOffset), document.GetLocation (startOffset)); + } + + + public CSharpAstResolver GetResolver (CSharpResolver resolver, AstNode rootNode) + { + return new CSharpAstResolver (resolver, rootNode, unresolvedFile); + } + + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionDataFactory.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionDataFactory.cs new file mode 100644 index 000000000..da61f8907 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionDataFactory.cs @@ -0,0 +1,89 @@ +// +// ICompletionDataFactory.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Completion; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public interface ICompletionDataFactory + { + ICompletionData CreateEntityCompletionData (IEntity entity); + ICompletionData CreateEntityCompletionData (IEntity entity, string text); + + ICompletionData CreateTypeCompletionData (IType type, bool showFullName, bool isInAttributeContext, bool addForTypeCreation); + + /// + /// Creates the member completion data. + /// Form: Type.Member + /// Used for generating enum members Foo.A, Foo.B where the enum 'Foo' is valid. + /// + ICompletionData CreateMemberCompletionData(IType type, IEntity member); + + /// + /// Creates a generic completion data. + /// + /// + /// The title of the completion data + /// + /// + /// The description of the literal. + /// + /// + /// The insert text. If null, title is taken. + /// + ICompletionData CreateLiteralCompletionData (string title, string description = null, string insertText = null); + + ICompletionData CreateNamespaceCompletionData (INamespace name); + + ICompletionData CreateVariableCompletionData (IVariable variable); + + ICompletionData CreateVariableCompletionData (ITypeParameter parameter); + + ICompletionData CreateEventCreationCompletionData (string delegateMethodName, IType delegateType, IEvent evt, string parameterDefinition, IUnresolvedMember currentMember, IUnresolvedTypeDefinition currentType); + + ICompletionData CreateNewOverrideCompletionData (int declarationBegin, IUnresolvedTypeDefinition type, IMember m); + ICompletionData CreateNewPartialCompletionData (int declarationBegin, IUnresolvedTypeDefinition type, IUnresolvedMember m); + + IEnumerable CreateCodeTemplateCompletionData (); + + IEnumerable CreatePreProcessorDefinesCompletionData (); + + /// + /// Creates a completion data that adds the required using for the created type. + /// + /// The type to import + /// If set to true the full name of the type needs to be used. + /// If true the completion data is used in 'new' context. + ICompletionData CreateImportCompletionData(IType type, bool useFullName, bool addForTypeCreation); + + ICompletionData CreateFormatItemCompletionData(string format, string description, object example); + + ICompletionData CreateXmlDocCompletionData (string tag, string description = null, string tagInsertionText = null); + + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs new file mode 100644 index 000000000..5bbb64e40 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs @@ -0,0 +1,54 @@ +// +// IParameterCopmletionFactory.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Completion; +using ICSharpCode.NRefactory.CSharp.Resolver; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public interface IParameterCompletionDataFactory + { + 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); + + IParameterDataProvider CreateIndexerParameterDataProvider (int startOffset, IType type, IEnumerable accessibleIndexers, AstNode resolvedNode); + + IParameterDataProvider CreateTypeParameterDataProvider (int startOffset, IEnumerable types); + + IParameterDataProvider CreateTypeParameterDataProvider (int startOffset, IEnumerable methods); + } + +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormatter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormatter.cs new file mode 100644 index 000000000..a6969fea7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormatter.cs @@ -0,0 +1,159 @@ +// +// CSharpFormatter.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using System.Threading; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum FormattingMode { + OnTheFly, + Intrusive + } + + /// + /// The C# Formatter generates a set of text replace actions to format a region in a C# document. + /// + public class CSharpFormatter + { + readonly CSharpFormattingOptions policy; + readonly TextEditorOptions options; + + /// + /// Gets the formatting policy the formatter uses. + /// + public CSharpFormattingOptions Policy { + get { + return policy; + } + } + + /// + /// Gets the text editor options the formatter uses. + /// Note: If none was specified TextEditorOptions.Default gets used. + /// + public TextEditorOptions TextEditorOptions { + get { + return options; + } + } + + List formattingRegions = new List (); + internal TextLocation lastFormattingLocation = new TextLocation(int.MaxValue, int.MaxValue); + + /// + /// Gets the formatting regions. NOTE: Will get changed to IReadOnlyList. + /// + public IList FormattingRegions { + get { + return formattingRegions; + } + } + + /// + /// Gets or sets the formatting mode. For on the fly formatting a lightweight formatting mode + /// gives better results. + /// + public FormattingMode FormattingMode { + get; + set; + } + + /// + /// Initializes a new instance of the class. + /// + /// The formatting policy to use. + /// The text editor options (optional). Default is: TextEditorOptions.Default + public CSharpFormatter(CSharpFormattingOptions policy, TextEditorOptions options = null) + { + if (policy == null) + throw new ArgumentNullException("policy"); + this.policy = policy; + this.options = options ?? TextEditorOptions.Default; + } + + /// + /// Format the specified document and gives back the formatted text as result. + /// + public string Format(IDocument document) + { + return InternalFormat (new StringBuilderDocument (document.Text)); + } + + /// + /// Format the specified text and gives back the formatted text as result. + /// + public string Format(string text) + { + return InternalFormat (new StringBuilderDocument (text)); + } + + string InternalFormat(IDocument document) + { + var syntaxTree = SyntaxTree.Parse (document, document.FileName); + var changes = AnalyzeFormatting(document, syntaxTree); + changes.ApplyChanges(); + return document.Text; + } + + /// + /// Analyzes the formatting of a given document and syntax tree. + /// + /// Document. + /// Syntax tree. + /// The cancellation token. + public FormattingChanges AnalyzeFormatting(IDocument document, SyntaxTree syntaxTree, CancellationToken token = default (CancellationToken)) + { + if (document == null) + throw new ArgumentNullException("document"); + if (syntaxTree == null) + throw new ArgumentNullException("syntaxTree"); + var result = new FormattingChanges(document); + var visitor = new FormattingVisitor(this, document, result, token); + syntaxTree.AcceptVisitor(visitor); + return result; + } + + /// + /// Adds a region in the document that should be formatted. + /// + public void AddFormattingRegion (DomRegion region) + { + formattingRegions.Add(region); + if (formattingRegions.Count == 1) { + lastFormattingLocation = region.End; + } else { + lastFormattingLocation = lastFormattingLocation < region.End ? region.End : lastFormattingLocation; + } + } + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormattingOptions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormattingOptions.cs new file mode 100644 index 000000000..0a97a1c0b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormattingOptions.cs @@ -0,0 +1,1039 @@ +// +// CSharpFormattingOptions.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Reflection; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum BraceStyle + { + DoNotChange, + EndOfLine, + EndOfLineWithoutSpace, + NextLine, + NextLineShifted, + NextLineShifted2, + BannerStyle + } + + public enum PropertyFormatting + { + AllowOneLine, + ForceOneLine, + ForceNewLine + } + + public enum Wrapping { + DoNotChange, + DoNotWrap, + WrapAlways, + WrapIfTooLong + } + + public enum NewLinePlacement { + DoNotCare, + NewLine, + SameLine + } + + public enum UsingPlacement { + TopOfFile, + InsideNamespace + } + + public enum EmptyLineFormatting { + DoNotChange, + Indent, + DoNotIndent + } + + public class CSharpFormattingOptions + { + public string Name { + get; + set; + } + + public bool IsBuiltIn { + get; + set; + } + + public CSharpFormattingOptions Clone () + { + return (CSharpFormattingOptions)MemberwiseClone (); + } + + #region Indentation + public bool IndentNamespaceBody { // tested + get; + set; + } + + public bool IndentClassBody { // tested + get; + set; + } + + public bool IndentInterfaceBody { // tested + get; + set; + } + + public bool IndentStructBody { // tested + get; + set; + } + + public bool IndentEnumBody { // tested + get; + set; + } + + public bool IndentMethodBody { // tested + get; + set; + } + + public bool IndentPropertyBody { // tested + get; + set; + } + + public bool IndentEventBody { // tested + get; + set; + } + + public bool IndentBlocks { // tested + get; + set; + } + + public bool IndentSwitchBody { // tested + get; + set; + } + + public bool IndentCaseBody { // tested + get; + set; + } + + public bool IndentBreakStatements { // tested + get; + set; + } + + public bool AlignEmbeddedStatements { // tested + get; + set; + } + + public bool AlignElseInIfStatements { + get; + set; + } + + + + public PropertyFormatting AutoPropertyFormatting { // tested + get; + set; + } + + public PropertyFormatting SimplePropertyFormatting { // tested + get; + set; + } + + public EmptyLineFormatting EmptyLineFormatting { + get; + set; + } + + public bool IndentPreprocessorDirectives { // tested + get; + set; + } + + public bool AlignToMemberReferenceDot { // TODO! + get; + set; + } + + public bool IndentBlocksInsideExpressions { + get; + set; + } + #endregion + + #region Braces + public BraceStyle NamespaceBraceStyle { // tested + get; + set; + } + + public BraceStyle ClassBraceStyle { // tested + get; + set; + } + + public BraceStyle InterfaceBraceStyle { // tested + get; + set; + } + + public BraceStyle StructBraceStyle { // tested + get; + set; + } + + public BraceStyle EnumBraceStyle { // tested + get; + set; + } + + public BraceStyle MethodBraceStyle { // tested + get; + set; + } + + public BraceStyle AnonymousMethodBraceStyle { + get; + set; + } + + public BraceStyle ConstructorBraceStyle { // tested + get; + set; + } + + public BraceStyle DestructorBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertyBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertyGetBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertySetBraceStyle { // tested + get; + set; + } + + public PropertyFormatting SimpleGetBlockFormatting { // tested + get; + set; + } + + public PropertyFormatting SimpleSetBlockFormatting { // tested + get; + set; + } + + public BraceStyle EventBraceStyle { // tested + get; + set; + } + + public BraceStyle EventAddBraceStyle { // tested + get; + set; + } + + public BraceStyle EventRemoveBraceStyle { // tested + get; + set; + } + + public bool AllowEventAddBlockInline { // tested + get; + set; + } + + public bool AllowEventRemoveBlockInline { // tested + get; + set; + } + + public BraceStyle StatementBraceStyle { // tested + get; + set; + } + + public bool AllowIfBlockInline { + get; + set; + } + + bool allowOneLinedArrayInitialziers = true; + public bool AllowOneLinedArrayInitialziers { + get { + return allowOneLinedArrayInitialziers; + } + set { + allowOneLinedArrayInitialziers = value; + } + } + #endregion + + #region NewLines + public NewLinePlacement ElseNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement ElseIfNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement CatchNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement FinallyNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement WhileNewLinePlacement { // tested + get; + set; + } + + NewLinePlacement embeddedStatementPlacement = NewLinePlacement.NewLine; + public NewLinePlacement EmbeddedStatementPlacement { + get { + return embeddedStatementPlacement; + } + set { + embeddedStatementPlacement = value; + } + } + #endregion + + #region Spaces + // Methods + public bool SpaceBeforeMethodDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyMethodDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeMethodDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceAfterMethodDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceWithinMethodDeclarationParentheses { // tested + get; + set; + } + + // Method calls + public bool SpaceBeforeMethodCallParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyMethodCallParentheses { // tested + get; + set; + } + + public bool SpaceBeforeMethodCallParameterComma { // tested + get; + set; + } + + public bool SpaceAfterMethodCallParameterComma { // tested + get; + set; + } + + public bool SpaceWithinMethodCallParentheses { // tested + get; + set; + } + + // fields + + public bool SpaceBeforeFieldDeclarationComma { // tested + get; + set; + } + + public bool SpaceAfterFieldDeclarationComma { // tested + get; + set; + } + + // local variables + + public bool SpaceBeforeLocalVariableDeclarationComma { // tested + get; + set; + } + + public bool SpaceAfterLocalVariableDeclarationComma { // tested + get; + set; + } + + // constructors + + public bool SpaceBeforeConstructorDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyConstructorDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBeforeConstructorDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceAfterConstructorDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceWithinConstructorDeclarationParentheses { // tested + get; + set; + } + + public NewLinePlacement NewLineBeforeConstructorInitializerColon { + get; + set; + } + + public NewLinePlacement NewLineAfterConstructorInitializerColon { + get; + set; + } + + // indexer + public bool SpaceBeforeIndexerDeclarationBracket { // tested + get; + set; + } + + public bool SpaceWithinIndexerDeclarationBracket { // tested + get; + set; + } + + public bool SpaceBeforeIndexerDeclarationParameterComma { + get; + set; + } + + public bool SpaceAfterIndexerDeclarationParameterComma { + get; + set; + } + + // delegates + + public bool SpaceBeforeDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBetweenEmptyDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeDelegateDeclarationParameterComma { + get; + set; + } + + public bool SpaceAfterDelegateDeclarationParameterComma { + get; + set; + } + + public bool SpaceWithinDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeNewParentheses { // tested + get; + set; + } + + public bool SpaceBeforeIfParentheses { // tested + get; + set; + } + + public bool SpaceBeforeWhileParentheses { // tested + get; + set; + } + + public bool SpaceBeforeForParentheses { // tested + get; + set; + } + + public bool SpaceBeforeForeachParentheses { // tested + get; + set; + } + + public bool SpaceBeforeCatchParentheses { // tested + get; + set; + } + + public bool SpaceBeforeSwitchParentheses { // tested + get; + set; + } + + public bool SpaceBeforeLockParentheses { // tested + get; + set; + } + + public bool SpaceBeforeUsingParentheses { // tested + get; + set; + } + + public bool SpaceAroundAssignment { // tested + get; + set; + } + + public bool SpaceAroundLogicalOperator { // tested + get; + set; + } + + public bool SpaceAroundEqualityOperator { // tested + get; + set; + } + + public bool SpaceAroundRelationalOperator { // tested + get; + set; + } + + public bool SpaceAroundBitwiseOperator { // tested + get; + set; + } + + public bool SpaceAroundAdditiveOperator { // tested + get; + set; + } + + public bool SpaceAroundMultiplicativeOperator { // tested + get; + set; + } + + public bool SpaceAroundShiftOperator { // tested + get; + set; + } + + public bool SpaceAroundNullCoalescingOperator { // Tested + get; + set; + } + + public bool SpaceAfterUnsafeAddressOfOperator { // Tested + get; + set; + } + + public bool SpaceAfterUnsafeAsteriskOfOperator { // Tested + get; + set; + } + + public bool SpaceAroundUnsafeArrowOperator { // Tested + get; + set; + } + + public bool SpacesWithinParentheses { // tested + get; + set; + } + + public bool SpacesWithinIfParentheses { // tested + get; + set; + } + + public bool SpacesWithinWhileParentheses { // tested + get; + set; + } + + public bool SpacesWithinForParentheses { // tested + get; + set; + } + + public bool SpacesWithinForeachParentheses { // tested + get; + set; + } + + public bool SpacesWithinCatchParentheses { // tested + get; + set; + } + + public bool SpacesWithinSwitchParentheses { // tested + get; + set; + } + + public bool SpacesWithinLockParentheses { // tested + get; + set; + } + + public bool SpacesWithinUsingParentheses { // tested + get; + set; + } + + public bool SpacesWithinCastParentheses { // tested + get; + set; + } + + public bool SpacesWithinSizeOfParentheses { // tested + get; + set; + } + + public bool SpaceBeforeSizeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinTypeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinNewParentheses { // tested + get; + set; + } + + public bool SpacesBetweenEmptyNewParentheses { // tested + get; + set; + } + + public bool SpaceBeforeNewParameterComma { // tested + get; + set; + } + + public bool SpaceAfterNewParameterComma { // tested + get; + set; + } + + public bool SpaceBeforeTypeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinCheckedExpressionParantheses { // tested + get; + set; + } + + public bool SpaceBeforeConditionalOperatorCondition { // tested + get; + set; + } + + public bool SpaceAfterConditionalOperatorCondition { // tested + get; + set; + } + + public bool SpaceBeforeConditionalOperatorSeparator { // tested + get; + set; + } + + public bool SpaceAfterConditionalOperatorSeparator { // tested + get; + set; + } + + // brackets + public bool SpacesWithinBrackets { // tested + get; + set; + } + + public bool SpacesBeforeBrackets { // tested + get; + set; + } + + public bool SpaceBeforeBracketComma { // tested + get; + set; + } + + public bool SpaceAfterBracketComma { // tested + get; + set; + } + + public bool SpaceBeforeForSemicolon { // tested + get; + set; + } + + public bool SpaceAfterForSemicolon { // tested + get; + set; + } + + public bool SpaceAfterTypecast { // tested + get; + set; + } + + public bool SpaceBeforeArrayDeclarationBrackets { // tested + get; + set; + } + + public bool SpaceInNamedArgumentAfterDoubleColon { + get; + set; + } + + public bool RemoveEndOfLineWhiteSpace { + get; + set; + } + + public bool SpaceBeforeSemicolon { + get; + set; + } + #endregion + + #region Blank Lines + public int MinimumBlankLinesBeforeUsings { + get; + set; + } + + public int MinimumBlankLinesAfterUsings { + get; + set; + } + + public int MinimumBlankLinesBeforeFirstDeclaration { + get; + set; + } + + public int MinimumBlankLinesBetweenTypes { + get; + set; + } + + public int MinimumBlankLinesBetweenFields { + get; + set; + } + + public int MinimumBlankLinesBetweenEventFields { + get; + set; + } + + public int MinimumBlankLinesBetweenMembers { + get; + set; + } + + public int MinimumBlankLinesAroundRegion { + get; + set; + } + + public int MinimumBlankLinesInsideRegion { + get; + set; + } + + #endregion + + + #region Keep formatting + public bool KeepCommentsAtFirstColumn { + get; + set; + } + #endregion + + #region Wrapping + + public Wrapping ArrayInitializerWrapping { + get; + set; + } + + public BraceStyle ArrayInitializerBraceStyle { + get; + set; + } + + public Wrapping ChainedMethodCallWrapping { + get; + set; + } + + public Wrapping MethodCallArgumentWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferMethodCallOpenParentheses { + get; + set; + } + + public NewLinePlacement MethodCallClosingParenthesesOnNewLine { + get; + set; + } + + public Wrapping IndexerArgumentWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferIndexerOpenBracket { + get; + set; + } + + public NewLinePlacement IndexerClosingBracketOnNewLine { + get; + set; + } + + public Wrapping MethodDeclarationParameterWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferMethodDeclarationOpenParentheses { + get; + set; + } + + public NewLinePlacement MethodDeclarationClosingParenthesesOnNewLine { + get; + set; + } + + public Wrapping IndexerDeclarationParameterWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferIndexerDeclarationOpenBracket { + get; + set; + } + + public NewLinePlacement IndexerDeclarationClosingBracketOnNewLine { + get; + set; + } + + public bool AlignToFirstIndexerArgument { + get; + set; + } + + public bool AlignToFirstIndexerDeclarationParameter { + get; + set; + } + + public bool AlignToFirstMethodCallArgument { + get; + set; + } + + public bool AlignToFirstMethodDeclarationParameter { + get; + set; + } + + public NewLinePlacement NewLineBeforeNewQueryClause { + get; + set; + } + + #endregion + + #region Using Declarations + public UsingPlacement UsingPlacement { + get; + set; + } + #endregion + + internal CSharpFormattingOptions() + { + } + + /*public static CSharpFormattingOptions Load (FilePath selectedFile) + { + using (var stream = System.IO.File.OpenRead (selectedFile)) { + return Load (stream); + } + } + + public static CSharpFormattingOptions Load (System.IO.Stream input) + { + CSharpFormattingOptions result = FormattingOptionsFactory.CreateMonoOptions (); + result.Name = "noname"; + using (XmlTextReader reader = new XmlTextReader (input)) { + while (reader.Read ()) { + if (reader.NodeType == XmlNodeType.Element) { + if (reader.LocalName == "Property") { + var info = typeof(CSharpFormattingOptions).GetProperty (reader.GetAttribute ("name")); + string valString = reader.GetAttribute ("value"); + object value; + if (info.PropertyType == typeof(bool)) { + value = Boolean.Parse (valString); + } else if (info.PropertyType == typeof(int)) { + value = Int32.Parse (valString); + } else { + value = Enum.Parse (info.PropertyType, valString); + } + info.SetValue (result, value, null); + } else if (reader.LocalName == "FormattingProfile") { + result.Name = reader.GetAttribute ("name"); + } + } else if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "FormattingProfile") { + //Console.WriteLine ("result:" + result.Name); + return result; + } + } + } + return result; + } + + public void Save (string fileName) + { + using (var writer = new XmlTextWriter (fileName, Encoding.Default)) { + writer.Formatting = System.Xml.Formatting.Indented; + writer.Indentation = 1; + writer.IndentChar = '\t'; + writer.WriteStartElement ("FormattingProfile"); + writer.WriteAttributeString ("name", Name); + foreach (PropertyInfo info in typeof (CSharpFormattingOptions).GetProperties ()) { + if (info.GetCustomAttributes (false).Any (o => o.GetType () == typeof(ItemPropertyAttribute))) { + writer.WriteStartElement ("Property"); + writer.WriteAttributeString ("name", info.Name); + writer.WriteAttributeString ("value", info.GetValue (this, null).ToString ()); + writer.WriteEndElement (); + } + } + writer.WriteEndElement (); + } + } + + public bool Equals (CSharpFormattingOptions other) + { + foreach (PropertyInfo info in typeof (CSharpFormattingOptions).GetProperties ()) { + if (info.GetCustomAttributes (false).Any (o => o.GetType () == typeof(ItemPropertyAttribute))) { + object val = info.GetValue (this, null); + object otherVal = info.GetValue (other, null); + if (val == null) { + if (otherVal == null) + continue; + return false; + } + if (!val.Equals (otherVal)) { + //Console.WriteLine ("!equal"); + return false; + } + } + } + //Console.WriteLine ("== equal"); + return true; + }*/ + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/ConstructFixer.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/ConstructFixer.cs new file mode 100644 index 000000000..f2fd87547 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/ConstructFixer.cs @@ -0,0 +1,514 @@ +// +// ConstructFixer.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using System.Text; +using System.Reflection; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + abstract class ConstructCompleter + { + public abstract bool TryFix (ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset); + + protected AstNode GetLastNonErrorChild (AstNode node) + { + var lastNode = node.LastChild; + + while (lastNode is ErrorNode) { + lastNode = lastNode.GetPrevNode(FormattingVisitor.NoWhitespacePredicate); + } + return lastNode; + } + } + + class TypeDeclarationCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var typeDeclaration = syntaxTree.GetNodeAt(location); + if (typeDeclaration != null) { + if (typeDeclaration.LBraceToken.IsNull && typeDeclaration.RBraceToken.IsNull) { + if (typeDeclaration.Members.Any()) + return false; + var lastNode = GetLastNonErrorChild (typeDeclaration); + if (lastNode == null) + return false; + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (typeDeclaration, fixer.Options.ClassBraceStyle, false, ref newOffset)); + return true; + } + } + return false; + } + } + + class DelegateDeclarationCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var typeDeclaration = syntaxTree.GetNodeAt(location); + if (typeDeclaration != null) { + if (typeDeclaration.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (typeDeclaration); + if (lastNode == null) + return false; + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, ");\n"); + newOffset += ");\n".Length; + return true; + } + } + return false; + } + } + + class MethodDeclarationCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var methodDeclaration = syntaxTree.GetNodeAt(location); + if (methodDeclaration != null) { + if (!methodDeclaration.LParToken.IsNull && methodDeclaration.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (methodDeclaration); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, ")\n\t{\t\t\n\t}"); + newOffset += ")\n\t{\t\t".Length; + return true; + } + } + return false; + } + } + + class IfStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var ifStatement = syntaxTree.GetNodeAt(location); + if (ifStatement != null) { + if (!ifStatement.LParToken.IsNull && ifStatement.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (ifStatement); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (ifStatement, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class ForeachStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var ifStatement = syntaxTree.GetNodeAt(location); + if (ifStatement != null) { + if (!ifStatement.LParToken.IsNull && ifStatement.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (ifStatement); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (ifStatement, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class WhileStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var ifStatement = syntaxTree.GetNodeAt(location); + if (ifStatement != null) { + if (!ifStatement.LParToken.IsNull && ifStatement.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (ifStatement); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (ifStatement, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class DoWhileStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + if (stmt != null) { + if (!stmt.LParToken.IsNull && stmt.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (stmt); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, ");"); + newOffset = insertionOffset + 2; + return true; + } + } + return false; + } + } + + class FixedStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + if (stmt != null) { + if (!stmt.LParToken.IsNull && stmt.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (stmt); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (stmt, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class SwitchStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var switchStatement = syntaxTree.GetNodeAt(location); + if (switchStatement != null) { + if (!switchStatement.LParToken.IsNull && switchStatement.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (switchStatement); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (switchStatement, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class InvocationCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var invocationExpression = syntaxTree.GetNodeAt(location); + + if (invocationExpression != null) { + if (!invocationExpression.LParToken.IsNull && invocationExpression.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (invocationExpression); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + + newOffset = insertionOffset; + + + var text = ")"; + newOffset++; + var expressionStatement = invocationExpression.Parent as ExpressionStatement; + if (expressionStatement != null) { + if (expressionStatement.SemicolonToken.IsNull) + text = ");"; + newOffset ++; + } + document.Insert(insertionOffset, text); + + + return true; + } + + } + return false; + } + } + + class BreakStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.SemicolonToken.IsNull) { + // TODO !!!! + return true; + } + return false; + } + } + + class CheckedStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.Parent is ExpressionStatement) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (stmt, fixer.Options.StatementBraceStyle, false, ref newOffset)); + return true; + } + return false; + } + } + + class UncheckedStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.Parent is ExpressionStatement) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (stmt, fixer.Options.StatementBraceStyle, false, ref newOffset)); + return true; + } + return false; + } + } + + class ExpressionStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var expressionStatement = syntaxTree.GetNodeAt(location); + + if (expressionStatement != null) { + int offset = document.GetOffset(expressionStatement.Expression.EndLocation); + if (expressionStatement.SemicolonToken.IsNull) { + document.Insert(offset, ";"); + newOffset = offset + 1; + } + return true; + } + return false; + } + } + + class LockStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + if (stmt != null) { + if (!stmt.LParToken.IsNull && stmt.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (stmt); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (stmt, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class ReturnStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.SemicolonToken.IsNull) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, ";"); + newOffset = insertionOffset + 1; + return true; + } + return false; + } + } + + class YieldReturnStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.SemicolonToken.IsNull) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, ";"); + newOffset = insertionOffset + 1; + return true; + } + return false; + } + } + + class ThrowStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.SemicolonToken.IsNull) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, ";"); + newOffset = insertionOffset + 1; + return true; + } + return false; + } + } + + + public class ConstructFixer + { + static readonly ConstructCompleter[] completer = { + new TypeDeclarationCompleter(), + new DelegateDeclarationCompleter (), + new MethodDeclarationCompleter (), + new IfStatementCompleter (), + new ForeachStatementCompleter (), + new WhileStatementCompleter (), + new LockStatementCompleter (), + new FixedStatementCompleter (), + new DoWhileStatementCompleter (), + new SwitchStatementCompleter (), + new BreakStatementCompleter (), + new ThrowStatementCompleter (), + new ReturnStatementCompleter (), + new YieldReturnStatementCompleter (), + new CheckedStatementCompleter (), + new UncheckedStatementCompleter (), + + new InvocationCompleter (), + new ExpressionStatementCompleter () + }; + + readonly CSharpFormattingOptions options; + readonly TextEditorOptions textEditorOptions; + + public CSharpFormattingOptions Options { + get { + return options; + } + } + + public ConstructFixer(CSharpFormattingOptions options, TextEditorOptions textEditorOptions) + { + this.options = options; + this.textEditorOptions = textEditorOptions; + } + + + string GetIndent(AstNode node) + { + if (node == null || node is SyntaxTree) + return ""; + if (node is BlockStatement || node is TypeDeclaration || node is NamespaceDeclaration) + return "\t" + GetIndent(node.Parent); + return GetIndent(node.Parent); + } + + internal string GenerateBody(AstNode node, BraceStyle braceStyle, bool addClosingBracket, ref int newOffset) + { + StringBuilder result = new StringBuilder(); + if (addClosingBracket) + result.Append(")"); + var nodeIndent = GetIndent(node.Parent); + switch (braceStyle) { + case BraceStyle.DoNotChange: + case BraceStyle.BannerStyle: + case BraceStyle.EndOfLine: + result.Append(" "); + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + break; + case BraceStyle.EndOfLineWithoutSpace: + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + break; + case BraceStyle.NextLine: + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent); + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + break; + case BraceStyle.NextLineShifted: + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + break; + case BraceStyle.NextLineShifted2: + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t" + "\t"); + break; + } + + newOffset += result.Length; + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent); + result.Append("}"); + + return result.ToString(); + } + + public bool TryFix (IDocument document, int offset, out int newOffset) + { + newOffset = offset; + + var syntaxTree = SyntaxTree.Parse(document, "a.cs"); + var location = document.GetLocation(offset - 1); + foreach (var c in completer) { + if (c.TryFix(this, syntaxTree, document, location, ref newOffset)) { + return true; + } + } + return false; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingChanges.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingChanges.cs new file mode 100644 index 000000000..f60b58bbd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingChanges.cs @@ -0,0 +1,169 @@ +// +// CSharpFormatter.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using System.Threading; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// The formatting changes are used to format a specific region inside a document and apply a minimal formatting + /// changeset to a given document. This is useful for a text editor environment. + /// + public class FormattingChanges + { + readonly IDocument document; + readonly internal List changes = new List (); + + internal FormattingChanges (IDocument document) + { + if (document == null) + throw new ArgumentNullException("document"); + this.document = document; + } + + public int Count { + get { + return changes.Count; + } + } + + /// + /// Applies the changes to the input document. + /// + public void ApplyChanges() + { + ApplyChanges(0, document.TextLength, document.Replace, (o, l, v) => document.GetText(o, l) == v); + } + + public void ApplyChanges(int startOffset, int length) + { + ApplyChanges(startOffset, length, document.Replace, (o, l, v) => document.GetText(o, l) == v); + } + + /// + /// Applies the changes to the given Script instance. + /// + public void ApplyChanges(Script script) + { + ApplyChanges(0, document.TextLength, script.Replace); + } + + public void ApplyChanges(int startOffset, int length, Script script) + { + ApplyChanges(startOffset, length, script.Replace); + } + + public void ApplyChanges(int startOffset, int length, Action documentReplace, Func filter = null) + { + int endOffset = startOffset + length; + // Console.WriteLine ("apply:"+ startOffset + "->" + endOffset); + // Console.WriteLine (document.Text.Substring (0, startOffset) + new string ('x',length) + document.Text.Substring (startOffset+ length)); + + TextReplaceAction previousChange = null; + int delta = 0; + var depChanges = new List (); + foreach (var change in changes.OrderBy(c => c.Offset)) { + if (previousChange != null) { + if (change.Equals(previousChange)) { + // ignore duplicate changes + continue; + } + if (change.Offset < previousChange.Offset + previousChange.RemovalLength) { + throw new InvalidOperationException ("Detected overlapping changes " + change + "/" + previousChange); + } + } + previousChange = change; + bool skipChange = change.Offset + change.RemovalLength < startOffset || change.Offset > endOffset; + skipChange |= filter != null && filter(change.Offset + delta, change.RemovalLength, change.NewText); + skipChange &= !depChanges.Contains(change); + if (!skipChange) { + documentReplace(change.Offset + delta, change.RemovalLength, change.NewText); + delta += change.NewText.Length - change.RemovalLength; + if (change.DependsOn != null) { + depChanges.Add(change.DependsOn); + } + } + } + changes.Clear(); + } + + internal TextReplaceAction AddChange(int offset, int removedChars, string insertedText) + { + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", "Should be >= 0"); + if (offset >= document.TextLength) + throw new ArgumentOutOfRangeException("offset", "Should be < document.TextLength"); + if (removedChars < 0) + throw new ArgumentOutOfRangeException("removedChars", "Should be >= 0"); + if (removedChars > offset + document.TextLength) + throw new ArgumentOutOfRangeException("removedChars", "Tried to remove beyond end of text"); + if (removedChars == 0 && string.IsNullOrEmpty (insertedText)) + return null; + var action = new TextReplaceAction (offset, removedChars, insertedText); + changes.Add(action); + return action; + } + + internal sealed class TextReplaceAction + { + internal readonly int Offset; + internal readonly int RemovalLength; + internal readonly string NewText; + internal TextReplaceAction DependsOn; + + public TextReplaceAction (int offset, int removalLength, string newText) + { + this.Offset = offset; + this.RemovalLength = removalLength; + this.NewText = newText ?? string.Empty; + } + + public override bool Equals(object obj) + { + TextReplaceAction other = obj as TextReplaceAction; + if (other == null) { + return false; + } + return this.Offset == other.Offset && this.RemovalLength == other.RemovalLength && this.NewText == other.NewText; + } + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() + { + return string.Format("[TextReplaceAction: Offset={0}, RemovalLength={1}, NewText={2}]", Offset, RemovalLength, NewText); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs new file mode 100644 index 000000000..ff32c1f8c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs @@ -0,0 +1,446 @@ +// +// FormattingOptionsFactory.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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 +{ + /// + /// The formatting options factory creates pre defined formatting option styles. + /// + public static class FormattingOptionsFactory + { + /// + /// Creates empty CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateEmpty() + { + return new CSharpFormattingOptions(); + } + + /// + /// Creates mono indent style CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateMono() + { + return new CSharpFormattingOptions { + IndentNamespaceBody = true, + IndentClassBody = true, + IndentInterfaceBody = true, + IndentStructBody = true, + IndentEnumBody = true, + IndentMethodBody = true, + IndentPropertyBody = true, + IndentEventBody = true, + IndentBlocks = true, + IndentSwitchBody = false, + IndentCaseBody = true, + IndentBreakStatements = true, + IndentPreprocessorDirectives = true, + IndentBlocksInsideExpressions = false, + NamespaceBraceStyle = BraceStyle.NextLine, + ClassBraceStyle = BraceStyle.NextLine, + InterfaceBraceStyle = BraceStyle.NextLine, + StructBraceStyle = BraceStyle.NextLine, + EnumBraceStyle = BraceStyle.NextLine, + MethodBraceStyle = BraceStyle.NextLine, + ConstructorBraceStyle = BraceStyle.NextLine, + DestructorBraceStyle = BraceStyle.NextLine, + AnonymousMethodBraceStyle = BraceStyle.EndOfLine, + + PropertyBraceStyle = BraceStyle.EndOfLine, + PropertyGetBraceStyle = BraceStyle.EndOfLine, + PropertySetBraceStyle = BraceStyle.EndOfLine, + SimpleGetBlockFormatting = PropertyFormatting.AllowOneLine, + SimpleSetBlockFormatting = PropertyFormatting.AllowOneLine, + + EventBraceStyle = BraceStyle.EndOfLine, + EventAddBraceStyle = BraceStyle.EndOfLine, + EventRemoveBraceStyle = BraceStyle.EndOfLine, + AllowEventAddBlockInline = true, + AllowEventRemoveBlockInline = true, + StatementBraceStyle = BraceStyle.EndOfLine, + + ElseNewLinePlacement = NewLinePlacement.SameLine, + ElseIfNewLinePlacement = NewLinePlacement.SameLine, + CatchNewLinePlacement = NewLinePlacement.SameLine, + FinallyNewLinePlacement = NewLinePlacement.SameLine, + WhileNewLinePlacement = NewLinePlacement.SameLine, + ArrayInitializerWrapping = Wrapping.WrapIfTooLong, + ArrayInitializerBraceStyle = BraceStyle.EndOfLine, + AllowOneLinedArrayInitialziers = true, + + SpaceBeforeMethodCallParentheses = true, + SpaceBeforeMethodDeclarationParentheses = true, + SpaceBeforeConstructorDeclarationParentheses = true, + SpaceBeforeDelegateDeclarationParentheses = true, + SpaceAfterMethodCallParameterComma = true, + SpaceAfterConstructorDeclarationParameterComma = true, + + SpaceBeforeNewParentheses = true, + SpacesWithinNewParentheses = false, + SpacesBetweenEmptyNewParentheses = false, + SpaceBeforeNewParameterComma = false, + SpaceAfterNewParameterComma = true, + + SpaceBeforeIfParentheses = true, + SpaceBeforeWhileParentheses = true, + SpaceBeforeForParentheses = true, + SpaceBeforeForeachParentheses = true, + SpaceBeforeCatchParentheses = true, + SpaceBeforeSwitchParentheses = true, + SpaceBeforeLockParentheses = true, + SpaceBeforeUsingParentheses = true, + SpaceAroundAssignment = true, + SpaceAroundLogicalOperator = true, + SpaceAroundEqualityOperator = true, + SpaceAroundRelationalOperator = true, + SpaceAroundBitwiseOperator = true, + SpaceAroundAdditiveOperator = true, + SpaceAroundMultiplicativeOperator = true, + SpaceAroundShiftOperator = true, + SpaceAroundNullCoalescingOperator = true, + SpacesWithinParentheses = false, + SpaceWithinMethodCallParentheses = false, + SpaceWithinMethodDeclarationParentheses = false, + SpacesWithinIfParentheses = false, + SpacesWithinWhileParentheses = false, + SpacesWithinForParentheses = false, + SpacesWithinForeachParentheses = false, + SpacesWithinCatchParentheses = false, + SpacesWithinSwitchParentheses = false, + SpacesWithinLockParentheses = false, + SpacesWithinUsingParentheses = false, + SpacesWithinCastParentheses = false, + SpacesWithinSizeOfParentheses = false, + SpacesWithinTypeOfParentheses = false, + SpacesWithinCheckedExpressionParantheses = false, + SpaceBeforeConditionalOperatorCondition = true, + SpaceAfterConditionalOperatorCondition = true, + SpaceBeforeConditionalOperatorSeparator = true, + SpaceAfterConditionalOperatorSeparator = true, + + SpacesWithinBrackets = false, + SpacesBeforeBrackets = true, + SpaceBeforeBracketComma = false, + SpaceAfterBracketComma = true, + + SpaceBeforeForSemicolon = false, + SpaceAfterForSemicolon = true, + SpaceAfterTypecast = false, + + AlignEmbeddedStatements = true, + SimplePropertyFormatting = PropertyFormatting.AllowOneLine, + AutoPropertyFormatting = PropertyFormatting.AllowOneLine, + EmptyLineFormatting = EmptyLineFormatting.DoNotIndent, + SpaceBeforeMethodDeclarationParameterComma = false, + SpaceAfterMethodDeclarationParameterComma = true, + SpaceAfterDelegateDeclarationParameterComma = true, + SpaceBeforeFieldDeclarationComma = false, + SpaceAfterFieldDeclarationComma = true, + SpaceBeforeLocalVariableDeclarationComma = false, + SpaceAfterLocalVariableDeclarationComma = true, + + SpaceBeforeIndexerDeclarationBracket = true, + SpaceWithinIndexerDeclarationBracket = false, + SpaceBeforeIndexerDeclarationParameterComma = false, + SpaceInNamedArgumentAfterDoubleColon = true, + RemoveEndOfLineWhiteSpace = true, + + SpaceAfterIndexerDeclarationParameterComma = true, + + MinimumBlankLinesBeforeUsings = 0, + MinimumBlankLinesAfterUsings = 1, + UsingPlacement = UsingPlacement.TopOfFile, + + MinimumBlankLinesBeforeFirstDeclaration = 0, + MinimumBlankLinesBetweenTypes = 1, + MinimumBlankLinesBetweenFields = 0, + MinimumBlankLinesBetweenEventFields = 0, + MinimumBlankLinesBetweenMembers = 1, + MinimumBlankLinesAroundRegion = 1, + MinimumBlankLinesInsideRegion = 1, + AlignToFirstIndexerArgument = false, + AlignToFirstIndexerDeclarationParameter = true, + AlignToFirstMethodCallArgument = false, + AlignToFirstMethodDeclarationParameter = true, + KeepCommentsAtFirstColumn = true, + ChainedMethodCallWrapping = Wrapping.DoNotChange, + MethodCallArgumentWrapping = Wrapping.DoNotChange, + NewLineAferMethodCallOpenParentheses = NewLinePlacement.DoNotCare, + MethodCallClosingParenthesesOnNewLine = NewLinePlacement.DoNotCare, + + IndexerArgumentWrapping = Wrapping.DoNotChange, + NewLineAferIndexerOpenBracket = NewLinePlacement.DoNotCare, + IndexerClosingBracketOnNewLine = NewLinePlacement.DoNotCare, + + NewLineBeforeNewQueryClause = NewLinePlacement.NewLine + }; + } + + /// + /// Creates sharp develop indent style CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateSharpDevelop() + { + var baseOptions = CreateKRStyle(); + return baseOptions; + } + + /// + /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, + /// is commonly used in C. It is less common for C++, C#, and others. + /// + public static CSharpFormattingOptions CreateKRStyle() + { + return new CSharpFormattingOptions() { + IndentNamespaceBody = true, + IndentClassBody = true, + IndentInterfaceBody = true, + IndentStructBody = true, + IndentEnumBody = true, + IndentMethodBody = true, + IndentPropertyBody = true, + IndentEventBody = true, + IndentBlocks = true, + IndentSwitchBody = true, + IndentCaseBody = true, + IndentBreakStatements = true, + IndentPreprocessorDirectives = true, + NamespaceBraceStyle = BraceStyle.NextLine, + ClassBraceStyle = BraceStyle.NextLine, + InterfaceBraceStyle = BraceStyle.NextLine, + StructBraceStyle = BraceStyle.NextLine, + EnumBraceStyle = BraceStyle.NextLine, + MethodBraceStyle = BraceStyle.NextLine, + ConstructorBraceStyle = BraceStyle.NextLine, + DestructorBraceStyle = BraceStyle.NextLine, + AnonymousMethodBraceStyle = BraceStyle.EndOfLine, + PropertyBraceStyle = BraceStyle.EndOfLine, + PropertyGetBraceStyle = BraceStyle.EndOfLine, + PropertySetBraceStyle = BraceStyle.EndOfLine, + SimpleGetBlockFormatting = PropertyFormatting.AllowOneLine, + SimpleSetBlockFormatting = PropertyFormatting.AllowOneLine, + + EventBraceStyle = BraceStyle.EndOfLine, + EventAddBraceStyle = BraceStyle.EndOfLine, + EventRemoveBraceStyle = BraceStyle.EndOfLine, + AllowEventAddBlockInline = true, + AllowEventRemoveBlockInline = true, + StatementBraceStyle = BraceStyle.EndOfLine, + + ElseNewLinePlacement = NewLinePlacement.SameLine, + ElseIfNewLinePlacement = NewLinePlacement.SameLine, + CatchNewLinePlacement = NewLinePlacement.SameLine, + FinallyNewLinePlacement = NewLinePlacement.SameLine, + WhileNewLinePlacement = NewLinePlacement.SameLine, + ArrayInitializerWrapping = Wrapping.WrapIfTooLong, + ArrayInitializerBraceStyle = BraceStyle.EndOfLine, + + SpaceBeforeMethodCallParentheses = false, + SpaceBeforeMethodDeclarationParentheses = false, + SpaceBeforeConstructorDeclarationParentheses = false, + SpaceBeforeDelegateDeclarationParentheses = false, + SpaceBeforeIndexerDeclarationBracket = false, + SpaceAfterMethodCallParameterComma = true, + SpaceAfterConstructorDeclarationParameterComma = true, + NewLineBeforeConstructorInitializerColon = NewLinePlacement.NewLine, + NewLineAfterConstructorInitializerColon = NewLinePlacement.SameLine, + + SpaceBeforeNewParentheses = false, + SpacesWithinNewParentheses = false, + SpacesBetweenEmptyNewParentheses = false, + SpaceBeforeNewParameterComma = false, + SpaceAfterNewParameterComma = true, + + SpaceBeforeIfParentheses = true, + SpaceBeforeWhileParentheses = true, + SpaceBeforeForParentheses = true, + SpaceBeforeForeachParentheses = true, + SpaceBeforeCatchParentheses = true, + SpaceBeforeSwitchParentheses = true, + SpaceBeforeLockParentheses = true, + SpaceBeforeUsingParentheses = true, + + SpaceAroundAssignment = true, + SpaceAroundLogicalOperator = true, + SpaceAroundEqualityOperator = true, + SpaceAroundRelationalOperator = true, + SpaceAroundBitwiseOperator = true, + SpaceAroundAdditiveOperator = true, + SpaceAroundMultiplicativeOperator = true, + SpaceAroundShiftOperator = true, + SpaceAroundNullCoalescingOperator = true, + SpacesWithinParentheses = false, + SpaceWithinMethodCallParentheses = false, + SpaceWithinMethodDeclarationParentheses = false, + SpacesWithinIfParentheses = false, + SpacesWithinWhileParentheses = false, + SpacesWithinForParentheses = false, + SpacesWithinForeachParentheses = false, + SpacesWithinCatchParentheses = false, + SpacesWithinSwitchParentheses = false, + SpacesWithinLockParentheses = false, + SpacesWithinUsingParentheses = false, + SpacesWithinCastParentheses = false, + SpacesWithinSizeOfParentheses = false, + SpacesWithinTypeOfParentheses = false, + SpacesWithinCheckedExpressionParantheses = false, + SpaceBeforeConditionalOperatorCondition = true, + SpaceAfterConditionalOperatorCondition = true, + SpaceBeforeConditionalOperatorSeparator = true, + SpaceAfterConditionalOperatorSeparator = true, + SpaceBeforeArrayDeclarationBrackets = false, + + SpacesWithinBrackets = false, + SpacesBeforeBrackets = false, + SpaceBeforeBracketComma = false, + SpaceAfterBracketComma = true, + + SpaceBeforeForSemicolon = false, + SpaceAfterForSemicolon = true, + SpaceAfterTypecast = false, + + AlignEmbeddedStatements = true, + SimplePropertyFormatting = PropertyFormatting.AllowOneLine, + AutoPropertyFormatting = PropertyFormatting.AllowOneLine, + EmptyLineFormatting = EmptyLineFormatting.DoNotIndent, + SpaceBeforeMethodDeclarationParameterComma = false, + SpaceAfterMethodDeclarationParameterComma = true, + SpaceAfterDelegateDeclarationParameterComma = true, + SpaceBeforeFieldDeclarationComma = false, + SpaceAfterFieldDeclarationComma = true, + SpaceBeforeLocalVariableDeclarationComma = false, + SpaceAfterLocalVariableDeclarationComma = true, + + SpaceWithinIndexerDeclarationBracket = false, + SpaceBeforeIndexerDeclarationParameterComma = false, + SpaceInNamedArgumentAfterDoubleColon = true, + + SpaceAfterIndexerDeclarationParameterComma = true, + RemoveEndOfLineWhiteSpace = true, + + MinimumBlankLinesBeforeUsings = 0, + MinimumBlankLinesAfterUsings = 1, + + MinimumBlankLinesBeforeFirstDeclaration = 0, + MinimumBlankLinesBetweenTypes = 1, + MinimumBlankLinesBetweenFields = 0, + MinimumBlankLinesBetweenEventFields = 0, + MinimumBlankLinesBetweenMembers = 1, + MinimumBlankLinesAroundRegion = 1, + MinimumBlankLinesInsideRegion = 1, + + KeepCommentsAtFirstColumn = true, + ChainedMethodCallWrapping = Wrapping.DoNotChange, + MethodCallArgumentWrapping = Wrapping.DoNotChange, + NewLineAferMethodCallOpenParentheses = NewLinePlacement.DoNotCare, + MethodCallClosingParenthesesOnNewLine = NewLinePlacement.DoNotCare, + + IndexerArgumentWrapping = Wrapping.DoNotChange, + NewLineAferIndexerOpenBracket = NewLinePlacement.DoNotCare, + IndexerClosingBracketOnNewLine = NewLinePlacement.DoNotCare, + + NewLineBeforeNewQueryClause = NewLinePlacement.NewLine + }; + } + + /// + /// Creates allman indent style CSharpFormatting options used in Visual Studio. + /// + public static CSharpFormattingOptions CreateAllman() + { + var baseOptions = CreateKRStyle(); + baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLine; + baseOptions.PropertyBraceStyle = BraceStyle.NextLine; + baseOptions.PropertyGetBraceStyle = BraceStyle.NextLine; + baseOptions.PropertySetBraceStyle = BraceStyle.NextLine; + + baseOptions.EventBraceStyle = BraceStyle.NextLine; + baseOptions.EventAddBraceStyle = BraceStyle.NextLine; + baseOptions.EventRemoveBraceStyle = BraceStyle.NextLine; + baseOptions.StatementBraceStyle = BraceStyle.NextLine; + baseOptions.ArrayInitializerBraceStyle = BraceStyle.NextLine; + + baseOptions.CatchNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.ElseNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.ElseIfNewLinePlacement = NewLinePlacement.SameLine; + + baseOptions.FinallyNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.WhileNewLinePlacement = NewLinePlacement.DoNotCare; + baseOptions.ArrayInitializerWrapping = Wrapping.DoNotChange; + baseOptions.IndentBlocksInsideExpressions = true; + + return baseOptions; + } + + /// + /// The Whitesmiths style, also called Wishart style to a lesser extent, is less common today than the previous three. It was originally used in the documentation for the first commercial C compiler, the Whitesmiths Compiler. + /// + public static CSharpFormattingOptions CreateWhitesmiths() + { + var baseOptions = CreateKRStyle(); + + baseOptions.NamespaceBraceStyle = BraceStyle.NextLineShifted; + baseOptions.ClassBraceStyle = BraceStyle.NextLineShifted; + baseOptions.InterfaceBraceStyle = BraceStyle.NextLineShifted; + baseOptions.StructBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EnumBraceStyle = BraceStyle.NextLineShifted; + baseOptions.MethodBraceStyle = BraceStyle.NextLineShifted; + baseOptions.ConstructorBraceStyle = BraceStyle.NextLineShifted; + baseOptions.DestructorBraceStyle = BraceStyle.NextLineShifted; + baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertyBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertyGetBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertySetBraceStyle = BraceStyle.NextLineShifted; + + baseOptions.EventBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EventAddBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EventRemoveBraceStyle = BraceStyle.NextLineShifted; + baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted; + baseOptions.IndentBlocksInsideExpressions = true; + return baseOptions; + } + + /// + /// Like the Allman and Whitesmiths styles, GNU style puts braces on a line by themselves, indented by 2 spaces, + /// except when opening a function definition, where they are not indented. + /// In either case, the contained code is indented by 2 spaces from the braces. + /// Popularised by Richard Stallman, the layout may be influenced by his background of writing Lisp code. + /// In Lisp the equivalent to a block (a progn) + /// is a first class data entity and giving it its own indent level helps to emphasize that, + /// whereas in C a block is just syntax. + /// Although not directly related to indentation, GNU coding style also includes a space before the bracketed + /// list of arguments to a function. + /// + public static CSharpFormattingOptions CreateGNU() + { + var baseOptions = CreateAllman(); + baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted2; + return baseOptions; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor.cs new file mode 100644 index 000000000..485996c91 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor.cs @@ -0,0 +1,662 @@ +// +// FormattingVisitor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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.Text; +using System.Linq; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; +using System.Threading; +using System.Collections.Generic; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Obsolete("This class was replaced by CSharpFormatter.")] + public class AstFormattingVisitor {} + + partial class FormattingVisitor + { + readonly CSharpFormatter formatter; + readonly FormattingChanges changes; + readonly IDocument document; + readonly CancellationToken token; + + Indent curIndent; + + public bool HadErrors { + get; + set; + } + + + CSharpFormattingOptions policy { + get { + return formatter.Policy; + } + } + + TextEditorOptions options { + get { + return formatter.TextEditorOptions; + } + } + + FormattingChanges.TextReplaceAction AddChange(int offset, int removedChars, string insertedText) + { + return changes.AddChange(offset, removedChars, insertedText); + } + + public FormattingVisitor(CSharpFormatter formatter, IDocument document, FormattingChanges changes, CancellationToken token) + { + if (formatter == null) + throw new ArgumentNullException("formatter"); + if (document == null) + throw new ArgumentNullException("document"); + if (changes == null) + throw new ArgumentNullException("changes"); + + this.formatter = formatter; + this.changes = changes; + this.document = document; + this.token = token; + + curIndent = new Indent(formatter.TextEditorOptions); + } + + void VisitChildrenToFormat (AstNode parent, Action callback) + { + AstNode next; + for (var child = parent.FirstChild; child != null; child = next) { + token.ThrowIfCancellationRequested(); + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.GetNextSibling(NoWhitespacePredicate); + + if (formatter.FormattingRegions.Count > 0) { + if (formatter.FormattingRegions.Any(r => r.IsInside(child.StartLocation) || r.IsInside(child.EndLocation))) { + callback(child); + } else { + var childRegion = child.Region; + if (formatter.FormattingRegions.Any(r => childRegion.IsInside(r.Begin) || childRegion.IsInside(r.End))) + callback(child); + } + if (child.StartLocation > formatter.lastFormattingLocation) + break; + } else { + callback(child); + } + } + } + + protected override void VisitChildren (AstNode node) + { + VisitChildrenToFormat (node, n => n.AcceptVisitor (this)); + } + + #region NewLines + + void AdjustNewLineBlock(AstNode startNode, int targetMinimumNewLineCount) + { + var indentString = policy.EmptyLineFormatting == EmptyLineFormatting.Indent ? curIndent.IndentString : ""; + + TextLocation newLineInsertPosition = startNode.EndLocation; + var node = startNode.NextSibling; + int currentNewLineCount = 0; + // Check the existing newlines + for (; currentNewLineCount < targetMinimumNewLineCount; node = node.NextSibling) { + if (node is WhitespaceNode) + continue; + if (!(node is NewLineNode)) + break; + newLineInsertPosition = node.EndLocation; + currentNewLineCount++; + if (policy.EmptyLineFormatting == EmptyLineFormatting.DoNotChange) { + if (node.NextSibling == null) + // end of file/block etc, nothing more to do but break before assigning null to node + break; + continue; + } + var isBlankLine = IsSpacing(document.GetLineByNumber(node.StartLocation.Line)); + if (!isBlankLine) { + // remove EOL whitespace if appropriate + if (policy.RemoveEndOfLineWhiteSpace) { + var offset = document.GetOffset(node.StartLocation); + var start = SearchWhitespaceStart(offset); + if (start != offset) + AddChange(start, offset - start, null); + } + } else { + var actualIndent = GetIndentation(node.StartLocation.Line); + if (actualIndent != indentString) { + var start = document.GetOffset(new TextLocation(node.StartLocation.Line, 0)); + AddChange(start, actualIndent.Length, indentString); + } + } + if (node.NextSibling == null) + // end of file/block etc, nothing more to do but break before assigning null to node + break; + } + if (currentNewLineCount < targetMinimumNewLineCount) { + // We need to add more newlines + var builder = new StringBuilder(); + for (; currentNewLineCount < targetMinimumNewLineCount; currentNewLineCount++) { + if (currentNewLineCount > 0) + // Don't indent the first line in the block since that is not an empty line. + builder.Append(indentString); + builder.Append(options.EolMarker); + } + var offset = document.GetOffset(newLineInsertPosition); + if (offset >= 0) + AddChange(offset, 0, builder.ToString()); + } else if (currentNewLineCount == targetMinimumNewLineCount && node is NewLineNode){ +// // Check to see if there are any newlines to remove +// var endNode = node.GetNextSibling(n => !(n is NewLineNode || n is WhitespaceNode)); +// if (endNode != null) { +// var startOffset = document.GetOffset(newLineInsertPosition); +// var endOffset = document.GetOffset(new TextLocation(endNode.StartLocation.Line, 0)); +// EnsureText(startOffset, endOffset, null); +// } + } + } + + public void EnsureMinimumNewLinesAfter(AstNode node, int blankLines) + { + if (node is PreProcessorDirective) { + var directive = (PreProcessorDirective)node; + if (directive.Type == PreProcessorDirectiveType.Pragma) + return; + } + if (blankLines < 0) + return; + if (formatter.FormattingMode != FormattingMode.Intrusive) + blankLines = Math.Min(1, blankLines); + AdjustNewLineBlock(node, blankLines); + } + + public void EnsureMinimumBlankLinesBefore(AstNode node, int blankLines) + { + if (formatter.FormattingMode != FormattingMode.Intrusive) + return; + var loc = node.StartLocation; + int line = loc.Line; + do { + line--; + } while (line > 0 && IsSpacing(document.GetLineByNumber(line))); + if (line > 0 && !IsSpacing(document.GetLineByNumber(line))) + line++; + + if (loc.Line - line >= blankLines) + return; + + var sb = new StringBuilder (); + for (int i = 0; i < blankLines; i++) + sb.Append(options.EolMarker); + int end = document.GetOffset(loc.Line, 1); + if (loc.Line == line) { + AddChange(end, 0, sb.ToString()); + return; + } + if (line + 1 > document.LineCount) + return; + int start = document.GetOffset(line + 1, 1); + if (end - start <= 0 && sb.Length == 0) + return; + AddChange(start, end - start, sb.ToString()); + } + + #endregion + + bool IsSimpleAccessor(Accessor accessor) + { + if (accessor.IsNull || accessor.Body.IsNull || accessor.Body.FirstChild == null) { + return true; + } + var firstStatement = accessor.Body.Statements.FirstOrDefault(); + if (firstStatement == null) + return true; + + if (!(firstStatement is ReturnStatement || firstStatement is ExpressionStatement|| firstStatement is EmptyStatement || firstStatement is ThrowStatement)) + return false; + + if (firstStatement.GetNextSibling(s => s.Role == BlockStatement.StatementRole) != null) + return false; + + return !(accessor.Body.Statements.FirstOrDefault() is BlockStatement); + } + + static bool IsSpacing(char ch) + { + return ch == ' ' || ch == '\t'; + } + + bool IsSpacing(ISegment segment) + { + int endOffset = segment.EndOffset; + for (int i = segment.Offset; i < endOffset; i++) { + if (!IsSpacing(document.GetCharAt(i))) { + return false; + } + } + return true; + } + + int SearchLastNonWsChar(int startOffset, int endOffset) + { + startOffset = Math.Max(0, startOffset); + endOffset = Math.Max(startOffset, endOffset); + if (startOffset >= endOffset) { + return startOffset; + } + int result = -1; + bool inComment = false; + + for (int i = startOffset; i < endOffset && i < document.TextLength; i++) { + char ch = document.GetCharAt(i); + if (IsSpacing(ch)) { + continue; + } + if (ch == '/' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '/') { + return result; + } + if (ch == '/' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '*') { + inComment = true; + i++; + continue; + } + if (inComment && ch == '*' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '/') { + inComment = false; + i++; + continue; + } + if (!inComment) { + result = i; + } + } + return result; + } + + void ForceSpace(int startOffset, int endOffset, bool forceSpace) + { + int lastNonWs = SearchLastNonWsChar(startOffset, endOffset); + if (lastNonWs < 0) + return; + + var spaceCount = Math.Max(0, endOffset - lastNonWs - 1); + if (forceSpace) { + if (spaceCount != 1) { + // Here we could technically remove spaceCount - 1 chars instead + // and skip replacing that with new space, but we want to trigger the + // overlap detection if this space is changed again for some reason + AddChange(lastNonWs + 1, spaceCount, " "); + } + } else if (spaceCount > 0 && !forceSpace) { + AddChange(lastNonWs + 1, spaceCount, ""); + } + } + + void ForceSpacesAfter(AstNode n, bool forceSpaces) + { + if (n == null) { + return; + } + TextLocation location = n.EndLocation; + int offset = document.GetOffset(location); + if (location.Column > document.GetLineByNumber(location.Line).Length) { + return; + } + int i = offset; + while (i < document.TextLength && IsSpacing (document.GetCharAt (i))) { + i++; + } + ForceSpace(offset - 1, i, forceSpaces); + } + + int ForceSpacesBefore(AstNode n, bool forceSpaces) + { + if (n == null || n.IsNull) { + return 0; + } + TextLocation location = n.StartLocation; + // respect manual line breaks. + if (location.Column <= 1 || GetIndentation(location.Line).Length == location.Column - 1) { + return 0; + } + + int offset = document.GetOffset(location); + int i = offset - 1; + while (i >= 0 && IsSpacing (document.GetCharAt (i))) { + i--; + } + ForceSpace(i, offset, forceSpaces); + return i; + } + + int ForceSpacesBeforeRemoveNewLines(AstNode n, bool forceSpace = true) + { + if (n == null || n.IsNull) { + return 0; + } + int offset = document.GetOffset(n.StartLocation); + int i = offset - 1; + while (i >= 0) { + char ch = document.GetCharAt(i); + if (!IsSpacing(ch) && ch != '\r' && ch != '\n') + break; + i--; + } + var length = Math.Max(0, (offset - 1) - i); + AddChange(i + 1, length, forceSpace ? " " : ""); + return i; + } + + internal static bool NoWhitespacePredicate(AstNode arg) + { + return !(arg is NewLineNode || arg is WhitespaceNode); + } + + static bool IsMember(AstNode nextSibling) + { + return nextSibling != null && nextSibling.NodeType == NodeType.Member; + } + + static bool ShouldBreakLine(NewLinePlacement placement, CSharpTokenNode token) + { + if (placement == NewLinePlacement.NewLine) + return true; + if (placement == NewLinePlacement.SameLine) + return false; + if (token.IsNull) + return false; + var prevMeaningfulNode = token.GetPrevNode (n =>n.Role !=Roles.NewLine && n.Role != Roles.Whitespace && n.Role !=Roles.Comment); + return prevMeaningfulNode.EndLocation.Line != token.StartLocation.Line; + } + + void ForceSpaceBefore(AstNode node, bool forceSpace) + { + var offset = document.GetOffset(node.StartLocation); + int end = offset; + // ForceSpace inserts a space one char after start in the case of a missing space + // Therefore, make sure that start < offset by starting at offset - 1 + int start = SearchWhitespaceStart(offset - 1); + ForceSpace(start, end, forceSpace); + } + + public void FixSemicolon(CSharpTokenNode semicolon) + { + if (semicolon.IsNull) + return; + int endOffset = document.GetOffset(semicolon.StartLocation); + int offset = endOffset; + while (offset - 1 > 0 && char.IsWhiteSpace (document.GetCharAt (offset - 1))) { + offset--; + } + if (policy.SpaceBeforeSemicolon) { + AddChange(offset, endOffset - offset, " "); + } else { + if (offset < endOffset) + AddChange(offset, endOffset - offset, null); + } + } + + void PlaceOnNewLine(NewLinePlacement newLine, AstNode keywordNode) + { + if (keywordNode == null || keywordNode.StartLocation.IsEmpty) + return; + + var prev = keywordNode.GetPrevNode (NoWhitespacePredicate); + if (prev is Comment || prev is PreProcessorDirective) + return; + + if (newLine == NewLinePlacement.DoNotCare) + newLine = prev.EndLocation.Line == keywordNode.StartLocation.Line ? NewLinePlacement.SameLine : NewLinePlacement.NewLine; + + int offset = document.GetOffset(keywordNode.StartLocation); + + int whitespaceStart = SearchWhitespaceStart(offset); + string indentString = newLine == NewLinePlacement.NewLine ? options.EolMarker + curIndent.IndentString : " "; + AddChange(whitespaceStart, offset - whitespaceStart, indentString); + } + + string nextStatementIndent; + + 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"); + Console.WriteLine(Environment.StackTrace); + return; + } + bool isEmpty = IsLineIsEmptyUpToEol(offset); + int lineStart = SearchWhitespaceLineStart(offset); + string indentString = nextStatementIndent ?? (isEmpty ? "" : options.EolMarker) + curIndent.IndentString; + nextStatementIndent = null; + EnsureText(lineStart, offset, indentString); + } + + void FixIndentation (AstNode node) + { + FixIndentation(node.StartLocation, 0); + } + + void FixIndentation(TextLocation location, int relOffset) + { + if (location.Line < 1 || location.Line > document.LineCount) { + Console.WriteLine("Invalid location " + location); + Console.WriteLine(Environment.StackTrace); + return; + } + + string lineIndent = GetIndentation(location.Line); + string indentString = curIndent.IndentString; + if (indentString != lineIndent && location.Column - 1 + relOffset == lineIndent.Length) { + AddChange(document.GetOffset(location.Line, 1), lineIndent.Length, indentString); + } + } + + void FixIndentationForceNewLine(AstNode node) + { + var directive = node as PreProcessorDirective; + if (node.GetPrevNode () is NewLineNode) { + if (directive != null && !policy.IndentPreprocessorDirectives) { + var startNode = node.GetPrevNode (); + var startOffset = document.GetOffset(startNode.EndLocation); + int endOffset = document.GetOffset(node.StartLocation); + AddChange(startOffset, endOffset - startOffset, ""); + return; + } else { + FixIndentation(node); + } + } else { + // if no new line preceeds an #endif directive it's excluded + if (directive != null) { + if (directive.Type == PreProcessorDirectiveType.Endif) + return; + } + var startNode = node.GetPrevSibling(n => !(n is WhitespaceNode)) ?? node; + var startOffset = document.GetOffset(startNode.EndLocation); + int endOffset = document.GetOffset(node.StartLocation); + if (startOffset >= endOffset) + return; + if (directive != null && !policy.IndentPreprocessorDirectives) { + AddChange(startOffset, endOffset - startOffset, ""); + return; + } + + AddChange(startOffset, endOffset - startOffset, curIndent.IndentString); + } + } + + string GetIndentation(int lineNumber) + { + var line = document.GetLineByNumber(lineNumber); + var b = new StringBuilder (); + int endOffset = line.EndOffset; + for (int i = line.Offset; i < endOffset; i++) { + char c = document.GetCharAt(i); + if (!IsSpacing(c)) { + break; + } + b.Append(c); + } + return b.ToString(); + } + + void EnsureText(int start, int end, string replacementText) + { + var length = end - start; + if (length == 0 && string.IsNullOrEmpty(replacementText)) + return; + if (replacementText == null || replacementText.Length != length) { + AddChange(start, length, replacementText); + return; + } + for (int i = 0; i < length; i++) { + if (document.GetCharAt(start + i) != replacementText[i]) { + AddChange(start, length, replacementText); + break; + } + } + } + + void FixOpenBrace(BraceStyle braceStyle, AstNode lbrace) + { + if (lbrace.IsNull) + return; + switch (braceStyle) { + case BraceStyle.DoNotChange: + return; + + case BraceStyle.BannerStyle: + case BraceStyle.EndOfLine: + var prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + int prevOffset = document.GetOffset(prev.EndLocation); + + if (prev is Comment || prev is PreProcessorDirective) { + int next = document.GetOffset(lbrace.GetNextNode ().StartLocation); + EnsureText(prevOffset, next, ""); + while (prev is Comment || prev is PreProcessorDirective) + prev = prev.GetPrevNode(); + prevOffset = document.GetOffset(prev.EndLocation); + AddChange(prevOffset, 0, " {"); + } else { + int braceOffset2 = document.GetOffset(lbrace.StartLocation); + EnsureText(prevOffset, braceOffset2, " "); + } + break; + case BraceStyle.EndOfLineWithoutSpace: + prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + prevOffset = document.GetOffset(prev.EndLocation); + int braceOffset = document.GetOffset(lbrace.StartLocation); + EnsureText(prevOffset, braceOffset, ""); + break; + + case BraceStyle.NextLine: + prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + prevOffset = document.GetOffset(prev.EndLocation); + braceOffset = document.GetOffset(lbrace.StartLocation); + EnsureText(prevOffset, braceOffset, options.EolMarker + curIndent.IndentString); + break; + case BraceStyle.NextLineShifted: + prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + prevOffset = document.GetOffset(prev.EndLocation); + braceOffset = document.GetOffset(lbrace.StartLocation); + curIndent.Push(IndentType.Block); + EnsureText(prevOffset, braceOffset, options.EolMarker + curIndent.IndentString); + curIndent.Pop(); + break; + case BraceStyle.NextLineShifted2: + prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + prevOffset = document.GetOffset(prev.EndLocation); + braceOffset = document.GetOffset(lbrace.StartLocation); + curIndent.Push(IndentType.Block); + EnsureText(prevOffset, braceOffset, options.EolMarker + curIndent.IndentString); + curIndent.Pop(); + break; + } + } + + void CorrectClosingBrace (AstNode rbrace) + { + if (rbrace.IsNull) + return; + int braceOffset = document.GetOffset(rbrace.StartLocation); + var prevNode = rbrace.GetPrevNode(); + int prevNodeOffset = prevNode != null ? document.GetOffset(prevNode.EndLocation) : 0; + if (prevNode is NewLineNode) { + EnsureText(prevNodeOffset, braceOffset, curIndent.IndentString); + } else { + EnsureText(prevNodeOffset, braceOffset, options.EolMarker + curIndent.IndentString); + } + } + + void FixClosingBrace(BraceStyle braceStyle, AstNode rbrace) + { + if (rbrace.IsNull) + return; + switch (braceStyle) { + case BraceStyle.DoNotChange: + return; + + case BraceStyle.NextLineShifted: + case BraceStyle.BannerStyle: + curIndent.Push(IndentType.Block); + CorrectClosingBrace (rbrace); + curIndent.Pop (); + break; + case BraceStyle.EndOfLineWithoutSpace: + case BraceStyle.EndOfLine: + case BraceStyle.NextLine: + CorrectClosingBrace (rbrace); + break; + + case BraceStyle.NextLineShifted2: + curIndent.Push(IndentType.Block); + CorrectClosingBrace (rbrace); + curIndent.Pop (); + break; + } + + } + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Expressions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Expressions.cs new file mode 100644 index 000000000..582e26fea --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Expressions.cs @@ -0,0 +1,735 @@ +// +// AstFormattingVisitor_Expressions.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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.Linq; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor : DepthFirstAstVisitor + { + public override void VisitComposedType(ComposedType composedType) + { + var spec = composedType.ArraySpecifiers.FirstOrDefault(); + if (spec != null) + ForceSpacesBefore(spec.LBracketToken, policy.SpaceBeforeArrayDeclarationBrackets); + + if (composedType.HasNullableSpecifier) + ForceSpacesBefore(composedType.NullableSpecifierToken, false); + + if (composedType.PointerRank > 0) + foreach (var token in composedType.PointerTokens) + ForceSpacesBefore(token, false); + + base.VisitComposedType(composedType); + } + + public override void VisitAnonymousMethodExpression(AnonymousMethodExpression lambdaExpression) + { + FormatArguments(lambdaExpression); + + if (!lambdaExpression.Body.IsNull) { + var old = curIndent; + this.curIndent = curIndent.GetIndentWithoutSpace (); + FixOpenBrace(policy.AnonymousMethodBraceStyle, lambdaExpression.Body.LBraceToken); + VisitBlockWithoutFixingBraces(lambdaExpression.Body, policy.IndentBlocks); + FixClosingBrace(policy.AnonymousMethodBraceStyle, lambdaExpression.Body.RBraceToken); + curIndent = old; + } + + } + + public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + ForceSpacesAround(assignmentExpression.OperatorToken, policy.SpaceAroundAssignment); + base.VisitAssignmentExpression(assignmentExpression); + } + + public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + bool forceSpaces = false; + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + forceSpaces = policy.SpaceAroundEqualityOperator; + break; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThan: + case BinaryOperatorType.LessThanOrEqual: + forceSpaces = policy.SpaceAroundRelationalOperator; + break; + case BinaryOperatorType.ConditionalAnd: + case BinaryOperatorType.ConditionalOr: + forceSpaces = policy.SpaceAroundLogicalOperator; + break; + case BinaryOperatorType.BitwiseAnd: + case BinaryOperatorType.BitwiseOr: + case BinaryOperatorType.ExclusiveOr: + forceSpaces = policy.SpaceAroundBitwiseOperator; + break; + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + forceSpaces = policy.SpaceAroundAdditiveOperator; + break; + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + forceSpaces = policy.SpaceAroundMultiplicativeOperator; + break; + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + forceSpaces = policy.SpaceAroundShiftOperator; + break; + case BinaryOperatorType.NullCoalescing: + forceSpaces = policy.SpaceAroundNullCoalescingOperator; + break; + } + var opToken = binaryOperatorExpression.OperatorToken; + if (opToken.PrevSibling != null && opToken.PrevSibling.Role != Roles.NewLine) { + ForceSpacesBefore(opToken, forceSpaces); + } else { + ForceSpacesAfter(binaryOperatorExpression.Left, false); + FixIndentation(opToken); + } + ForceSpacesAfter(opToken, opToken.NextSibling != null && opToken.NextSibling.Role != Roles.NewLine && forceSpaces); + + binaryOperatorExpression.Left.AcceptVisitor(this); + // Handle line breaks in binary opeartor expression. + if (binaryOperatorExpression.Left.EndLocation.Line != binaryOperatorExpression.Right.StartLocation.Line) { + if (opToken.StartLocation.Line == binaryOperatorExpression.Right.StartLocation.Line) { + FixStatementIndentation(opToken.StartLocation); + } else { + FixStatementIndentation(binaryOperatorExpression.Right.StartLocation); + } + } + binaryOperatorExpression.Right.AcceptVisitor(this); + } + + public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + ForceSpacesBefore(conditionalExpression.QuestionMarkToken, policy.SpaceBeforeConditionalOperatorCondition); + ForceSpacesAfter(conditionalExpression.QuestionMarkToken, policy.SpaceAfterConditionalOperatorCondition); + ForceSpacesBefore(conditionalExpression.ColonToken, policy.SpaceBeforeConditionalOperatorSeparator); + ForceSpacesAfter(conditionalExpression.ColonToken, policy.SpaceAfterConditionalOperatorSeparator); + base.VisitConditionalExpression(conditionalExpression); + } + + public override void VisitCastExpression(CastExpression castExpression) + { + if (castExpression.RParToken != null) { + ForceSpacesAfter(castExpression.LParToken, policy.SpacesWithinCastParentheses); + ForceSpacesBefore(castExpression.RParToken, policy.SpacesWithinCastParentheses); + + ForceSpacesAfter(castExpression.RParToken, policy.SpaceAfterTypecast); + } + base.VisitCastExpression(castExpression); + } + + void ForceSpacesAround(AstNode node, bool forceSpaces) + { + if (node.IsNull) + return; + ForceSpacesBefore(node, forceSpaces); + ForceSpacesAfter(node, forceSpaces); + } + + void FormatCommas(AstNode parent, bool before, bool after) + { + if (parent.IsNull) { + return; + } + foreach (CSharpTokenNode comma in parent.Children.Where (node => node.Role == Roles.Comma)) { + ForceSpacesAfter(comma, after); + ForceSpacesBefore(comma, before); + } + } + + bool DoWrap(Wrapping wrapping, AstNode wrapNode, int argumentCount) + { + return wrapping == Wrapping.WrapAlways || + options.WrapLineLength > 0 && argumentCount > 1 && wrapping == Wrapping.WrapIfTooLong && wrapNode.StartLocation.Column >= options.WrapLineLength; + } + + void FormatArguments(AstNode node) + { + Wrapping methodCallArgumentWrapping; + NewLinePlacement newLineAferMethodCallOpenParentheses; + bool doAlignToFirstArgument; + NewLinePlacement methodClosingParenthesesOnNewLine; + bool spaceWithinMethodCallParentheses; + bool spaceWithinEmptyParentheses; + bool spaceAfterMethodCallParameterComma; + bool spaceBeforeMethodCallParameterComma; + + CSharpTokenNode rParToken, lParToken; + List arguments; + + var constructorDeclaration = node as ConstructorDeclaration; + if (constructorDeclaration != null) { + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinConstructorDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterConstructorDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeConstructorDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = constructorDeclaration.LParToken; + rParToken = constructorDeclaration.RParToken; + arguments = constructorDeclaration.Parameters.Cast().ToList(); + } else if (node is IndexerDeclaration) { + var indexer = (IndexerDeclaration)node; + methodCallArgumentWrapping = policy.IndexerDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferIndexerDeclarationOpenBracket; + methodClosingParenthesesOnNewLine = policy.IndexerDeclarationClosingBracketOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstIndexerDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinIndexerDeclarationBracket; + spaceAfterMethodCallParameterComma = policy.SpaceAfterIndexerDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeIndexerDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = indexer.LBracketToken; + rParToken = indexer.RBracketToken; + arguments = indexer.Parameters.Cast().ToList(); + } else if (node is OperatorDeclaration) { + var op = (OperatorDeclaration)node; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = op.LParToken; + rParToken = op.RParToken; + arguments = op.Parameters.Cast().ToList(); + } else if (node is MethodDeclaration) { + var methodDeclaration = node as MethodDeclaration; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = methodDeclaration.LParToken; + rParToken = methodDeclaration.RParToken; + arguments = methodDeclaration.Parameters.Cast().ToList(); + } else if (node is IndexerExpression) { + var indexer = (IndexerExpression)node; + methodCallArgumentWrapping = policy.IndexerArgumentWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferIndexerOpenBracket; + doAlignToFirstArgument = policy.AlignToFirstIndexerArgument; + methodClosingParenthesesOnNewLine = policy.IndexerClosingBracketOnNewLine; + spaceWithinMethodCallParentheses = policy.SpacesWithinBrackets; + spaceAfterMethodCallParameterComma = policy.SpaceAfterBracketComma; + spaceWithinEmptyParentheses = spaceWithinMethodCallParentheses; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeBracketComma; + rParToken = indexer.RBracketToken; + lParToken = indexer.LBracketToken; + arguments = indexer.Arguments.Cast().ToList(); + } else if (node is ObjectCreateExpression) { + var oce = node as ObjectCreateExpression; + methodCallArgumentWrapping = policy.MethodCallArgumentWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodCallOpenParentheses; + doAlignToFirstArgument = policy.AlignToFirstMethodCallArgument; + methodClosingParenthesesOnNewLine = policy.MethodCallClosingParenthesesOnNewLine; + spaceWithinMethodCallParentheses = policy.SpacesWithinNewParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterNewParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeNewParameterComma; + spaceWithinEmptyParentheses = policy.SpacesBetweenEmptyNewParentheses; + + rParToken = oce.RParToken; + lParToken = oce.LParToken; + arguments = oce.Arguments.Cast().ToList(); + } else if (node is Attribute) { + var oce = node as Attribute; + methodCallArgumentWrapping = policy.MethodCallArgumentWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodCallOpenParentheses; + doAlignToFirstArgument = policy.AlignToFirstMethodCallArgument; + methodClosingParenthesesOnNewLine = policy.MethodCallClosingParenthesesOnNewLine; + spaceWithinMethodCallParentheses = policy.SpacesWithinNewParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterNewParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeNewParameterComma; + spaceWithinEmptyParentheses = policy.SpacesBetweenEmptyNewParentheses; + + rParToken = oce.RParToken; + lParToken = oce.LParToken; + arguments = oce.Arguments.Cast().ToList(); + } else if (node is LambdaExpression) { + var methodDeclaration = node as LambdaExpression; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = methodDeclaration.LParToken; + rParToken = methodDeclaration.RParToken; + arguments = methodDeclaration.Parameters.Cast().ToList(); + } else if (node is AnonymousMethodExpression) { + var methodDeclaration = node as AnonymousMethodExpression; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = methodDeclaration.LParToken; + rParToken = methodDeclaration.RParToken; + arguments = methodDeclaration.Parameters.Cast().ToList(); + } else if (node is ConstructorInitializer) { + var constructorInitializer = node as ConstructorInitializer; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = constructorInitializer.LParToken; + rParToken = constructorInitializer.RParToken; + arguments = constructorInitializer.Arguments.Cast().ToList(); + } else { + InvocationExpression invocationExpression = node as InvocationExpression; + methodCallArgumentWrapping = policy.MethodCallArgumentWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodCallOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodCallClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodCallArgument; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodCallParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodCallParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodCallParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodCallParentheses; + + rParToken = invocationExpression.RParToken; + lParToken = invocationExpression.LParToken; + arguments = invocationExpression.Arguments.Cast().ToList(); + } + + if (formatter.FormattingMode == ICSharpCode.NRefactory.CSharp.FormattingMode.OnTheFly) + methodCallArgumentWrapping = Wrapping.DoNotChange; + int argumentStart = 1; + var firstarg = arguments.FirstOrDefault(); + if (firstarg != null && firstarg.GetPrevNode().Role == Roles.NewLine) { + doAlignToFirstArgument = false; + argumentStart = 0; + } + bool wrapMethodCall = DoWrap(methodCallArgumentWrapping, rParToken, arguments.Count); + if (wrapMethodCall && arguments.Any()) { + if (ShouldBreakLine(newLineAferMethodCallOpenParentheses, lParToken)) { + curIndent.Push(IndentType.Continuation); + foreach (var arg in arguments) { + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + } + curIndent.Pop(); + } else { + if (!doAlignToFirstArgument) { + curIndent.Push(IndentType.Continuation); + foreach (var arg in arguments.Take (argumentStart)) { + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + } + foreach (var arg in arguments.Skip (argumentStart)) { + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + } + curIndent.Pop(); + } else { + int extraSpaces = Math.Max(0, arguments.First().StartLocation.Column - 1 - curIndent.IndentString.Length); + curIndent.ExtraSpaces += extraSpaces; + foreach (var arg in arguments.Take (argumentStart)) { + arg.AcceptVisitor(this); + } + foreach (var arg in arguments.Skip(argumentStart)) { + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + } + curIndent.ExtraSpaces -= extraSpaces; + } + } + + if (!rParToken.IsNull) { + if (ShouldBreakLine(methodClosingParenthesesOnNewLine, rParToken)) { + FixStatementIndentation(rParToken.StartLocation); + } else if (methodClosingParenthesesOnNewLine == NewLinePlacement.SameLine) { + ForceSpacesBeforeRemoveNewLines(rParToken, spaceWithinMethodCallParentheses); + } + } + } else { + + foreach (var arg in arguments.Take (argumentStart)) { + if (policy.IndentBlocksInsideExpressions) + curIndent.Push(IndentType.Continuation); + arg.AcceptVisitor(this); + if (policy.IndentBlocksInsideExpressions) + curIndent.Pop(); + } + foreach (var arg in arguments.Skip(argumentStart)) { + if (arg.GetPrevSibling(NoWhitespacePredicate) != null) { + if (methodCallArgumentWrapping == Wrapping.DoNotWrap) { + ForceSpacesBeforeRemoveNewLines(arg, spaceAfterMethodCallParameterComma && arg.GetPrevSibling(NoWhitespacePredicate).Role == Roles.Comma); + if (policy.IndentBlocksInsideExpressions) + curIndent.Push(IndentType.Continuation); + arg.AcceptVisitor(this); + if (policy.IndentBlocksInsideExpressions) + curIndent.Pop(); + } else { + if (!doAlignToFirstArgument && arg.PrevSibling.Role == Roles.NewLine) { + curIndent.Push(IndentType.Continuation); + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + curIndent.Pop(); + } else { + if (arg.PrevSibling.StartLocation.Line == arg.StartLocation.Line) { + ForceSpacesBefore(arg, spaceAfterMethodCallParameterComma && arg.GetPrevSibling(NoWhitespacePredicate).Role == Roles.Comma); + if (policy.IndentBlocksInsideExpressions) + curIndent.Push(IndentType.Continuation); + arg.AcceptVisitor(this); + if (policy.IndentBlocksInsideExpressions) + curIndent.Pop(); + } else { + int extraSpaces = Math.Max(0, arguments.First().StartLocation.Column - 1 - curIndent.IndentString.Length); + curIndent.ExtraSpaces += extraSpaces; + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + curIndent.ExtraSpaces -= extraSpaces; + } + } + } + } else { + arg.AcceptVisitor(this); + } + } + if (!rParToken.IsNull) { + if (methodCallArgumentWrapping == Wrapping.DoNotWrap) { + ForceSpacesBeforeRemoveNewLines(rParToken, arguments.Any() ? spaceWithinMethodCallParentheses : spaceWithinEmptyParentheses); + } else { + bool sameLine = rParToken.GetPrevNode(n => n.Role == Roles.Argument || n.Role == Roles.Parameter || n.Role == Roles.LPar || n.Role == Roles.Comma).EndLocation.Line == rParToken.StartLocation.Line; + if (sameLine) { + ForceSpacesBeforeRemoveNewLines(rParToken, arguments.Any() ? spaceWithinMethodCallParentheses : spaceWithinEmptyParentheses); + } else { + FixStatementIndentation(rParToken.StartLocation); + } + } + } + } + if (!rParToken.IsNull) { + foreach (CSharpTokenNode comma in rParToken.Parent.Children.Where(n => n.Role == Roles.Comma)) { + ForceSpacesBefore(comma, spaceBeforeMethodCallParameterComma); + } + } + } + + public override void VisitInvocationExpression(InvocationExpression invocationExpression) + { + if (!invocationExpression.Target.IsNull) + invocationExpression.Target.AcceptVisitor(this); + + ForceSpacesBefore(invocationExpression.LParToken, policy.SpaceBeforeMethodCallParentheses); + if (invocationExpression.Arguments.Any()) { + ForceSpacesAfter(invocationExpression.LParToken, policy.SpaceWithinMethodCallParentheses); + } else { + ForceSpacesAfter(invocationExpression.LParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + ForceSpacesBefore(invocationExpression.RParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + } + bool popIndent = false; + if (invocationExpression.Target is MemberReferenceExpression) { + var mt = (MemberReferenceExpression)invocationExpression.Target; + if (mt.Target is InvocationExpression) { + if (DoWrap(policy.ChainedMethodCallWrapping, mt.DotToken, 2)) { + curIndent.Push(IndentType.Block); + popIndent = true; + FixStatementIndentation(mt.DotToken.StartLocation); + } else { + if (policy.ChainedMethodCallWrapping == Wrapping.DoNotWrap) + ForceSpacesBeforeRemoveNewLines(mt.DotToken, false); + } + } + } + FormatArguments(invocationExpression); + if (popIndent) + curIndent.Pop(); + } + + public override void VisitIndexerExpression(IndexerExpression indexerExpression) + { + ForceSpacesBeforeRemoveNewLines(indexerExpression.LBracketToken, policy.SpacesBeforeBrackets); + ForceSpacesAfter(indexerExpression.LBracketToken, policy.SpacesWithinBrackets); + + if (!indexerExpression.Target.IsNull) + indexerExpression.Target.AcceptVisitor(this); + + FormatArguments(indexerExpression); + + + + } + + public override void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + var lp = parenthesizedExpression.LParToken; + var expr = parenthesizedExpression.Expression; + int extraSpaces = 0; + if (lp.StartLocation.Line == expr.StartLocation.Line) { + ForceSpacesAfter(lp, policy.SpacesWithinParentheses); + } else { + extraSpaces += options.IndentSize; + curIndent.ExtraSpaces += extraSpaces; + FixIndentation(expr); + } + + base.VisitParenthesizedExpression(parenthesizedExpression); + + var rp = parenthesizedExpression.RParToken; + + curIndent.ExtraSpaces -= extraSpaces; + if (rp.StartLocation.Line == expr.EndLocation.Line) { + ForceSpacesBefore(rp, policy.SpacesWithinParentheses); + } else { + FixIndentation(rp); + } + } + + public override void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + ForceSpacesBeforeRemoveNewLines(sizeOfExpression.LParToken, policy.SpaceBeforeSizeOfParentheses); + ForceSpacesAfter(sizeOfExpression.LParToken, policy.SpacesWithinSizeOfParentheses); + ForceSpacesBeforeRemoveNewLines(sizeOfExpression.RParToken, policy.SpacesWithinSizeOfParentheses); + base.VisitSizeOfExpression(sizeOfExpression); + } + + public override void VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + ForceSpacesBeforeRemoveNewLines(typeOfExpression.LParToken, policy.SpaceBeforeTypeOfParentheses); + ForceSpacesAfter(typeOfExpression.LParToken, policy.SpacesWithinTypeOfParentheses); + ForceSpacesBeforeRemoveNewLines(typeOfExpression.RParToken, policy.SpacesWithinTypeOfParentheses); + base.VisitTypeOfExpression(typeOfExpression); + } + + public override void VisitCheckedExpression(CheckedExpression checkedExpression) + { + ForceSpacesAfter(checkedExpression.LParToken, policy.SpacesWithinCheckedExpressionParantheses); + ForceSpacesBeforeRemoveNewLines(checkedExpression.RParToken, policy.SpacesWithinCheckedExpressionParantheses); + base.VisitCheckedExpression(checkedExpression); + } + + public override void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + ForceSpacesAfter(uncheckedExpression.LParToken, policy.SpacesWithinCheckedExpressionParantheses); + ForceSpacesBeforeRemoveNewLines(uncheckedExpression.RParToken, policy.SpacesWithinCheckedExpressionParantheses); + base.VisitUncheckedExpression(uncheckedExpression); + } + + public override void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + ForceSpacesBeforeRemoveNewLines(objectCreateExpression.LParToken, policy.SpaceBeforeNewParentheses); + + if (objectCreateExpression.Arguments.Any()) { + if (!objectCreateExpression.LParToken.IsNull) + ForceSpacesAfter(objectCreateExpression.LParToken, policy.SpacesWithinNewParentheses); + } else { + if (!objectCreateExpression.LParToken.IsNull) + ForceSpacesAfter(objectCreateExpression.LParToken, policy.SpacesBetweenEmptyNewParentheses); + } + + if (!objectCreateExpression.Type.IsNull) + objectCreateExpression.Type.AcceptVisitor(this); + objectCreateExpression.Initializer.AcceptVisitor(this); + FormatArguments(objectCreateExpression); + } + + public override void VisitArrayCreateExpression(ArrayCreateExpression arrayObjectCreateExpression) + { + FormatCommas(arrayObjectCreateExpression, policy.SpaceBeforeMethodCallParameterComma, policy.SpaceAfterMethodCallParameterComma); + base.VisitArrayCreateExpression(arrayObjectCreateExpression); + } + + public override void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + var old = curIndent; + curIndent = curIndent.Clone(); + curIndent.ExtraSpaces = 0; + + if (DoWrap(policy.ArrayInitializerWrapping, arrayInitializerExpression.RBraceToken, arrayInitializerExpression.Elements.Count)) { + FixOpenBrace(policy.ArrayInitializerBraceStyle, arrayInitializerExpression.LBraceToken); + curIndent.Push(IndentType.Block); + foreach (var init in arrayInitializerExpression.Elements) { + FixStatementIndentation(init.StartLocation); + init.AcceptVisitor(this); + } + curIndent.Pop(); + FixClosingBrace(policy.ArrayInitializerBraceStyle, arrayInitializerExpression.RBraceToken); + } else if (policy.ArrayInitializerWrapping == Wrapping.DoNotWrap) { + ForceSpacesBeforeRemoveNewLines(arrayInitializerExpression.LBraceToken); + foreach (var init in arrayInitializerExpression.Elements) { + ForceSpacesBeforeRemoveNewLines(init); + init.AcceptVisitor(this); + } + ForceSpacesBeforeRemoveNewLines(arrayInitializerExpression.RBraceToken); + } else { + var lBrace = arrayInitializerExpression.LBraceToken; + var rBrace = arrayInitializerExpression.RBraceToken; + + foreach (var child in arrayInitializerExpression.Children) { + if (child.Role == Roles.LBrace) { + if (lBrace.StartLocation.Line == rBrace.StartLocation.Line && policy.AllowOneLinedArrayInitialziers) { + ForceSpacesAfter(child, true); + } else { + FixOpenBrace(policy.ArrayInitializerBraceStyle, child); + } + curIndent.Push(IndentType.Block); + continue; + } + if (child.Role == Roles.RBrace) { + curIndent.Pop(); + if (lBrace.StartLocation.Line == rBrace.StartLocation.Line && policy.AllowOneLinedArrayInitialziers) { + ForceSpaceBefore(child, true); + + } else { + FixClosingBrace(policy.ArrayInitializerBraceStyle, child); + } + continue; + } + if (child.Role == Roles.Expression) { + if (child.PrevSibling != null) { + if (child.PrevSibling.Role == Roles.NewLine) { + FixIndentation(child); + } + if (child.PrevSibling.Role == Roles.Comma) { + ForceSpaceBefore(child, true); + } + } + child.AcceptVisitor(this); + if (child.NextSibling != null && child.NextSibling.Role == Roles.Comma) + ForceSpacesAfter(child, false); + continue; + } + + child.AcceptVisitor(this); + } + } + curIndent = old; + } + + public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + var assignToken = parameterDeclaration.AssignToken; + if (!assignToken.IsNull) + ForceSpacesAround(assignToken, policy.SpaceAroundAssignment); + base.VisitParameterDeclaration(parameterDeclaration); + } + + public override void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + FormatArguments(lambdaExpression); + ForceSpacesBeforeRemoveNewLines(lambdaExpression.ArrowToken, true); + + if (!lambdaExpression.Body.IsNull) { + var body = lambdaExpression.Body as BlockStatement; + if (body != null) { + var old = curIndent; + this.curIndent = curIndent.GetIndentWithoutSpace (); + FixOpenBrace(policy.AnonymousMethodBraceStyle, body.LBraceToken); + VisitBlockWithoutFixingBraces(body, policy.IndentMethodBody); + FixClosingBrace(policy.AnonymousMethodBraceStyle, body.RBraceToken); + curIndent = old; + } else { + ForceSpacesAfter(lambdaExpression.ArrowToken, true); + lambdaExpression.Body.AcceptVisitor(this); + } + } + } + + public override void VisitNamedExpression(NamedExpression namedExpression) + { + ForceSpacesAround(namedExpression.AssignToken, policy.SpaceAroundAssignment); + base.VisitNamedExpression(namedExpression); + } + + public override void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + ForceSpacesAfter(namedArgumentExpression.ColonToken, policy.SpaceInNamedArgumentAfterDoubleColon); + + base.VisitNamedArgumentExpression(namedArgumentExpression); + } + + public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + var dot = memberReferenceExpression.DotToken; + if (dot.PrevSibling.EndLocation.Line == dot.StartLocation.Line) + ForceSpacesBefore(dot, false); + ForceSpacesAfter(dot, false); + base.VisitMemberReferenceExpression(memberReferenceExpression); + } + + public override void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + ForceSpacesAround(pointerReferenceExpression.ArrowToken, policy.SpaceAroundUnsafeArrowOperator); + base.VisitPointerReferenceExpression(pointerReferenceExpression); + } + + public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + base.VisitUnaryOperatorExpression(unaryOperatorExpression); + switch (unaryOperatorExpression.Operator) { + case UnaryOperatorType.Any: + break; + case UnaryOperatorType.Not: + case UnaryOperatorType.BitNot: + case UnaryOperatorType.Minus: + case UnaryOperatorType.Plus: + case UnaryOperatorType.Increment: + case UnaryOperatorType.Decrement: + ForceSpacesBeforeRemoveNewLines(unaryOperatorExpression.Expression, false); + break; + case UnaryOperatorType.PostIncrement: + case UnaryOperatorType.PostDecrement: + ForceSpacesBeforeRemoveNewLines(unaryOperatorExpression.OperatorToken, false); + break; + case UnaryOperatorType.Dereference: + ForceSpacesAfter(unaryOperatorExpression.OperatorToken, policy.SpaceAfterUnsafeAsteriskOfOperator); + break; + case UnaryOperatorType.AddressOf: + ForceSpacesAfter(unaryOperatorExpression.OperatorToken, policy.SpaceAfterUnsafeAddressOfOperator); + break; + case UnaryOperatorType.Await: + ForceSpacesBeforeRemoveNewLines(unaryOperatorExpression.Expression, true); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Global.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Global.cs new file mode 100644 index 000000000..b91cbdd9a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Global.cs @@ -0,0 +1,351 @@ +// +// AstFormattingVisitor_Global.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor : DepthFirstAstVisitor + { + int GetGlobalNewLinesFor(AstNode child) + { + if (child.NextSibling == null) + // last node in the document => no extra newlines + return 0; + if (child.NextSibling.Role == Roles.RBrace) + // Last node in a block => no extra newlines, it's handled later by FixClosingBrace() + return 0; + + int newLines = 1; + var nextSibling = child.GetNextSibling(NoWhitespacePredicate); + if (nextSibling is PreProcessorDirective) { + var directive = (PreProcessorDirective)nextSibling; + if (directive.Type == PreProcessorDirectiveType.Endif) + return -1; + if (directive.Type == PreProcessorDirectiveType.Undef) + return -1; + } + if ((child is UsingDeclaration || child is UsingAliasDeclaration) && !(nextSibling is UsingDeclaration || nextSibling is UsingAliasDeclaration)) { + newLines += policy.MinimumBlankLinesAfterUsings; + } else if ((child is TypeDeclaration) && (nextSibling is TypeDeclaration)) { + newLines += policy.MinimumBlankLinesBetweenTypes; + } + + return newLines; + } + + public override void VisitSyntaxTree(SyntaxTree unit) + { + bool first = true; + VisitChildrenToFormat(unit, child => { + if (first && (child is UsingDeclaration || child is UsingAliasDeclaration)) { + EnsureMinimumBlankLinesBefore(child, policy.MinimumBlankLinesBeforeUsings); + first = false; + } + if (NoWhitespacePredicate(child)) + FixIndentation(child); + child.AcceptVisitor(this); + if (NoWhitespacePredicate(child) && !first) + EnsureMinimumNewLinesAfter(child, GetGlobalNewLinesFor(child)); + }); + } + + public override void VisitAttributeSection(AttributeSection attributeSection) + { + VisitChildrenToFormat(attributeSection, child => { + child.AcceptVisitor(this); + if (child.NextSibling != null && child.NextSibling.Role == Roles.RBracket) { + ForceSpacesAfter(child, false); + } + }); + } + + public override void VisitAttribute(Attribute attribute) + { + if (attribute.HasArgumentList) { + ForceSpacesBefore(attribute.LParToken, policy.SpaceBeforeMethodCallParentheses); + if (attribute.Arguments.Any()) { + ForceSpacesAfter(attribute.LParToken, policy.SpaceWithinMethodCallParentheses); + } else { + ForceSpacesAfter(attribute.LParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + ForceSpacesBefore(attribute.RParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + } + FormatArguments(attribute); + } + } + + public override void VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + ForceSpacesAfter(usingDeclaration.UsingToken, true); + FixSemicolon(usingDeclaration.SemicolonToken); + } + + public override void VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) + { + ForceSpacesAfter(usingDeclaration.UsingToken, true); + ForceSpacesAround(usingDeclaration.AssignToken, policy.SpaceAroundAssignment); + FixSemicolon(usingDeclaration.SemicolonToken); + } + + public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + FixOpenBrace(policy.NamespaceBraceStyle, namespaceDeclaration.LBraceToken); + if (policy.IndentNamespaceBody) + curIndent.Push(IndentType.Block); + + bool first = true; + bool startFormat = false; + VisitChildrenToFormat(namespaceDeclaration, child => { + if (first) { + startFormat = child.StartLocation > namespaceDeclaration.LBraceToken.StartLocation; + } + if (child.Role == Roles.LBrace) { + var next = child.GetNextSibling(NoWhitespacePredicate); + var blankLines = 1; + if (next is UsingDeclaration || next is UsingAliasDeclaration) { + blankLines += policy.MinimumBlankLinesBeforeUsings; + } else { + blankLines += policy.MinimumBlankLinesBeforeFirstDeclaration; + } + EnsureMinimumNewLinesAfter(child, blankLines); + startFormat = true; + return; + } + if (child.Role == Roles.RBrace) { + startFormat = false; + return; + } + if (!startFormat || !NoWhitespacePredicate (child)) + return; + if (first && (child is UsingDeclaration || child is UsingAliasDeclaration)) { + // TODO: policy.BlankLinesBeforeUsings + first = false; + } + if (NoWhitespacePredicate(child)) + FixIndentationForceNewLine(child); + child.AcceptVisitor(this); + if (NoWhitespacePredicate(child)) + EnsureMinimumNewLinesAfter(child, GetGlobalNewLinesFor(child)); + }); + + if (policy.IndentNamespaceBody) + curIndent.Pop(); + + FixClosingBrace(policy.NamespaceBraceStyle, namespaceDeclaration.RBraceToken); + } + + void FixAttributesAndDocComment(EntityDeclaration entity) + { + var node = entity.FirstChild; + while (node != null && node.Role == Roles.Comment) { + node = node.GetNextSibling(NoWhitespacePredicate); + FixIndentation(node); + } + if (entity.Attributes.Count > 0) { + AstNode n = null; + entity.Attributes.First().AcceptVisitor(this); + foreach (var attr in entity.Attributes.Skip (1)) { + FixIndentation(attr); + attr.AcceptVisitor(this); + n = attr; + } + if (n != null) { + FixIndentation(n.GetNextNode(NoWhitespacePredicate)); + } else { + FixIndentation(entity.Attributes.First().GetNextNode(NoWhitespacePredicate)); + } + } + } + + public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + FixAttributesAndDocComment(typeDeclaration); + BraceStyle braceStyle; + bool indentBody = false; + switch (typeDeclaration.ClassType) { + case ClassType.Class: + braceStyle = policy.ClassBraceStyle; + indentBody = policy.IndentClassBody; + break; + case ClassType.Struct: + braceStyle = policy.StructBraceStyle; + indentBody = policy.IndentStructBody; + break; + case ClassType.Interface: + braceStyle = policy.InterfaceBraceStyle; + indentBody = policy.IndentInterfaceBody; + break; + case ClassType.Enum: + braceStyle = policy.EnumBraceStyle; + indentBody = policy.IndentEnumBody; + break; + default: + throw new InvalidOperationException("unsupported class type : " + typeDeclaration.ClassType); + } + + foreach (var constraint in typeDeclaration.Constraints) + constraint.AcceptVisitor (this); + + FixOpenBrace(braceStyle, typeDeclaration.LBraceToken); + + if (indentBody) + curIndent.Push(IndentType.Block); + bool startFormat = true; + bool first = true; + VisitChildrenToFormat(typeDeclaration, child => { + if (first) { + startFormat = child.StartLocation > typeDeclaration.LBraceToken.StartLocation; + first = false; + } + if (child.Role == Roles.LBrace) { + startFormat = true; + if (braceStyle != BraceStyle.DoNotChange) + EnsureMinimumNewLinesAfter(child, GetTypeLevelNewLinesFor(child)); + return; + } + if (child.Role == Roles.RBrace) { + startFormat = false; + return; + } + if (!startFormat || !NoWhitespacePredicate (child)) + return; + if (child.Role == Roles.Comma) { + ForceSpacesBeforeRemoveNewLines (child, false); + EnsureMinimumNewLinesAfter(child, 1); + return; + } + if (NoWhitespacePredicate(child)) + FixIndentationForceNewLine(child); + child.AcceptVisitor(this); + if (NoWhitespacePredicate(child) && child.GetNextSibling (NoWhitespacePredicate).Role != Roles.Comma) + EnsureMinimumNewLinesAfter(child, GetTypeLevelNewLinesFor(child)); + }); + + if (indentBody) + curIndent.Pop(); + + FixClosingBrace(braceStyle, typeDeclaration.RBraceToken); + } + + int GetTypeLevelNewLinesFor(AstNode child) + { + var blankLines = 1; + var nextSibling = child.GetNextSibling(NoWhitespacePredicate); + if (child is PreProcessorDirective) { + var directive = (PreProcessorDirective)child; + if (directive.Type == PreProcessorDirectiveType.Region) { + blankLines += policy.MinimumBlankLinesInsideRegion; + } + if (directive.Type == PreProcessorDirectiveType.Endregion) { + if (child.GetNextSibling(NoWhitespacePredicate) is CSharpTokenNode) + return 1; + blankLines += policy.MinimumBlankLinesAroundRegion; + } + return blankLines; + } + + if (nextSibling is PreProcessorDirective) { + var directive = (PreProcessorDirective)nextSibling; + if (directive.Type == PreProcessorDirectiveType.Region) { + if (child is CSharpTokenNode) + return 1; + blankLines += policy.MinimumBlankLinesAroundRegion; + } + if (directive.Type == PreProcessorDirectiveType.Endregion) + blankLines += policy.MinimumBlankLinesInsideRegion; + if (directive.Type == PreProcessorDirectiveType.Endif) + return -1; + if (directive.Type == PreProcessorDirectiveType.Undef) + return -1; + return blankLines; + } + if (child.Role == Roles.LBrace) + return 1; + if (child is Comment) + return 1; + if (child is EventDeclaration) { + if (nextSibling is EventDeclaration) { + blankLines += policy.MinimumBlankLinesBetweenEventFields; + return blankLines; + } + } + + if (child is FieldDeclaration || child is FixedFieldDeclaration) { + if (nextSibling is FieldDeclaration || nextSibling is FixedFieldDeclaration) { + blankLines += policy.MinimumBlankLinesBetweenFields; + return blankLines; + } + } + + if (child is TypeDeclaration) { + if (nextSibling is TypeDeclaration) { + blankLines += policy.MinimumBlankLinesBetweenTypes; + return blankLines; + } + } + + if (nextSibling.Role == Roles.TypeMemberRole) + blankLines += policy.MinimumBlankLinesBetweenMembers; + return blankLines; + } + + public override void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + ForceSpacesBefore(delegateDeclaration.LParToken, policy.SpaceBeforeDelegateDeclarationParentheses); + if (delegateDeclaration.Parameters.Any()) { + ForceSpacesAfter(delegateDeclaration.LParToken, policy.SpaceWithinDelegateDeclarationParentheses); + ForceSpacesBefore(delegateDeclaration.RParToken, policy.SpaceWithinDelegateDeclarationParentheses); + } else { + ForceSpacesAfter(delegateDeclaration.LParToken, policy.SpaceBetweenEmptyDelegateDeclarationParentheses); + ForceSpacesBefore(delegateDeclaration.RParToken, policy.SpaceBetweenEmptyDelegateDeclarationParentheses); + } + FormatCommas(delegateDeclaration, policy.SpaceBeforeDelegateDeclarationParameterComma, policy.SpaceAfterDelegateDeclarationParameterComma); + + base.VisitDelegateDeclaration(delegateDeclaration); + } + + public override void VisitComment(Comment comment) + { + if (comment.StartsLine && !HadErrors && (!policy.KeepCommentsAtFirstColumn || comment.StartLocation.Column > 1)) + FixIndentation(comment); + } + + public override void VisitConstraint(Constraint constraint) + { + VisitChildrenToFormat (constraint, node => { + if (node is AstType) { + node.AcceptVisitor (this); + } else if (node.Role == Roles.LPar) { + ForceSpacesBefore (node, false); + ForceSpacesAfter (node, false); + } else if (node.Role ==Roles.Comma) { + ForceSpacesBefore (node, false); + ForceSpacesAfter (node, true); + } + }); + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Query.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Query.cs new file mode 100644 index 000000000..bfdc47640 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Query.cs @@ -0,0 +1,124 @@ +// +// AstFormattingVisitor_Query.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor + { + int GetUpdatedStartLocation(QueryExpression queryExpression) + { + //TODO + return queryExpression.StartLocation.Column; + } + + public override void VisitQueryExpression(QueryExpression queryExpression) + { + var oldIndent = curIndent.Clone(); + + var column = GetUpdatedStartLocation(queryExpression); + + int extraSpaces = column - 1 - (curIndent.CurIndent / options.TabSize); + if (extraSpaces < 0) { + //This check should probably be removed in the future, when GetUpdatedStartLocation is implemented + extraSpaces = 0; + } + + curIndent.ExtraSpaces = extraSpaces; + VisitChildren(queryExpression); + + curIndent = oldIndent; + } + + public override void VisitQueryFromClause(QueryFromClause queryFromClause) + { + FixClauseIndentation(queryFromClause, queryFromClause.FromKeyword); + } + + public override void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + VisitChildren(queryContinuationClause); + } + + public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + FixClauseIndentation(queryGroupClause, queryGroupClause.GroupKeyword); + } + + public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + FixClauseIndentation(queryJoinClause, queryJoinClause.JoinKeyword); + } + + public override void VisitQueryLetClause(QueryLetClause queryLetClause) + { + FixClauseIndentation(queryLetClause, queryLetClause.LetKeyword); + } + + public override void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + FixClauseIndentation(querySelectClause, querySelectClause.SelectKeyword); + } + + public override void VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + FixClauseIndentation(queryOrderClause, queryOrderClause.OrderbyToken); + } + + public override void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + FixClauseIndentation(queryWhereClause, queryWhereClause.WhereKeyword); + } + + void FixClauseIndentation(QueryClause clause, AstNode keyword) { + var parentExpression = clause.GetParent(); + bool isFirstClause = parentExpression.Clauses.First() == clause; + if (!isFirstClause) { + PlaceOnNewLine(policy.NewLineBeforeNewQueryClause, keyword); + } + + int extraSpaces = options.IndentSize; + curIndent.ExtraSpaces += extraSpaces; + foreach (var child in clause.Children) { + var expression = child as Expression; + if (expression != null) { + FixIndentation(child); + child.AcceptVisitor(this); + } + + var tokenNode = child as CSharpTokenNode; + if (tokenNode != null) { + if (tokenNode.GetNextSibling(NoWhitespacePredicate).StartLocation.Line != tokenNode.EndLocation.Line) { + ForceSpacesAfter(tokenNode, false); + } + } + } + curIndent.ExtraSpaces -= extraSpaces; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Statements.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Statements.cs new file mode 100644 index 000000000..3c5c92095 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Statements.cs @@ -0,0 +1,517 @@ +// +// AstFormattingVisitor_Statements.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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.Linq; +using ICSharpCode.NRefactory.Editor; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor : DepthFirstAstVisitor + { + public override void VisitExpressionStatement(ExpressionStatement expressionStatement) + { + base.VisitExpressionStatement(expressionStatement); + FixSemicolon(expressionStatement.SemicolonToken); + } + + void VisitBlockWithoutFixingBraces(BlockStatement blockStatement, bool indent) + { + if (indent) { + curIndent.Push(IndentType.Block); + } + + VisitChildrenToFormat (blockStatement, child => { + if (child.Role == Roles.LBrace || child.Role == Roles.RBrace) { + return; + } + + if (child is Statement) { + FixStatementIndentation(child.StartLocation); + child.AcceptVisitor(this); + } else if (child is Comment) { + child.AcceptVisitor(this); + } else if (child is NewLineNode) { + // ignore + } else { + // pre processor directives at line start, if they are there. + if (child.StartLocation.Column > 1) + FixStatementIndentation(child.StartLocation); + } + }); + + if (indent) { + curIndent.Pop (); + } + } + + public override void VisitBlockStatement(BlockStatement blockStatement) + { + FixIndentation(blockStatement); + VisitBlockWithoutFixingBraces(blockStatement, policy.IndentBlocks); + FixIndentation(blockStatement.RBraceToken); + } + + public override void VisitBreakStatement(BreakStatement breakStatement) + { + FixSemicolon(breakStatement.SemicolonToken); + } + + public override void VisitCheckedStatement(CheckedStatement checkedStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, checkedStatement.Body); + } + + public override void VisitContinueStatement(ContinueStatement continueStatement) + { + FixSemicolon(continueStatement.SemicolonToken); + } + + public override void VisitEmptyStatement(EmptyStatement emptyStatement) + { + // Empty + } + + public override void VisitFixedStatement(FixedStatement fixedStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, fixedStatement.EmbeddedStatement); + } + + public override void VisitForeachStatement(ForeachStatement foreachStatement) + { + ForceSpacesBeforeRemoveNewLines(foreachStatement.LParToken, policy.SpaceBeforeForeachParentheses); + + ForceSpacesAfter(foreachStatement.LParToken, policy.SpacesWithinForeachParentheses); + ForceSpacesBeforeRemoveNewLines(foreachStatement.RParToken, policy.SpacesWithinForeachParentheses); + + FixEmbeddedStatment(policy.StatementBraceStyle, foreachStatement.EmbeddedStatement); + } + + void FixEmbeddedStatment(BraceStyle braceStyle, AstNode node) + { + FixEmbeddedStatment(braceStyle, null, false, node); + } + + void FixEmbeddedStatment(BraceStyle braceStyle, CSharpTokenNode token, bool allowInLine, AstNode node, bool statementAlreadyIndented = false) + { + if (node == null) + return; + bool isBlock = node is BlockStatement; + FormattingChanges.TextReplaceAction beginBraceAction = null; + FormattingChanges.TextReplaceAction endBraceAction = null; + BlockStatement closeBlockToBeFixed = null; + if (isBlock) { + BlockStatement block = node as BlockStatement; + if (allowInLine && block.StartLocation.Line == block.EndLocation.Line && block.Statements.Count() <= 1) { + if (block.Statements.Count() == 1) + nextStatementIndent = " "; + } else { + if (!statementAlreadyIndented) + FixOpenBrace(braceStyle, block.LBraceToken); + closeBlockToBeFixed = block; + } + + if (braceStyle == BraceStyle.NextLineShifted2) + curIndent.Push(IndentType.Block); + } else { + if (allowInLine && token.StartLocation.Line == node.EndLocation.Line) { + nextStatementIndent = " "; + } + } + bool pushed = false; + + if (policy.IndentBlocks && !( + policy.AlignEmbeddedStatements && node is IfElseStatement && node.Parent is IfElseStatement || + policy.AlignEmbeddedStatements && node is UsingStatement && node.Parent is UsingStatement || + policy.AlignEmbeddedStatements && node is LockStatement && node.Parent is LockStatement)) { + curIndent.Push(IndentType.Block); + pushed = true; + } + if (isBlock) { + VisitBlockWithoutFixingBraces((BlockStatement)node, false); + } else { + if (!statementAlreadyIndented) { + PlaceOnNewLine(policy.EmbeddedStatementPlacement, node); + nextStatementIndent = null; + } + node.AcceptVisitor(this); + } + nextStatementIndent = null; + if (pushed) + curIndent.Pop(); + if (beginBraceAction != null && endBraceAction != null) { + beginBraceAction.DependsOn = endBraceAction; + endBraceAction.DependsOn = beginBraceAction; + } + + if (isBlock && braceStyle == BraceStyle.NextLineShifted2) + curIndent.Pop(); + if (closeBlockToBeFixed != null) { + FixClosingBrace(braceStyle, closeBlockToBeFixed.RBraceToken); + } + + } + + public bool IsLineIsEmptyUpToEol(TextLocation startLocation) + { + return IsLineIsEmptyUpToEol(document.GetOffset(startLocation) - 1); + } + + bool IsLineIsEmptyUpToEol(int startOffset) + { + for (int offset = startOffset - 1; offset >= 0; offset--) { + char ch = document.GetCharAt(offset); + if (ch != ' ' && ch != '\t') { + return NewLine.IsNewLine (ch); + } + } + return true; + } + + int SearchWhitespaceStart(int startOffset) + { + if (startOffset < 0) { + throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); + } + for (int offset = startOffset - 1; offset >= 0; offset--) { + char ch = document.GetCharAt(offset); + if (!Char.IsWhiteSpace(ch)) { + return offset + 1; + } + } + return 0; + } + + int SearchWhitespaceEnd(int startOffset) + { + if (startOffset > document.TextLength) { + throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); + } + for (int offset = startOffset + 1; offset < document.TextLength; offset++) { + char ch = document.GetCharAt(offset); + if (!Char.IsWhiteSpace(ch)) { + return offset + 1; + } + } + return document.TextLength - 1; + } + + int SearchWhitespaceLineStart(int startOffset) + { + if (startOffset < 0) { + throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); + } + for (int offset = startOffset - 1; offset >= 0; offset--) { + char ch = document.GetCharAt(offset); + if (ch != ' ' && ch != '\t') { + return offset + 1; + } + } + return 0; + } + + public override void VisitForStatement(ForStatement forStatement) + { + foreach (AstNode node in forStatement.Children) { + if (node.Role == Roles.Semicolon) { + if (node.GetNextSibling(NoWhitespacePredicate) is CSharpTokenNode || node.GetNextSibling(NoWhitespacePredicate) is EmptyStatement) { + continue; + } + ForceSpacesBefore(node, policy.SpaceBeforeForSemicolon); + ForceSpacesAfter(node, policy.SpaceAfterForSemicolon); + } else if (node.Role == Roles.LPar) { + ForceSpacesBeforeRemoveNewLines(node, policy.SpaceBeforeForParentheses); + ForceSpacesAfter(node, policy.SpacesWithinForParentheses); + } else if (node.Role == Roles.RPar) { + ForceSpacesBeforeRemoveNewLines(node, policy.SpacesWithinForParentheses); + } else if (node.Role == Roles.EmbeddedStatement) { + FixEmbeddedStatment(policy.StatementBraceStyle, node); + } else { + node.AcceptVisitor(this); + } + } + } + + public override void VisitGotoStatement(GotoStatement gotoStatement) + { + VisitChildren(gotoStatement); + FixSemicolon(gotoStatement.SemicolonToken); + } + + public override void VisitIfElseStatement(IfElseStatement ifElseStatement) + { + ForceSpacesBeforeRemoveNewLines(ifElseStatement.LParToken, policy.SpaceBeforeIfParentheses); + Align(ifElseStatement.LParToken, ifElseStatement.Condition, policy.SpacesWithinIfParentheses); + ForceSpacesBeforeRemoveNewLines(ifElseStatement.RParToken, policy.SpacesWithinIfParentheses); + + + if (!ifElseStatement.TrueStatement.IsNull) { + FixEmbeddedStatment(policy.StatementBraceStyle, ifElseStatement.IfToken, policy.AllowIfBlockInline, ifElseStatement.TrueStatement); + } + + if (!ifElseStatement.FalseStatement.IsNull) { + var placeElseOnNewLine = policy.ElseNewLinePlacement; + if (!(ifElseStatement.TrueStatement is BlockStatement)) { + placeElseOnNewLine = NewLinePlacement.NewLine; + } + PlaceOnNewLine(placeElseOnNewLine, ifElseStatement.ElseToken); + if (ifElseStatement.FalseStatement is IfElseStatement) { + PlaceOnNewLine(policy.ElseIfNewLinePlacement, ((IfElseStatement)ifElseStatement.FalseStatement).IfToken); + } + FixEmbeddedStatment(policy.StatementBraceStyle, ifElseStatement.ElseToken, policy.AllowIfBlockInline, ifElseStatement.FalseStatement, ifElseStatement.FalseStatement is IfElseStatement); + } + } + + public override void VisitLabelStatement(LabelStatement labelStatement) + { + // TODO + VisitChildren(labelStatement); + } + + public override void VisitLockStatement(LockStatement lockStatement) + { + ForceSpacesBeforeRemoveNewLines(lockStatement.LParToken, policy.SpaceBeforeLockParentheses); + + ForceSpacesAfter(lockStatement.LParToken, policy.SpacesWithinLockParentheses); + ForceSpacesBeforeRemoveNewLines(lockStatement.RParToken, policy.SpacesWithinLockParentheses); + + FixEmbeddedStatment(policy.StatementBraceStyle, lockStatement.EmbeddedStatement); + } + + public override void VisitReturnStatement(ReturnStatement returnStatement) + { + VisitChildren(returnStatement); + FixSemicolon(returnStatement.SemicolonToken); + } + + public override void VisitSwitchStatement(SwitchStatement switchStatement) + { + ForceSpacesBeforeRemoveNewLines(switchStatement.LParToken, policy.SpaceBeforeSwitchParentheses); + + ForceSpacesAfter(switchStatement.LParToken, policy.SpacesWithinSwitchParentheses); + ForceSpacesBeforeRemoveNewLines(switchStatement.RParToken, policy.SpacesWithinSwitchParentheses); + + FixOpenBrace(policy.StatementBraceStyle, switchStatement.LBraceToken); + VisitChildren(switchStatement); + FixClosingBrace(policy.StatementBraceStyle, switchStatement.RBraceToken); + } + + public override void VisitSwitchSection(SwitchSection switchSection) + { + if (policy.IndentSwitchBody) { + curIndent.Push(IndentType.Block); + } + + foreach (CaseLabel label in switchSection.CaseLabels) { + FixStatementIndentation(label.StartLocation); + label.AcceptVisitor(this); + } + if (policy.IndentCaseBody) { + curIndent.Push(IndentType.Block); + } + + foreach (var stmt in switchSection.Statements) { + if (stmt is BreakStatement && !policy.IndentBreakStatements && policy.IndentCaseBody) { + curIndent.Pop(); + FixStatementIndentation(stmt.StartLocation); + stmt.AcceptVisitor(this); + curIndent.Push(IndentType.Block); + continue; + } + FixStatementIndentation(stmt.StartLocation); + stmt.AcceptVisitor(this); + } + if (policy.IndentCaseBody) { + curIndent.Pop (); + } + + if (policy.IndentSwitchBody) { + curIndent.Pop (); + } + } + + public override void VisitCaseLabel(CaseLabel caseLabel) + { + ForceSpacesBefore(caseLabel.ColonToken, false); + } + + public override void VisitThrowStatement(ThrowStatement throwStatement) + { + VisitChildren(throwStatement); + FixSemicolon(throwStatement.SemicolonToken); + } + + public override void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + if (!tryCatchStatement.TryBlock.IsNull) { + FixEmbeddedStatment(policy.StatementBraceStyle, tryCatchStatement.TryBlock); + } + + foreach (CatchClause clause in tryCatchStatement.CatchClauses) { + PlaceOnNewLine(policy.CatchNewLinePlacement, clause.CatchToken); + if (!clause.LParToken.IsNull) { + ForceSpacesBeforeRemoveNewLines(clause.LParToken, policy.SpaceBeforeCatchParentheses); + + ForceSpacesAfter(clause.LParToken, policy.SpacesWithinCatchParentheses); + ForceSpacesBeforeRemoveNewLines(clause.RParToken, policy.SpacesWithinCatchParentheses); + } + FixEmbeddedStatment(policy.StatementBraceStyle, clause.Body); + } + + if (!tryCatchStatement.FinallyBlock.IsNull) { + PlaceOnNewLine(policy.FinallyNewLinePlacement, tryCatchStatement.FinallyToken); + + FixEmbeddedStatment(policy.StatementBraceStyle, tryCatchStatement.FinallyBlock); + } + + } + + public override void VisitCatchClause(CatchClause catchClause) + { + // Handled in TryCatchStatement + } + + public override void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, uncheckedStatement.Body); + } + + public override void VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, unsafeStatement.Body); + } + + public override void VisitUsingStatement(UsingStatement usingStatement) + { + ForceSpacesBeforeRemoveNewLines(usingStatement.LParToken, policy.SpaceBeforeUsingParentheses); + + Align(usingStatement.LParToken, usingStatement.ResourceAcquisition, policy.SpacesWithinUsingParentheses); + + ForceSpacesBeforeRemoveNewLines(usingStatement.RParToken, policy.SpacesWithinUsingParentheses); + + FixEmbeddedStatment(policy.StatementBraceStyle, usingStatement.EmbeddedStatement); + } + + public override void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + var returnType = variableDeclarationStatement.Type; + returnType.AcceptVisitor(this); + if ((variableDeclarationStatement.Modifiers & Modifiers.Const) == Modifiers.Const) { + ForceSpacesAround(returnType, true); + } else { + ForceSpacesAfter(returnType, true); + } + var lastLoc = variableDeclarationStatement.StartLocation; + foreach (var initializer in variableDeclarationStatement.Variables) { + if (lastLoc.Line != initializer.StartLocation.Line) { + FixStatementIndentation(initializer.StartLocation); + lastLoc = initializer.StartLocation; + } + initializer.AcceptVisitor(this); + } + + FormatCommas(variableDeclarationStatement, policy.SpaceBeforeLocalVariableDeclarationComma, policy.SpaceAfterLocalVariableDeclarationComma); + FixSemicolon(variableDeclarationStatement.SemicolonToken); + } + + public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, doWhileStatement.EmbeddedStatement); + PlaceOnNewLine(doWhileStatement.EmbeddedStatement is BlockStatement ? policy.WhileNewLinePlacement : NewLinePlacement.NewLine, doWhileStatement.WhileToken); + + Align(doWhileStatement.LParToken, doWhileStatement.Condition, policy.SpacesWithinWhileParentheses); + ForceSpacesBeforeRemoveNewLines(doWhileStatement.RParToken, policy.SpacesWithinWhileParentheses); + } + + void Align(AstNode lPar, AstNode alignNode, bool space) + { + int extraSpaces = 0; + var useExtraSpaces = lPar.StartLocation.Line == alignNode.StartLocation.Line; + if (useExtraSpaces) { + extraSpaces = Math.Max(0, lPar.StartLocation.Column + (space ? 1 : 0) - curIndent.IndentString.Length); + curIndent.ExtraSpaces += extraSpaces; + ForceSpacesAfter(lPar, space); + } else { + curIndent.Push(IndentType.Continuation); + FixIndentation(alignNode); + } + alignNode.AcceptVisitor(this); + + if (useExtraSpaces) { + curIndent.ExtraSpaces -= extraSpaces; + } else { + curIndent.Pop(); + } + + } + + public override void VisitWhileStatement(WhileStatement whileStatement) + { + ForceSpacesBeforeRemoveNewLines(whileStatement.LParToken, policy.SpaceBeforeWhileParentheses); + Align(whileStatement.LParToken, whileStatement.Condition, policy.SpacesWithinWhileParentheses); + ForceSpacesBeforeRemoveNewLines(whileStatement.RParToken, policy.SpacesWithinWhileParentheses); + + FixEmbeddedStatment(policy.StatementBraceStyle, whileStatement.EmbeddedStatement); + } + + public override void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + FixSemicolon(yieldBreakStatement.SemicolonToken); + } + + public override void VisitYieldReturnStatement(YieldReturnStatement yieldStatement) + { + yieldStatement.Expression.AcceptVisitor(this); + FixSemicolon(yieldStatement.SemicolonToken); + } + + public override void VisitVariableInitializer(VariableInitializer variableInitializer) + { + if (!variableInitializer.AssignToken.IsNull) { + ForceSpacesAround(variableInitializer.AssignToken, policy.SpaceAroundAssignment); + } + if (!variableInitializer.Initializer.IsNull) { + int extraSpaces = 0; + var useExtraSpaces = variableInitializer.AssignToken.StartLocation.Line == variableInitializer.Initializer.StartLocation.Line; + if (useExtraSpaces) { + extraSpaces = Math.Max(0, variableInitializer.AssignToken.StartLocation.Column + 1 - curIndent.IndentString.Length); + curIndent.ExtraSpaces += extraSpaces; + } else { + curIndent.Push(IndentType.Continuation); + FixIndentation(variableInitializer.Initializer); + } + variableInitializer.Initializer.AcceptVisitor(this); + + if (useExtraSpaces) { + curIndent.ExtraSpaces -= extraSpaces; + } else { + curIndent.Pop(); + } + } + + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_TypeMembers.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_TypeMembers.cs new file mode 100644 index 000000000..913a61e2e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_TypeMembers.cs @@ -0,0 +1,477 @@ +// +// AstFormattingVisitor_TypeMembers.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor : DepthFirstAstVisitor + { + public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + FixAttributesAndDocComment(propertyDeclaration); + bool oneLine = false; + bool fixClosingBrace = false; + PropertyFormatting propertyFormatting; + + if ((propertyDeclaration.Getter.IsNull || propertyDeclaration.Getter.Body.IsNull) && + (propertyDeclaration.Setter.IsNull || propertyDeclaration.Setter.Body.IsNull)) { + propertyFormatting = policy.AutoPropertyFormatting; + } else { + propertyFormatting = policy.SimplePropertyFormatting; + } + + switch (propertyFormatting) { + case PropertyFormatting.AllowOneLine: + bool isSimple = IsSimpleAccessor(propertyDeclaration.Getter) && IsSimpleAccessor(propertyDeclaration.Setter); + int accessorLine = propertyDeclaration.RBraceToken.StartLocation.Line; + if (!propertyDeclaration.Getter.IsNull && propertyDeclaration.Setter.IsNull) { + accessorLine = propertyDeclaration.Getter.StartLocation.Line; + } else if (propertyDeclaration.Getter.IsNull && !propertyDeclaration.Setter.IsNull) { + accessorLine = propertyDeclaration.Setter.StartLocation.Line; + } else { + var acc = propertyDeclaration.Getter.StartLocation < propertyDeclaration.Setter.StartLocation ? + propertyDeclaration.Getter : propertyDeclaration.Setter; + accessorLine = acc.StartLocation.Line; + } + if (isSimple && + Math.Min(propertyDeclaration.Getter.StartLocation.Line, propertyDeclaration.Setter.StartLocation.Line) == propertyDeclaration.LBraceToken.StartLocation.Line && + propertyDeclaration.Getter.StartLocation.Line != propertyDeclaration.Setter.StartLocation.Line) + goto case PropertyFormatting.ForceOneLine; + if (!isSimple || propertyDeclaration.LBraceToken.StartLocation.Line != accessorLine) { + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken); + } else { + ForceSpacesBefore(propertyDeclaration.Getter, true); + ForceSpacesBefore(propertyDeclaration.Setter, true); + ForceSpacesBeforeRemoveNewLines(propertyDeclaration.RBraceToken, true); + oneLine = true; + } + break; + case PropertyFormatting.ForceNewLine: + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken); + break; + case PropertyFormatting.ForceOneLine: + isSimple = IsSimpleAccessor(propertyDeclaration.Getter) && IsSimpleAccessor(propertyDeclaration.Setter); + if (isSimple) { + var lBraceToken = propertyDeclaration.LBraceToken; + var rBraceToken = propertyDeclaration.RBraceToken; + ForceSpacesBeforeRemoveNewLines(lBraceToken, true); + if (!propertyDeclaration.Getter.IsNull) + ForceSpacesBeforeRemoveNewLines(propertyDeclaration.Getter, true); + if (!propertyDeclaration.Setter.IsNull) + ForceSpacesBeforeRemoveNewLines(propertyDeclaration.Setter, true); + + ForceSpacesBeforeRemoveNewLines(rBraceToken, true); + oneLine = true; + } else { + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken); + } + break; + } + if (policy.IndentPropertyBody) + curIndent.Push(IndentType.Block); + + FormatAccessor(propertyDeclaration.Getter, policy.PropertyGetBraceStyle, policy.SimpleGetBlockFormatting, oneLine); + FormatAccessor(propertyDeclaration.Setter, policy.PropertySetBraceStyle, policy.SimpleSetBlockFormatting, oneLine); + + if (policy.IndentPropertyBody) { + curIndent.Pop(); + } + if (fixClosingBrace) + FixClosingBrace(policy.PropertyBraceStyle, propertyDeclaration.RBraceToken); + + } + + void FormatAccessor(Accessor accessor, BraceStyle braceStyle, PropertyFormatting blockFormatting, bool oneLine) + { + if (accessor.IsNull) + return; + if (!oneLine) { + if (!IsLineIsEmptyUpToEol(accessor.StartLocation)) { + int offset = this.document.GetOffset(accessor.StartLocation); + int start = SearchWhitespaceStart(offset); + string indentString = this.curIndent.IndentString; + AddChange(start, offset - start, this.options.EolMarker + indentString); + } else { + FixIndentation(accessor); + } + } else { + blockFormatting = PropertyFormatting.ForceOneLine; + if (!accessor.Body.IsNull) { + ForceSpacesBeforeRemoveNewLines(accessor.Body.LBraceToken, true); + ForceSpacesBeforeRemoveNewLines(accessor.Body.RBraceToken, true); + } + } + + + if (!accessor.IsNull) { + if (!accessor.Body.IsNull) { + if (IsSimpleAccessor (accessor)) { + switch (blockFormatting) { + case PropertyFormatting.AllowOneLine: + if (accessor.Body.LBraceToken.StartLocation.Line != accessor.Body.RBraceToken.StartLocation.Line) + goto case PropertyFormatting.ForceNewLine; + nextStatementIndent = " "; + VisitBlockWithoutFixingBraces(accessor.Body, policy.IndentBlocks); + nextStatementIndent = null; + if (!oneLine) + ForceSpacesBeforeRemoveNewLines(accessor.Body.RBraceToken, true); + break; + case PropertyFormatting.ForceOneLine: + FixOpenBrace(BraceStyle.EndOfLine, accessor.Body.LBraceToken); + + + var statement = accessor.Body.Statements.FirstOrDefault(); + if (statement != null) { + ForceSpacesBeforeRemoveNewLines(statement, true); + statement.AcceptVisitor(this); + } + if (!oneLine) + ForceSpacesBeforeRemoveNewLines(accessor.Body.RBraceToken, true); + break; + case PropertyFormatting.ForceNewLine: + FixOpenBrace(braceStyle, accessor.Body.LBraceToken); + VisitBlockWithoutFixingBraces(accessor.Body, policy.IndentBlocks); + if (!oneLine) + FixClosingBrace(braceStyle, accessor.Body.RBraceToken); + break; + } + } else { + FixOpenBrace(braceStyle, accessor.Body.LBraceToken); + VisitBlockWithoutFixingBraces(accessor.Body, policy.IndentBlocks); + FixClosingBrace(braceStyle, accessor.Body.RBraceToken); + } + } + } + } + + public override void VisitAccessor(Accessor accessor) + { + FixAttributesAndDocComment(accessor); + + base.VisitAccessor(accessor); + } + + public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + FixAttributesAndDocComment(indexerDeclaration); + + ForceSpacesBefore(indexerDeclaration.LBracketToken, policy.SpaceBeforeIndexerDeclarationBracket); + ForceSpacesAfter(indexerDeclaration.LBracketToken, policy.SpaceWithinIndexerDeclarationBracket); + + FormatArguments(indexerDeclaration); + + bool oneLine = false; + bool fixClosingBrace = false; + switch (policy.SimplePropertyFormatting) { + case PropertyFormatting.AllowOneLine: + bool isSimple = IsSimpleAccessor(indexerDeclaration.Getter) && IsSimpleAccessor(indexerDeclaration.Setter); + int accessorLine = indexerDeclaration.RBraceToken.StartLocation.Line; + if (!indexerDeclaration.Getter.IsNull && indexerDeclaration.Setter.IsNull) { + accessorLine = indexerDeclaration.Getter.StartLocation.Line; + } else if (indexerDeclaration.Getter.IsNull && !indexerDeclaration.Setter.IsNull) { + accessorLine = indexerDeclaration.Setter.StartLocation.Line; + } else { + var acc = indexerDeclaration.Getter.StartLocation < indexerDeclaration.Setter.StartLocation ? + indexerDeclaration.Getter : indexerDeclaration.Setter; + accessorLine = acc.StartLocation.Line; + } + if (!isSimple || indexerDeclaration.LBraceToken.StartLocation.Line != accessorLine) { + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, indexerDeclaration.LBraceToken); + } else { + ForceSpacesBefore(indexerDeclaration.Getter, true); + ForceSpacesBefore(indexerDeclaration.Setter, true); + ForceSpacesBeforeRemoveNewLines(indexerDeclaration.RBraceToken, true); + oneLine = true; + } + break; + case PropertyFormatting.ForceNewLine: + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, indexerDeclaration.LBraceToken); + break; + case PropertyFormatting.ForceOneLine: + isSimple = IsSimpleAccessor(indexerDeclaration.Getter) && IsSimpleAccessor(indexerDeclaration.Setter); + if (isSimple) { + int offset = this.document.GetOffset(indexerDeclaration.LBraceToken.StartLocation); + + int start = SearchWhitespaceStart(offset); + int end = SearchWhitespaceEnd(offset); + AddChange(start, offset - start, " "); + AddChange(offset + 1, end - offset - 2, " "); + + offset = this.document.GetOffset(indexerDeclaration.RBraceToken.StartLocation); + start = SearchWhitespaceStart(offset); + AddChange(start, offset - start, " "); + oneLine = true; + + } else { + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, indexerDeclaration.LBraceToken); + } + break; + } + + if (policy.IndentPropertyBody) + curIndent.Push(IndentType.Block); + + FormatAccessor(indexerDeclaration.Getter, policy.PropertyGetBraceStyle, policy.SimpleGetBlockFormatting, oneLine); + FormatAccessor(indexerDeclaration.Setter, policy.PropertySetBraceStyle, policy.SimpleSetBlockFormatting, oneLine); + if (policy.IndentPropertyBody) + curIndent.Pop(); + + if (fixClosingBrace) + FixClosingBrace(policy.PropertyBraceStyle, indexerDeclaration.RBraceToken); + } + + static bool IsSimpleEvent(AstNode node) + { + return node is EventDeclaration; + } + + public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + FixAttributesAndDocComment(eventDeclaration); + + FixOpenBrace(policy.EventBraceStyle, eventDeclaration.LBraceToken); + if (policy.IndentEventBody) + curIndent.Push(IndentType.Block); + + if (!eventDeclaration.AddAccessor.IsNull) { + FixIndentation(eventDeclaration.AddAccessor); + if (!eventDeclaration.AddAccessor.Body.IsNull) { + if (!policy.AllowEventAddBlockInline || eventDeclaration.AddAccessor.Body.LBraceToken.StartLocation.Line != eventDeclaration.AddAccessor.Body.RBraceToken.StartLocation.Line) { + FixOpenBrace(policy.EventAddBraceStyle, eventDeclaration.AddAccessor.Body.LBraceToken); + VisitBlockWithoutFixingBraces(eventDeclaration.AddAccessor.Body, policy.IndentBlocks); + FixClosingBrace(policy.EventAddBraceStyle, eventDeclaration.AddAccessor.Body.RBraceToken); + } else { + nextStatementIndent = " "; + VisitBlockWithoutFixingBraces(eventDeclaration.AddAccessor.Body, policy.IndentBlocks); + nextStatementIndent = null; + } + } + } + + if (!eventDeclaration.RemoveAccessor.IsNull) { + FixIndentation(eventDeclaration.RemoveAccessor); + if (!eventDeclaration.RemoveAccessor.Body.IsNull) { + if (!policy.AllowEventRemoveBlockInline || eventDeclaration.RemoveAccessor.Body.LBraceToken.StartLocation.Line != eventDeclaration.RemoveAccessor.Body.RBraceToken.StartLocation.Line) { + FixOpenBrace(policy.EventRemoveBraceStyle, eventDeclaration.RemoveAccessor.Body.LBraceToken); + VisitBlockWithoutFixingBraces(eventDeclaration.RemoveAccessor.Body, policy.IndentBlocks); + FixClosingBrace(policy.EventRemoveBraceStyle, eventDeclaration.RemoveAccessor.Body.RBraceToken); + } else { + nextStatementIndent = " "; + VisitBlockWithoutFixingBraces(eventDeclaration.RemoveAccessor.Body, policy.IndentBlocks); + nextStatementIndent = null; + } + } + } + + if (policy.IndentEventBody) + curIndent.Pop(); + + FixClosingBrace(policy.EventBraceStyle, eventDeclaration.RBraceToken); + } + + public override void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + FixAttributesAndDocComment(eventDeclaration); + + foreach (var m in eventDeclaration.ModifierTokens) { + ForceSpacesAfter(m, true); + } + + ForceSpacesBeforeRemoveNewLines(eventDeclaration.EventToken.GetNextSibling(NoWhitespacePredicate), true); + eventDeclaration.ReturnType.AcceptVisitor(this); + ForceSpacesAfter(eventDeclaration.ReturnType, true); + /* + var lastLoc = eventDeclaration.StartLocation; + curIndent.Push(IndentType.Block); + foreach (var initializer in eventDeclaration.Variables) { + if (lastLoc.Line != initializer.StartLocation.Line) { + FixStatementIndentation(initializer.StartLocation); + lastLoc = initializer.StartLocation; + } + initializer.AcceptVisitor(this); + } + curIndent.Pop (); + */ + FixSemicolon(eventDeclaration.SemicolonToken); + } + + public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + FixAttributesAndDocComment(fieldDeclaration); + + fieldDeclaration.ReturnType.AcceptVisitor(this); + ForceSpacesAfter(fieldDeclaration.ReturnType, true); + + FormatCommas(fieldDeclaration, policy.SpaceBeforeFieldDeclarationComma, policy.SpaceAfterFieldDeclarationComma); + + var lastLoc = fieldDeclaration.ReturnType.StartLocation; + 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); + } + FixSemicolon(fieldDeclaration.SemicolonToken); + } + + public override void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + FixAttributesAndDocComment(fixedFieldDeclaration); + + FormatCommas(fixedFieldDeclaration, policy.SpaceBeforeFieldDeclarationComma, policy.SpaceAfterFieldDeclarationComma); + + var lastLoc = fixedFieldDeclaration.StartLocation; + curIndent.Push(IndentType.Block); + foreach (var initializer in fixedFieldDeclaration.Variables) { + if (lastLoc.Line != initializer.StartLocation.Line) { + FixStatementIndentation(initializer.StartLocation); + lastLoc = initializer.StartLocation; + } + initializer.AcceptVisitor(this); + } + curIndent.Pop(); + FixSemicolon(fixedFieldDeclaration.SemicolonToken); + } + + public override void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + FixAttributesAndDocComment(enumMemberDeclaration); + var initializer = enumMemberDeclaration.Initializer; + if (!initializer.IsNull) { + ForceSpacesAround(enumMemberDeclaration.AssignToken, policy.SpaceAroundAssignment); + initializer.AcceptVisitor(this); + } + } + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + FixAttributesAndDocComment(methodDeclaration); + + ForceSpacesBefore(methodDeclaration.LParToken, policy.SpaceBeforeMethodDeclarationParentheses); + if (methodDeclaration.Parameters.Any()) { + ForceSpacesAfter(methodDeclaration.LParToken, policy.SpaceWithinMethodDeclarationParentheses); + FormatArguments(methodDeclaration); + } else { + ForceSpacesAfter(methodDeclaration.LParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); + ForceSpacesBefore(methodDeclaration.RParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); + } + + foreach (var constraint in methodDeclaration.Constraints) + constraint.AcceptVisitor(this); + + if (!methodDeclaration.Body.IsNull) { + FixOpenBrace(policy.MethodBraceStyle, methodDeclaration.Body.LBraceToken); + VisitBlockWithoutFixingBraces(methodDeclaration.Body, policy.IndentMethodBody); + FixClosingBrace(policy.MethodBraceStyle, methodDeclaration.Body.RBraceToken); + } + } + + public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + FixAttributesAndDocComment(operatorDeclaration); + + ForceSpacesBefore(operatorDeclaration.LParToken, policy.SpaceBeforeMethodDeclarationParentheses); + if (operatorDeclaration.Parameters.Any()) { + ForceSpacesAfter(operatorDeclaration.LParToken, policy.SpaceWithinMethodDeclarationParentheses); + FormatArguments(operatorDeclaration); + } else { + ForceSpacesAfter(operatorDeclaration.LParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); + ForceSpacesBefore(operatorDeclaration.RParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); + } + + if (!operatorDeclaration.Body.IsNull) { + FixOpenBrace(policy.MethodBraceStyle, operatorDeclaration.Body.LBraceToken); + VisitBlockWithoutFixingBraces(operatorDeclaration.Body, policy.IndentMethodBody); + FixClosingBrace(policy.MethodBraceStyle, operatorDeclaration.Body.RBraceToken); + } + } + + public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + FixAttributesAndDocComment(constructorDeclaration); + + ForceSpacesBefore(constructorDeclaration.LParToken, policy.SpaceBeforeConstructorDeclarationParentheses); + if (constructorDeclaration.Parameters.Any()) { + ForceSpacesAfter(constructorDeclaration.LParToken, policy.SpaceWithinConstructorDeclarationParentheses); + FormatArguments(constructorDeclaration); + } else { + ForceSpacesAfter(constructorDeclaration.LParToken, policy.SpaceBetweenEmptyConstructorDeclarationParentheses); + ForceSpacesBefore(constructorDeclaration.RParToken, policy.SpaceBetweenEmptyConstructorDeclarationParentheses); + } + + var initializer = constructorDeclaration.Initializer; + if (!initializer.IsNull) { + curIndent.Push(IndentType.Block); + PlaceOnNewLine(policy.NewLineBeforeConstructorInitializerColon, constructorDeclaration.ColonToken); + PlaceOnNewLine(policy.NewLineAfterConstructorInitializerColon, initializer); + initializer.AcceptVisitor(this); + curIndent.Pop(); + } + if (!constructorDeclaration.Body.IsNull) { + FixOpenBrace(policy.ConstructorBraceStyle, constructorDeclaration.Body.LBraceToken); + VisitBlockWithoutFixingBraces(constructorDeclaration.Body, policy.IndentMethodBody); + FixClosingBrace(policy.ConstructorBraceStyle, constructorDeclaration.Body.RBraceToken); + } + } + public override void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + ForceSpacesBefore(constructorInitializer.LParToken, policy.SpaceBeforeMethodCallParentheses); + if (constructorInitializer.Arguments.Any()) { + ForceSpacesAfter(constructorInitializer.LParToken, policy.SpaceWithinMethodCallParentheses); + } else { + ForceSpacesAfter(constructorInitializer.LParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + ForceSpacesBefore(constructorInitializer.RParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + } + + FormatArguments(constructorInitializer); + + } + public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + FixAttributesAndDocComment(destructorDeclaration); + + CSharpTokenNode lParen = destructorDeclaration.LParToken; + ForceSpaceBefore(lParen, policy.SpaceBeforeConstructorDeclarationParentheses); + + if (!destructorDeclaration.Body.IsNull) { + FixOpenBrace(policy.DestructorBraceStyle, destructorDeclaration.Body.LBraceToken); + VisitBlockWithoutFixingBraces(destructorDeclaration.Body, policy.IndentMethodBody); + FixClosingBrace(policy.DestructorBraceStyle, destructorDeclaration.Body.RBraceToken); + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/GeneratedCodeSettings.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/GeneratedCodeSettings.cs new file mode 100644 index 000000000..dd2df993e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/GeneratedCodeSettings.cs @@ -0,0 +1,216 @@ +// +// GeneratedCodeSettings.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum GeneratedCodeMember + { + Unknown, + + StaticFields, + InstanceFields, + StaticProperties, + InstanceProperties, + Indexer, + Constructors, + StaticMethods, + InstanceMethods, + StaticEvents, + InstanceEvents, + Operators, + NestedTypes + } + + public class GeneratedCodeSettings + { + List codeMemberOrder; + + public List CodeMemberOrder { + get { + return codeMemberOrder; + } + set { + codeMemberOrder = value; + } + } + + public bool GenerateCategoryComments { + get; + set; + } + + public bool SubOrderAlphabetical { + get; + set; + } + + public void Apply (AstNode rootNode) + { + if (rootNode == null) + throw new ArgumentNullException ("rootNode"); + rootNode.AcceptVisitor (new GenerateCodeVisitior (this)); + } + + public virtual string GetCategoryLabel(GeneratedCodeMember memberCategory) + { + switch (memberCategory) { + case GeneratedCodeMember.StaticFields: + return "Static Fields"; + case GeneratedCodeMember.InstanceFields: + return "Fields"; + case GeneratedCodeMember.StaticProperties: + return "Static Properties"; + case GeneratedCodeMember.InstanceProperties: + return "Properties"; + case GeneratedCodeMember.Indexer: + return "Indexer"; + case GeneratedCodeMember.Constructors: + return "Constructors"; + case GeneratedCodeMember.StaticMethods: + return "Static Methods"; + case GeneratedCodeMember.InstanceMethods: + return "Methods"; + case GeneratedCodeMember.StaticEvents: + return "Static Events"; + case GeneratedCodeMember.InstanceEvents: + return "Events"; + case GeneratedCodeMember.Operators: + return "Operators"; + case GeneratedCodeMember.NestedTypes: + return "Nested Types"; + } + return null; + } + + class GenerateCodeVisitior : DepthFirstAstVisitor + { + GeneratedCodeSettings settings; + + public GenerateCodeVisitior(GeneratedCodeSettings settings) + { + if (settings == null) + throw new ArgumentNullException("settings"); + this.settings = settings; + } + + GeneratedCodeMember GetCodeMemberCategory(EntityDeclaration x) + { + bool isStatic = x.HasModifier(Modifiers.Static) || x.HasModifier(Modifiers.Const); + if (x is FieldDeclaration) + return isStatic ? GeneratedCodeMember.StaticFields : GeneratedCodeMember.InstanceFields; + if (x is IndexerDeclaration) + return GeneratedCodeMember.Indexer; + if (x is PropertyDeclaration) + return isStatic ? GeneratedCodeMember.StaticProperties : GeneratedCodeMember.InstanceProperties; + if (x is ConstructorDeclaration || x is DestructorDeclaration) + return GeneratedCodeMember.Constructors; + if (x is MethodDeclaration) + return isStatic ? GeneratedCodeMember.StaticMethods : GeneratedCodeMember.InstanceMethods; + if (x is OperatorDeclaration) + return GeneratedCodeMember.Operators; + if (x is EventDeclaration || x is CustomEventDeclaration) + return isStatic ? GeneratedCodeMember.StaticEvents : GeneratedCodeMember.InstanceEvents; + + if (x is TypeDeclaration) + return GeneratedCodeMember.NestedTypes; + + return GeneratedCodeMember.Unknown; + } + + public override void VisitTypeDeclaration (TypeDeclaration typeDeclaration) + { + if (typeDeclaration.ClassType == ClassType.Enum) + return; + var entities = new List (typeDeclaration.Members); + entities.Sort ((x, y) => { + int i1 = settings.CodeMemberOrder.IndexOf (GetCodeMemberCategory (x)); + int i2 = settings.CodeMemberOrder.IndexOf (GetCodeMemberCategory (y)); + if (i1 != i2) + return i1.CompareTo (i2); + if (settings.SubOrderAlphabetical) + return (x.Name ?? "").CompareTo ((y.Name ?? "")); + return entities.IndexOf (x).CompareTo (entities.IndexOf (y)); + }); + typeDeclaration.Members.Clear (); + typeDeclaration.Members.AddRange (entities); + + if (settings.GenerateCategoryComments) { + var curCat = GeneratedCodeMember.Unknown; + foreach (var mem in entities) { + if (mem.NextSibling is EntityDeclaration) + mem.Parent.InsertChildAfter (mem, new NewLineNode (), Roles.NewLine); + + var cat = GetCodeMemberCategory (mem); + if (cat == curCat) + continue; + curCat = cat; + var label = settings.GetCategoryLabel (curCat); + if (string.IsNullOrEmpty (label)) + continue; + + var cmt = new Comment ("", CommentType.SingleLine); + var cmt2 = new Comment (" " + label, CommentType.SingleLine); + var cmt3 = new Comment ("", CommentType.SingleLine); + mem.Parent.InsertChildBefore (mem, cmt, Roles.Comment); + mem.Parent.InsertChildBefore (mem, cmt2, Roles.Comment); + mem.Parent.InsertChildBefore (mem, cmt3, Roles.Comment); + if (cmt.PrevSibling is EntityDeclaration) + mem.Parent.InsertChildBefore (cmt, new NewLineNode (), Roles.NewLine); + } + } + } + } + + static Lazy defaultSettings = new Lazy( + () => new GeneratedCodeSettings() { + CodeMemberOrder = new List() { + GeneratedCodeMember.StaticFields, + GeneratedCodeMember.InstanceFields, + GeneratedCodeMember.StaticProperties, + GeneratedCodeMember.InstanceProperties, + GeneratedCodeMember.Indexer, + GeneratedCodeMember.Constructors, + GeneratedCodeMember.StaticMethods, + GeneratedCodeMember.InstanceMethods, + GeneratedCodeMember.StaticEvents, + GeneratedCodeMember.InstanceEvents, + GeneratedCodeMember.Operators, + GeneratedCodeMember.NestedTypes + }, + GenerateCategoryComments = true, + SubOrderAlphabetical = true + }); + + public static GeneratedCodeSettings Default { + get { + return defaultSettings.Value; + } + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs new file mode 100644 index 000000000..347e66f6e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs @@ -0,0 +1,246 @@ +// +// Indent.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum IndentType + { + Block, + DoubleBlock, + Continuation, + Alignment, + Label, + Empty + } + + public class Indent + { + readonly CloneableStack indentStack = new CloneableStack(); + readonly TextEditorOptions options; + int curIndent; + int extraSpaces; + string indentString; + + public int CurIndent { + get { + return curIndent; + } + } + + public Indent(TextEditorOptions options) + { + this.options = options; + Reset(); + } + + Indent(Indent engine) + { + this.indentStack = engine.indentStack.Clone(); + this.options = engine.options; + this.curIndent = engine.curIndent; + this.extraSpaces = engine.extraSpaces; + this.indentString = engine.indentString; + } + + public Indent Clone() + { + return new Indent(this); + } + + public void Reset() + { + curIndent = 0; + indentString = ""; + indentStack.Clear(); + } + + public void Push(IndentType type) + { + indentStack.Push(type); + curIndent += GetIndent(type); + Update(); + } + + public void Push(Indent indent) + { + foreach (var i in indent.indentStack) + Push(i); + } + + public void Pop() + { + curIndent -= GetIndent(indentStack.Pop()); + Update(); + } + + public bool PopIf(IndentType type) + { + if (Count > 0 && Peek() == type) + { + Pop(); + return true; + } + + return false; + } + + public void PopWhile(IndentType type) + { + while (Count > 0 && Peek() == type) + { + Pop(); + } + } + + public bool PopTry() + { + if (Count > 0) + { + Pop(); + return true; + } + + return false; + } + + public int Count { + get { + return indentStack.Count; + } + } + + public IndentType Peek() + { + return indentStack.Peek(); + } + + int GetIndent(IndentType indentType) + { + switch (indentType) { + case IndentType.Block: + return options.IndentSize; + case IndentType.DoubleBlock: + return options.IndentSize * 2; + case IndentType.Alignment: + case IndentType.Continuation: + return options.ContinuationIndent; + case IndentType.Label: + return options.LabelIndent; + case IndentType.Empty: + return 0; + default: + throw new ArgumentOutOfRangeException(); + } + } + + void Update() + { + if (options.TabsToSpaces) { + indentString = new string(' ', curIndent + ExtraSpaces); + return; + } + indentString = new string('\t', curIndent / options.TabSize) + new string(' ', curIndent % options.TabSize) + new string(' ', ExtraSpaces); + } + + public int ExtraSpaces { + get { + return extraSpaces; + } + set { + if (value < 0) + throw new ArgumentOutOfRangeException("ExtraSpaces >= 0 but was " + value); + extraSpaces = value; + Update(); + } + } + + + public string IndentString { + get { + return indentString; + } + } + + public override string ToString() + { + return string.Format("[Indent: curIndent={0}]", curIndent); + } + + public Indent GetIndentWithoutSpace () + { + var result = new Indent(options); + foreach (var i in indentStack) + result.Push(i); + return result; + } + + public static Indent ConvertFrom(string indentString, Indent correctIndent, TextEditorOptions options = null) + { + options = options ?? TextEditorOptions.Default; + var result = new Indent(options); + + var indent = string.Concat(indentString.Where(c => c == ' ' || c == '\t')); + var indentTypes = new Stack(correctIndent.indentStack); + + foreach (var _ in indent.TakeWhile(c => c == '\t')) + { + if (indentTypes.Count > 0) + result.Push(indentTypes.Pop()); + else + result.Push(IndentType.Continuation); + } + + result.ExtraSpaces = indent + .SkipWhile(c => c == '\t') + .TakeWhile(c => c == ' ') + .Count(); + + return result; + } + + public void RemoveAlignment() + { + ExtraSpaces = 0; + if (Count > 0 && Peek() == IndentType.Alignment) + Pop(); + } + + public void SetAlignment(int i, bool forceSpaces = false) + { + var alignChars = Math.Max(0, i); + if (forceSpaces) { + ExtraSpaces = alignChars; + return; + } + RemoveAlignment(); + Push(IndentType.Alignment); + } + + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/TextEditorOptions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/TextEditorOptions.cs new file mode 100644 index 000000000..7c3eef2d2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/TextEditorOptions.cs @@ -0,0 +1,116 @@ +// +// TextEditorOptions.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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 +{ + /// + /// The text editor options class holds basic information about the text editor settings that influences code generation and formatting beside + /// the CSharpFormattingOptions. + /// + public class TextEditorOptions + { + public static readonly TextEditorOptions Default = new TextEditorOptions (); + + /// + /// Gets or sets a value indicating if tabs need to be replaced by spaces. If that is true, all indenting will be done with spaces only, + /// otherwise the indenting will start with tabs. + /// + public bool TabsToSpaces { + get; + set; + } + + /// + /// Gets or sets the size of the tab chacter as spaces. + /// + public int TabSize { + get; + set; + } + + /// + /// Gets or sets the size of a single indent as spaces. + /// + public int IndentSize { + get; + set; + } + + /// + /// Gets or sets the continuation indent. A continuation indent is the indent that will be put after an embedded statement that is no block. + /// + public int ContinuationIndent { + get; + set; + } + + /// + /// Gets or sets the label indent. A label indent is the indent that will be put before an label. + /// (Note: it may be negative -IndentSize would cause that labels are unindented) + /// + public int LabelIndent { + get; + set; + } + + /// + /// Gets or sets the eol marker. + /// + public string EolMarker { + get; + set; + } + + /// + /// If true blank lines will be indented up to the indent level, otherwise blank lines will have the length 0. + /// + public bool IndentBlankLines { + get; + set; + } + + /// + /// Gets or sets the length of the desired line length. The formatting engine will wrap at wrap points set to Wrapping.WrapIfTooLong if the line length is too long. + /// 0 means do not wrap. + /// + public int WrapLineLength { + get; + set; + } + + public TextEditorOptions() + { + TabsToSpaces = false; + TabSize = 4; + IndentSize = 4; + ContinuationIndent = 4; + WrapLineLength = 0; + EolMarker = Environment.NewLine; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj new file mode 100644 index 000000000..4c53d5ff5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj @@ -0,0 +1,420 @@ + + + + {53DCA265-3C3C-42F9-B647-F72BA678122B} + Debug + AnyCPU + Library + ICSharpCode.NRefactory.CSharp + ICSharpCode.NRefactory.CSharp + Properties + False + False + 4 + False + 8.0.30703 + 2.0 + true + ..\ICSharpCode.NRefactory.snk + False + File + ..\bin\$(Configuration)\ICSharpCode.NRefactory.CSharp.xml + 1591,1587,1570 + ..\bin\$(Configuration)\ + False + obj\$(Configuration)\ + + + AnyCPU + False + Auto + 4194304 + 4096 + + + False + False + DEBUG;TRACE;FULL_AST;NET_4_0 + + + True + False + TRACE;FULL_AST;NET_4_0 + obj\ + + + PdbOnly + True + ..\bin\Release\ + + + full + True + True + ..\bin\Debug\ + + + False + False + DEBUG;TRACE;FULL_AST;NET_4_0;NET_4_5 + v4.5 + + + full + True + True + v4.5 + ..\bin\net_4_5_Debug\ + + + True + False + TRACE;FULL_AST;NET_4_0;NET_4_5 + v4.5 + + + none + True + v4.5 + ..\bin\net_4_5_Release\ + + + + + + + + + + Properties\GlobalAssemblyInfo.cs + + + + + + + + + + + + + + + + + + + + + + ArrayInitializerExpression.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CSharpIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CSharpIndentEngine.cs new file mode 100644 index 000000000..478bc45c6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CSharpIndentEngine.cs @@ -0,0 +1,557 @@ +// +// CSharpIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Indentation engine based on a state machine. + /// Supports only pushing new chars to the end. + /// + /// + /// Represents the context for transitions between . + /// Delegates the responsibility for pushing a new char to the current + /// state and changes between states depending on the pushed chars. + /// + public class CSharpIndentEngine : IStateMachineIndentEngine + { + #region Properties + + /// + /// Formatting options. + /// + internal readonly CSharpFormattingOptions formattingOptions; + + /// + /// Text editor options. + /// + internal readonly TextEditorOptions textEditorOptions; + + /// + /// A readonly reference to the document that's parsed + /// by the engine. + /// + internal readonly IDocument document; + + /// + /// Represents the new line character. + /// + internal readonly char newLineChar; + + /// + /// The current indentation state. + /// + internal IndentState currentState; + + /// + /// Stores conditional symbols of #define directives. + /// + internal HashSet conditionalSymbols; + + /// + /// Stores custom conditional symbols. + /// + internal HashSet customConditionalSymbols; + + /// + /// Stores the results of evaluations of the preprocessor if/elif directives + /// in the current block (between #if and #endif). + /// + internal CloneableStack ifDirectiveEvalResults = new CloneableStack (); + + /// + /// Stores the indentation levels of the if directives in the current block. + /// + internal CloneableStack ifDirectiveIndents = new CloneableStack(); + + /// + /// Stores the last sequence of characters that can form a + /// valid keyword or variable name. + /// + internal StringBuilder wordToken; + + /// + /// Stores the previous sequence of chars that formed a + /// valid keyword or variable name. + /// + internal string previousKeyword; + + #endregion + + #region IDocumentIndentEngine + + /// + public IDocument Document + { + get { return document; } + } + + /// + public string ThisLineIndent + { + get + { + // OPTION: IndentBlankLines + // remove the indentation of this line if isLineStart is true +// if (!textEditorOptions.IndentBlankLines && isLineStart) +// { +// return string.Empty; +// } + + return currentState.ThisLineIndent.IndentString; + } + } + + /// + public string NextLineIndent + { + get + { + return currentState.NextLineIndent.IndentString; + } + } + + /// + public string CurrentIndent + { + get + { + return currentIndent.ToString(); + } + } + + /// + /// + /// This is set depending on the current and + /// can change its value until the char is + /// pushed. If this is true, that doesn't necessarily mean that the + /// current line has an incorrect indent (this can be determined + /// only at the end of the current line). + /// + public bool NeedsReindent + { + get + { + // return true if it's the first column of the line and it has an indent + if (Location.Column == 1) + { + return ThisLineIndent.Length > 0; + } + + // ignore incorrect indentations when there's only ws on this line + if (isLineStart) + { + return false; + } + + return ThisLineIndent != CurrentIndent.ToString(); + } + } + + /// + public int Offset + { + get + { + return offset; + } + } + + /// + public TextLocation Location + { + get + { + return new TextLocation(line, column); + } + } + + /// + public bool EnableCustomIndentLevels + { + get; + set; + } + + #endregion + + #region Fields + + /// + /// Represents the number of pushed chars. + /// + internal int offset = 0; + + /// + /// The current line number. + /// + internal int line = 1; + + /// + /// The current column number. + /// + /// + /// One char can take up multiple columns (e.g. \t). + /// + internal int column = 1; + + /// + /// True if is true for all + /// chars at the current line. + /// + internal bool isLineStart = true; + + /// + /// True if was true before the current + /// . + /// + internal bool isLineStartBeforeWordToken = true; + + /// + /// Current char that's being pushed. + /// + internal char currentChar = '\0'; + + /// + /// Last non-whitespace char that has been pushed. + /// + internal char previousChar = '\0'; + + /// + /// Previous new line char + /// + internal char previousNewline = '\0'; + + /// + /// Current indent level on this line. + /// + internal StringBuilder currentIndent = new StringBuilder(); + + /// + /// True if this line began in . + /// + internal bool lineBeganInsideVerbatimString = false; + + /// + /// True if this line began in . + /// + internal bool lineBeganInsideMultiLineComment = false; + + #endregion + + #region Constructors + + /// + /// Creates a new CSharpIndentEngine instance. + /// + /// + /// An instance of which is being parsed. + /// + /// + /// C# formatting options. + /// + /// + /// Text editor options for indentation. + /// + public CSharpIndentEngine(IDocument document, TextEditorOptions textEditorOptions, CSharpFormattingOptions formattingOptions) + { + this.formattingOptions = formattingOptions; + this.textEditorOptions = textEditorOptions; + this.document = document; + + this.currentState = new GlobalBodyState(this); + + this.conditionalSymbols = new HashSet(); + this.customConditionalSymbols = new HashSet(); + this.wordToken = new StringBuilder(); + this.previousKeyword = string.Empty; + this.newLineChar = textEditorOptions.EolMarker[0]; + } + + /// + /// Creates a new CSharpIndentEngine instance from the given prototype. + /// + /// + /// An CSharpIndentEngine instance. + /// + public CSharpIndentEngine(CSharpIndentEngine prototype) + { + this.formattingOptions = prototype.formattingOptions; + this.textEditorOptions = prototype.textEditorOptions; + this.document = prototype.document; + + this.newLineChar = prototype.newLineChar; + this.currentState = prototype.currentState.Clone(this); + this.conditionalSymbols = new HashSet(prototype.conditionalSymbols); + this.customConditionalSymbols = new HashSet(prototype.customConditionalSymbols); + + this.wordToken = new StringBuilder(prototype.wordToken.ToString()); + this.previousKeyword = string.Copy(prototype.previousKeyword); + + this.offset = prototype.offset; + this.line = prototype.line; + this.column = prototype.column; + this.isLineStart = prototype.isLineStart; + this.isLineStartBeforeWordToken = prototype.isLineStartBeforeWordToken; + this.currentChar = prototype.currentChar; + this.previousChar = prototype.previousChar; + this.previousNewline = prototype.previousNewline; + this.currentIndent = new StringBuilder(prototype.CurrentIndent.ToString()); + this.lineBeganInsideMultiLineComment = prototype.lineBeganInsideMultiLineComment; + this.lineBeganInsideVerbatimString = prototype.lineBeganInsideVerbatimString; + this.ifDirectiveEvalResults = prototype.ifDirectiveEvalResults.Clone(); + this.ifDirectiveIndents = prototype.ifDirectiveIndents.Clone(); + + this.EnableCustomIndentLevels = prototype.EnableCustomIndentLevels; + } + + #endregion + + #region IClonable + + object ICloneable.Clone() + { + return Clone(); + } + + /// + IDocumentIndentEngine IDocumentIndentEngine.Clone() + { + return Clone(); + } + + public IStateMachineIndentEngine Clone() + { + return new CSharpIndentEngine(this); + } + + #endregion + + #region Methods + + /// + public void Push(char ch) + { + // append this char to the wordbuf if it can form a valid keyword, otherwise check + // if the last sequence of chars form a valid keyword and reset the wordbuf. + if ((wordToken.Length == 0 ? char.IsLetter(ch) : char.IsLetterOrDigit(ch)) || ch == '_') + { + wordToken.Append(ch); + } + else if (wordToken.Length > 0) + { + currentState.CheckKeyword(wordToken.ToString()); + previousKeyword = wordToken.ToString(); + wordToken.Length = 0; + isLineStartBeforeWordToken = false; + } + + var isNewLine = NewLine.IsNewLine(ch); + if (!isNewLine) { + currentState.Push(currentChar = ch); + offset++; + previousNewline = '\0'; + // ignore whitespace and newline chars + var isWhitespace = currentChar == ' ' || currentChar == '\t'; + if (!isWhitespace) + { + previousChar = currentChar; + isLineStart = false; + } + + if (isLineStart) + { + currentIndent.Append(ch); + } + + if (ch == '\t') + { + var nextTabStop = (column - 1 + textEditorOptions.IndentSize) / textEditorOptions.IndentSize; + column = 1 + nextTabStop * textEditorOptions.IndentSize; + } + else + { + column++; + } + } else { + if (ch == NewLine.LF && previousNewline == NewLine.CR) { + offset++; + return; + } + currentState.Push(currentChar = newLineChar); + offset++; + + previousNewline = ch; + // there can be more than one chars that determine the EOL, + // the engine uses only one of them defined with newLineChar + if (currentChar != newLineChar) + { + return; + } + currentIndent.Length = 0; + isLineStart = true; + isLineStartBeforeWordToken = true; + column = 1; + line++; + + lineBeganInsideMultiLineComment = IsInsideMultiLineComment; + lineBeganInsideVerbatimString = IsInsideVerbatimString; + } + } + + /// + public void Reset() + { + currentState = new GlobalBodyState(this); + conditionalSymbols.Clear(); + ifDirectiveEvalResults.Clear(); + ifDirectiveIndents.Clear(); + + offset = 0; + line = 1; + column = 1; + isLineStart = true; + currentChar = '\0'; + previousChar = '\0'; + currentIndent.Length = 0; + lineBeganInsideMultiLineComment = false; + lineBeganInsideVerbatimString = false; + } + + /// + public void Update(int offset) + { + if (Offset > offset) + { + Reset(); + } + + while (Offset < offset) + { + Push(Document.GetCharAt(Offset)); + } + } + + /// + /// Defines the conditional symbol. + /// + /// The symbol to define. + public void DefineSymbol(string defineSymbol) + { + if (!customConditionalSymbols.Contains(defineSymbol)) + customConditionalSymbols.Add(defineSymbol); + } + + /// + /// Removes the symbol. + /// + /// The symbol to undefine. + public void RemoveSymbol(string undefineSymbol) + { + if (customConditionalSymbols.Contains(undefineSymbol)) + customConditionalSymbols.Remove(undefineSymbol); + } + #endregion + + #region IStateMachineIndentEngine + + public bool IsInsidePreprocessorDirective + { + get { return currentState is PreProcessorState; } + } + + public bool IsInsidePreprocessorComment + { + get { return currentState is PreProcessorCommentState; } + } + + public bool IsInsideStringLiteral + { + get { return currentState is StringLiteralState; } + } + + public bool IsInsideVerbatimString + { + get { return currentState is VerbatimStringState; } + } + + public bool IsInsideCharacter + { + get { return currentState is CharacterState; } + } + + public bool IsInsideString + { + get { return IsInsideStringLiteral || IsInsideVerbatimString || IsInsideCharacter; } + } + + public bool IsInsideLineComment + { + get { return currentState is LineCommentState; } + } + + public bool IsInsideMultiLineComment + { + get { return currentState is MultiLineCommentState; } + } + + public bool IsInsideDocLineComment + { + get { return currentState is DocCommentState; } + } + + public bool IsInsideComment + { + get { return IsInsideLineComment || IsInsideMultiLineComment || IsInsideDocLineComment; } + } + + public bool IsInsideOrdinaryComment + { + get { return IsInsideLineComment || IsInsideMultiLineComment; } + } + + public bool IsInsideOrdinaryCommentOrString + { + get { return IsInsideOrdinaryComment || IsInsideString; } + } + + public bool LineBeganInsideVerbatimString + { + get { return lineBeganInsideVerbatimString; } + } + + public bool LineBeganInsideMultiLineComment + { + get { return lineBeganInsideMultiLineComment; } + } + + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CacheIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CacheIndentEngine.cs new file mode 100644 index 000000000..b19f38868 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CacheIndentEngine.cs @@ -0,0 +1,627 @@ +// +// CacheIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a decorator of an IStateMachineIndentEngine instance that provides + /// logic for reseting and updating the engine on text changed events. + /// + /// + /// The decorator is based on periodical caching of the engine's state and + /// delegating all logic behind indentation to the currently active engine. + /// + public class CacheIndentEngine : IStateMachineIndentEngine + { + + #region Properties + + IStateMachineIndentEngine currentEngine; + Stack cachedEngines = new Stack(); + + #endregion + + #region Constructors + + /// + /// Creates a new CacheIndentEngine instance. + /// + /// + /// An instance of to which the + /// logic for indentation will be delegated. + /// + /// + /// The number of chars between caching. + /// + public CacheIndentEngine(IStateMachineIndentEngine decoratedEngine, int cacheRate = 2000) + { + this.currentEngine = decoratedEngine; + } + + /// + /// Creates a new CacheIndentEngine instance from the given prototype. + /// + /// + /// A CacheIndentEngine instance. + /// + public CacheIndentEngine(CacheIndentEngine prototype) + { + this.currentEngine = prototype.currentEngine.Clone(); + } + + #endregion + + #region IDocumentIndentEngine + + /// + public IDocument Document { + get { return currentEngine.Document; } + } + + /// + public string ThisLineIndent { + get { return currentEngine.ThisLineIndent; } + } + + /// + public string NextLineIndent { + get { return currentEngine.NextLineIndent; } + } + + /// + public string CurrentIndent { + get { return currentEngine.CurrentIndent; } + } + + /// + public bool NeedsReindent { + get { return currentEngine.NeedsReindent; } + } + + /// + public int Offset { + get { return currentEngine.Offset; } + } + + /// + public TextLocation Location { + get { return currentEngine.Location; } + } + + /// + public bool EnableCustomIndentLevels + { + get { return currentEngine.EnableCustomIndentLevels; } + set { currentEngine.EnableCustomIndentLevels = value; } + } + + /// + public void Push(char ch) + { + currentEngine.Push(ch); + } + + /// + public void Reset() + { + currentEngine.Reset(); + cachedEngines.Clear(); + } + + /// + /// Resets the engine to offset. Clears all cached engines after the given offset. + /// + public void ResetEngineToPosition(int offset) + { + // We are already there + if (currentEngine.Offset <= offset) + return; + + bool gotCachedEngine = false; + while (cachedEngines.Count > 0) { + var topEngine = cachedEngines.Peek(); + if (topEngine.Offset <= offset) { + currentEngine = topEngine.Clone(); + gotCachedEngine = true; + break; + } else { + cachedEngines.Pop(); + } + } + if (!gotCachedEngine) + currentEngine.Reset(); + } + + /// + /// + /// If the is negative, the engine will + /// update to: document.TextLength + (offset % document.TextLength+1) + /// Otherwise it will update to: offset % document.TextLength+1 + /// + public void Update(int position) + { + const int BUFFER_SIZE = 2000; + + if (currentEngine.Offset == position) { + //positions match, nothing to be done + return; + } else if (currentEngine.Offset > position) { + //moving backwards, so reset from previous saved location + ResetEngineToPosition(position); + } + + // get the engine caught up + int nextSave = (cachedEngines.Count == 0) ? BUFFER_SIZE : cachedEngines.Peek().Offset + BUFFER_SIZE; + if (currentEngine.Offset + 1 == position) { + char ch = currentEngine.Document.GetCharAt(currentEngine.Offset); + currentEngine.Push(ch); + if (currentEngine.Offset == nextSave) + cachedEngines.Push(currentEngine.Clone()); + } else { + //bulk copy characters in case buffer is unmanaged + //(faster if we reduce managed/unmanaged transitions) + while (currentEngine.Offset < position) { + int endCut = currentEngine.Offset + BUFFER_SIZE; + if (endCut > position) + endCut = position; + string buffer = currentEngine.Document.GetText(currentEngine.Offset, endCut - currentEngine.Offset); + foreach (char ch in buffer) { + currentEngine.Push(ch); + //ConsoleWrite ("pushing character '{0}'", ch); + if (currentEngine.Offset == nextSave) { + cachedEngines.Push(currentEngine.Clone()); + nextSave += BUFFER_SIZE; + } + } + } + } + } + + public IStateMachineIndentEngine GetEngine(int offset) + { + ResetEngineToPosition(offset); + return currentEngine; + } + + #endregion + + #region IClonable + + /// + public IStateMachineIndentEngine Clone() + { + return new CacheIndentEngine(this); + } + + /// + IDocumentIndentEngine IDocumentIndentEngine.Clone() + { + return Clone(); + } + + object ICloneable.Clone() + { + return Clone(); + } + + #endregion + + #region IStateMachineIndentEngine + + public bool IsInsidePreprocessorDirective { + get { return currentEngine.IsInsidePreprocessorDirective; } + } + + public bool IsInsidePreprocessorComment { + get { return currentEngine.IsInsidePreprocessorComment; } + } + + public bool IsInsideStringLiteral { + get { return currentEngine.IsInsideStringLiteral; } + } + + public bool IsInsideVerbatimString { + get { return currentEngine.IsInsideVerbatimString; } + } + + public bool IsInsideCharacter { + get { return currentEngine.IsInsideCharacter; } + } + + public bool IsInsideString { + get { return currentEngine.IsInsideString; } + } + + public bool IsInsideLineComment { + get { return currentEngine.IsInsideLineComment; } + } + + public bool IsInsideMultiLineComment { + get { return currentEngine.IsInsideMultiLineComment; } + } + + public bool IsInsideDocLineComment { + get { return currentEngine.IsInsideDocLineComment; } + } + + public bool IsInsideComment { + get { return currentEngine.IsInsideComment; } + } + + public bool IsInsideOrdinaryComment { + get { return currentEngine.IsInsideOrdinaryComment; } + } + + public bool IsInsideOrdinaryCommentOrString { + get { return currentEngine.IsInsideOrdinaryCommentOrString; } + } + + public bool LineBeganInsideVerbatimString { + get { return currentEngine.LineBeganInsideVerbatimString; } + } + + public bool LineBeganInsideMultiLineComment { + get { return currentEngine.LineBeganInsideMultiLineComment; } + } + + #endregion + + } + /* +/ // + /// Represents a decorator of an IStateMachineIndentEngine instance that provides + /// logic for reseting and updating the engine on text changed events. + /// + /// + /// The decorator is based on periodical caching of the engine's state and + /// delegating all logic behind indentation to the currently active engine. + /// + public class CacheIndentEngine : IStateMachineIndentEngine + { + #region Properties + + /// + /// Represents the cache interval in number of chars pushed to the engine. + /// + /// + /// When this many new chars are pushed to the engine, the currently active + /// engine gets cloned and added to the end of . + /// + readonly int cacheRate; + + /// + /// Determines how much memory to reserve on initialization for the + /// cached engines. + /// + const int cacheCapacity = 25; + + /// + /// Currently active engine. + /// + /// + /// Should be equal to the last engine in . + /// + IStateMachineIndentEngine currentEngine; + + /// + /// List of cached engines sorted ascending by + /// . + /// + IStateMachineIndentEngine[] cachedEngines; + + /// + /// The index of the last cached engine in cachedEngines. + /// + /// + /// Should be equal to: currentEngine.Offset / CacheRate + /// + int lastCachedEngine; + + #endregion + + #region Constructors + + /// + /// Creates a new CacheIndentEngine instance. + /// + /// + /// An instance of to which the + /// logic for indentation will be delegated. + /// + /// + /// The number of chars between caching. + /// + public CacheIndentEngine(IStateMachineIndentEngine decoratedEngine, int cacheRate = 2000) + { + this.cachedEngines = new IStateMachineIndentEngine[cacheCapacity]; + + this.cachedEngines[0] = decoratedEngine.Clone(); + this.currentEngine = this.cachedEngines[0].Clone(); + this.cacheRate = cacheRate; + } + + /// + /// Creates a new CacheIndentEngine instance from the given prototype. + /// + /// + /// A CacheIndentEngine instance. + /// + public CacheIndentEngine(CacheIndentEngine prototype) + { + this.cachedEngines = new IStateMachineIndentEngine[prototype.cachedEngines.Length]; + Array.Copy(prototype.cachedEngines, this.cachedEngines, prototype.cachedEngines.Length); + + this.lastCachedEngine = prototype.lastCachedEngine; + this.currentEngine = prototype.currentEngine.Clone(); + this.cacheRate = prototype.cacheRate; + } + + #endregion + + #region Methods + + /// + /// Performs caching of the . + /// + void cache() + { + if (currentEngine.Offset % cacheRate != 0) + { + throw new Exception("The current engine's offset is not divisable with the cacheRate."); + } + + // determine the new current engine from cachedEngines + lastCachedEngine = currentEngine.Offset / cacheRate; + + if (cachedEngines.Length < lastCachedEngine + 1) + { + Array.Resize(ref cachedEngines, lastCachedEngine * 2); + } + + cachedEngines[lastCachedEngine] = currentEngine.Clone(); + } + + #endregion + + #region IDocumentIndentEngine + + /// + public IDocument Document + { + get { return currentEngine.Document; } + } + + /// + public string ThisLineIndent + { + get { return currentEngine.ThisLineIndent; } + } + + /// + public string NextLineIndent + { + get { return currentEngine.NextLineIndent; } + } + + /// + public string CurrentIndent + { + get { return currentEngine.CurrentIndent; } + } + + /// + public bool NeedsReindent + { + get { return currentEngine.NeedsReindent; } + } + + /// + public int Offset + { + get { return currentEngine.Offset; } + } + + /// + public TextLocation Location + { + get { return currentEngine.Location; } + } + + /// + public void Push(char ch) + { + currentEngine.Push(ch); + + if (currentEngine.Offset % cacheRate == 0) + { + cache(); + } + } + + /// + public void Reset() + { + currentEngine = cachedEngines[lastCachedEngine = 0]; + } + + /// + /// + /// If the is negative, the engine will + /// update to: document.TextLength + (offset % document.TextLength+1) + /// Otherwise it will update to: offset % document.TextLength+1 + /// + public void Update(int offset) + { + // map the given offset to the [0, document.TextLength] interval + // using modulo arithmetics + offset %= Document.TextLength + 1; + if (offset < 0) + { + offset += Document.TextLength + 1; + } + + // check if the engine has to be updated to some previous offset + if (currentEngine.Offset > offset) + { + // replace the currentEngine with the first one whose offset + // is less then the given + lastCachedEngine = offset / cacheRate; + currentEngine = cachedEngines[lastCachedEngine].Clone(); + } + + // update the engine to the given offset + while (Offset < offset) + { + Push(Document.GetCharAt(Offset)); + } + } + + public IStateMachineIndentEngine GetEngine(int offset) + { + // map the given offset to the [0, document.TextLength] interval + // using modulo arithmetics + offset %= Document.TextLength + 1; + if (offset < 0) + { + offset += Document.TextLength + 1; + } + + // check if the engine has to be updated to some previous offset + if (currentEngine.Offset > offset) + { + // replace the currentEngine with the first one whose offset + // is less then the given + lastCachedEngine = offset / cacheRate; + return cachedEngines[lastCachedEngine].Clone(); + } + + return currentEngine; + } + + #endregion + + #region IClonable + + /// + public IStateMachineIndentEngine Clone() + { + return new CacheIndentEngine(this); + } + + /// + IDocumentIndentEngine IDocumentIndentEngine.Clone() + { + return Clone(); + } + + object ICloneable.Clone() + { + return Clone(); + } + + #endregion + + #region IStateMachineIndentEngine + + public bool IsInsidePreprocessorDirective + { + get { return currentEngine.IsInsidePreprocessorDirective; } + } + + public bool IsInsidePreprocessorComment + { + get { return currentEngine.IsInsidePreprocessorComment; } + } + + public bool IsInsideStringLiteral + { + get { return currentEngine.IsInsideStringLiteral; } + } + + public bool IsInsideVerbatimString + { + get { return currentEngine.IsInsideVerbatimString; } + } + + public bool IsInsideCharacter + { + get { return currentEngine.IsInsideCharacter; } + } + + public bool IsInsideString + { + get { return currentEngine.IsInsideString; } + } + + public bool IsInsideLineComment + { + get { return currentEngine.IsInsideLineComment; } + } + + public bool IsInsideMultiLineComment + { + get { return currentEngine.IsInsideMultiLineComment; } + } + + public bool IsInsideDocLineComment + { + get { return currentEngine.IsInsideDocLineComment; } + } + + public bool IsInsideComment + { + get { return currentEngine.IsInsideComment; } + } + + public bool IsInsideOrdinaryComment + { + get { return currentEngine.IsInsideOrdinaryComment; } + } + + public bool IsInsideOrdinaryCommentOrString + { + get { return currentEngine.IsInsideOrdinaryCommentOrString; } + } + + public bool LineBeganInsideVerbatimString + { + get { return currentEngine.LineBeganInsideVerbatimString; } + } + + public bool LineBeganInsideMultiLineComment + { + get { return currentEngine.LineBeganInsideMultiLineComment; } + } + + #endregion + } + + */ +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IDocumentIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IDocumentIndentEngine.cs new file mode 100644 index 000000000..821aa0bc8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IDocumentIndentEngine.cs @@ -0,0 +1,108 @@ +// +// IDocumentIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// The base interface for all indent engines. + /// + public interface IDocumentIndentEngine : ICloneable + { + /// + /// A reference to the document that's parsed by the engine. + /// + IDocument Document { get; } + + /// + /// The indentation string of the current line. + /// + string ThisLineIndent { get; } + + /// + /// The indentation string of the next line. + /// + string NextLineIndent { get; } + + /// + /// The indent string on the beginning of the current line. + /// + string CurrentIndent { get; } + + /// + /// True if the current line needs to be reindented. + /// + bool NeedsReindent { get; } + + /// + /// The current offset of the engine. + /// + int Offset { get; } + + /// + /// The current location of the engine. + /// + TextLocation Location { get; } + + /// + /// If this is true, the engine should try to adjust its indent + /// levels to manual user's corrections, even if they are wrong. + /// + bool EnableCustomIndentLevels { get; set; } + + /// + /// Pushes a new char into the engine which calculates the new + /// indentation levels. + /// + /// + /// A new character. + /// + void Push(char ch); + + /// + /// Resets the engine. + /// + void Reset(); + + /// + /// Updates the engine to the given offset. + /// + /// + /// Valid offset in . + /// + void Update(int offset); + + /// + /// Clones the engine and preserves the current state. + /// + /// + /// An indentical clone which can operate without interference + /// with this engine. + /// + new IDocumentIndentEngine Clone(); + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IStateMachineIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IStateMachineIndentEngine.cs new file mode 100644 index 000000000..44c3949cf --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IStateMachineIndentEngine.cs @@ -0,0 +1,60 @@ +// +// IStateMachineIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com) +// +// 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. +namespace ICSharpCode.NRefactory.CSharp +{ + public interface IStateMachineIndentEngine : IDocumentIndentEngine + { + bool IsInsidePreprocessorDirective { get; } + + bool IsInsidePreprocessorComment { get; } + + bool IsInsideStringLiteral { get; } + + bool IsInsideVerbatimString { get; } + + bool IsInsideCharacter { get; } + + bool IsInsideString { get; } + + bool IsInsideLineComment { get; } + + bool IsInsideMultiLineComment { get; } + + bool IsInsideDocLineComment { get; } + + bool IsInsideComment { get; } + + bool IsInsideOrdinaryComment { get; } + + bool IsInsideOrdinaryCommentOrString { get; } + + bool LineBeganInsideVerbatimString { get; } + + bool LineBeganInsideMultiLineComment { get; } + + new IStateMachineIndentEngine Clone(); + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IndentState.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IndentState.cs new file mode 100644 index 000000000..8867268e6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IndentState.cs @@ -0,0 +1,2017 @@ +// +// IndentState.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com) +// +// 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.Globalization; +using System.Linq; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + #region IndentState + + /// + /// The base class for all indentation states. + /// Each state defines the logic for indentation based on chars that + /// are pushed to it. + /// + public abstract class IndentState : ICloneable + { + #region Properties + + /// + /// The indentation engine using this state. + /// + public CSharpIndentEngine Engine; + + /// + /// The parent state. + /// This state can use the indentation levels of its parent. + /// When this state exits, the engine returns to the parent. + /// + public IndentState Parent; + + /// + /// The indentation of the current line. + /// This is set when the state is created and will be changed to + /// when the + /// is pushed. + /// + public Indent ThisLineIndent; + + /// + /// The indentation of the next line. + /// This is set when the state is created and can change depending + /// on the pushed chars. + /// + public Indent NextLineIndent; + + #endregion + + #region Constructors + + protected IndentState() + { + } + + /// + /// Creates a new indentation state that is a copy of the given + /// prototype. + /// + /// + /// The prototype state. + /// + /// + /// The engine of the new state. + /// + protected IndentState(IndentState prototype, CSharpIndentEngine engine) + { + Engine = engine; + Parent = prototype.Parent != null ? prototype.Parent.Clone(engine) : null; + + ThisLineIndent = prototype.ThisLineIndent.Clone(); + NextLineIndent = prototype.NextLineIndent.Clone(); + } + + #endregion + + #region IClonable + + object ICloneable.Clone() + { + return Clone(Engine); + } + + public abstract IndentState Clone(CSharpIndentEngine engine); + + #endregion + + #region Methods + + internal void Initialize (CSharpIndentEngine engine, IndentState parent = null) + { + Parent = parent; + Engine = engine; + + InitializeState(); + } + + /// + /// Initializes the state: + /// - sets the default indentation levels. + /// + /// + /// Each state can override this method if it needs a different + /// logic for setting up the default indentations. + /// + public virtual void InitializeState() + { + ThisLineIndent = new Indent(Engine.textEditorOptions); + NextLineIndent = ThisLineIndent.Clone(); + } + + /// + /// Actions performed when this state exits. + /// + public virtual void OnExit() + { + if (Parent != null) + { + // if a state exits on the newline character, it has to push + // it back to its parent (and so on recursively if the parent + // state also exits). Otherwise, the parent state wouldn't + // know that the engine isn't on the same line anymore. + if (Engine.currentChar == Engine.newLineChar) + { + Parent.Push(Engine.newLineChar); + } + + // when a state exits the engine stays on the same line and this + // state has to override the Parent.ThisLineIndent. + Parent.ThisLineIndent = ThisLineIndent.Clone(); + } + } + + /// + /// Changes the current state of the using the current + /// state as the parent for the new one. + /// + /// + /// The type of the new state. Must be assignable from . + /// + public void ChangeState() + where T : IndentState, new () + { + var t = new T(); + t.Initialize(Engine, Engine.currentState); + Engine.currentState = t; + } + + /// + /// Exits this state by setting the current state of the + /// to this state's parent. + /// + public void ExitState() + { + OnExit(); + Engine.currentState = Engine.currentState.Parent ?? new GlobalBodyState(Engine); + } + + /// + /// Common logic behind the push method. + /// Each state can override this method and implement its own logic. + /// + /// + /// The current character that's being pushed. + /// + public virtual void Push(char ch) + { + // replace ThisLineIndent with NextLineIndent if the newLineChar is pushed + if (ch == Engine.newLineChar) + { + var delta = Engine.textEditorOptions.ContinuationIndent; + while (NextLineIndent.CurIndent - ThisLineIndent.CurIndent > delta && + NextLineIndent.PopIf(IndentType.Continuation)) ; + ThisLineIndent = NextLineIndent.Clone(); + } + } + + /// + /// When derived, checks if the given sequence of chars form + /// a valid keyword or variable name, depending on the state. + /// + /// + /// A possible keyword. + /// + public virtual void CheckKeyword(string keyword) + { } + + /// + /// When derived, checks if the given sequence of chars form + /// a valid keyword or variable name, depending on the state. + /// + /// + /// A possible keyword. + /// + /// + /// This method should be called from . + /// It is left to derived classes to call this method because of + /// performance issues. + /// + public virtual void CheckKeywordOnPush(string keyword) + { } + + #endregion + } + + #endregion + + #region Null state + + /// + /// Null state. + /// + /// + /// Doesn't define any transitions to new states. + /// + public class NullState : IndentState + { + public NullState() + { } + + public NullState(NullState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new NullState(this, engine); + } + } + + #endregion + + #region Brackets body states + + #region Brackets body base + + /// + /// The base for all brackets body states. + /// + /// + /// Represents a block of code between a pair of brackets. + /// + public abstract class BracketsBodyBaseState : IndentState + { + + /// + /// When derived in a concrete bracket body state, represents + /// the closed bracket character pair. + /// + public abstract char ClosedBracket { get; } + + protected BracketsBodyBaseState() + { } + + protected BracketsBodyBaseState(BracketsBodyBaseState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { + base.Push(ch); + switch (ch) { + case '#': + if (Engine.isLineStart) + ChangeState(); + break; + case '/': + if (Engine.previousChar == '/') + ChangeState(); + break; + case '*': + if (Engine.previousChar == '/') + ChangeState(); + break; + case '"': + if (Engine.previousChar == '@') + { + ChangeState(); + } + else + { + ChangeState(); + } + break; + case '\'': + ChangeState(); + break; + case '{': + ChangeState(); + break; + case '(': + ChangeState(); + break; + case '[': + ChangeState(); + break; + default: + if (ch == ClosedBracket) + ExitState(); + break; + } + } + } + + #endregion + + #region Braces body state + + /// + /// Braces body state. + /// + /// + /// Represents a block of code between { and }. + /// + public class BracesBodyState : BracketsBodyBaseState + { + /// + /// Type of the current block body. + /// + public Body CurrentBody; + + /// + /// Type of the next block body. + /// Same as if none of the + /// keywords have been read. + /// + public Body NextBody; + + /// + /// Type of the current statement. + /// + public Statement CurrentStatement + { + get + { + return currentStatement; + } + set + { + // clear NestedIfStatementLevels if this statement breaks the sequence + if (currentStatement == Statement.None && value != Statement.Else) + { + NestedIfStatementLevels.Clear(); + } + + currentStatement = value; + } + } + Statement currentStatement; + + /// + /// Contains indent levels of nested if statements. + /// + internal CloneableStack NestedIfStatementLevels = new CloneableStack(); + + /// + /// Contains the indent level of the last statement or body keyword. + /// + public Indent LastBlockIndent; + + /// + /// True if the engine is on the right side of the equal operator '='. + /// + public bool IsRightHandExpression; + + /// + /// True if the '=' char has been pushed and it's not + /// a part of a relational operator (>=, <=, !=, ==). + /// + public bool IsEqualCharPushed; + + /// + /// The indentation of the previous line. + /// + public int PreviousLineIndent; + + /// + /// True if the dot member (e.g. method invocation) indentation has + /// been handled in the current statement. + /// + public bool IsMemberReferenceDotHandled; + + public override char ClosedBracket + { + get { return '}'; } + } + + public BracesBodyState() + { + } + + public BracesBodyState(BracesBodyState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + CurrentBody = prototype.CurrentBody; + NextBody = prototype.NextBody; + CurrentStatement = prototype.CurrentStatement; + NestedIfStatementLevels = prototype.NestedIfStatementLevels.Clone(); + IsRightHandExpression = prototype.IsRightHandExpression; + IsEqualCharPushed = prototype.IsEqualCharPushed; + IsMemberReferenceDotHandled = prototype.IsMemberReferenceDotHandled; + LastBlockIndent = prototype.LastBlockIndent; + PreviousLineIndent = prototype.PreviousLineIndent; + } + + public override void Push(char ch) + { + // handle IsRightHandExpression property + if (IsEqualCharPushed) + { + if (IsRightHandExpression) + { + if (ch == Engine.newLineChar) + { + NextLineIndent.RemoveAlignment(); + NextLineIndent.Push(IndentType.Continuation); + } + } + // ignore "==" and "=>" operators + else if (ch != '=' && ch != '>') + { + IsRightHandExpression = true; + + if (ch == Engine.newLineChar) + { + NextLineIndent.Push(IndentType.Continuation); + } + else + { + NextLineIndent.SetAlignment(Engine.column - NextLineIndent.CurIndent); + } + } + + IsEqualCharPushed = ch == ' ' || ch == '\t'; + } + + if (ch == ';' || (ch == ',' && IsRightHandExpression)) + { + OnStatementExit(); + } + else if (ch == '=' && !(Engine.previousChar == '=' || Engine.previousChar == '<' || Engine.previousChar == '>' || Engine.previousChar == '!')) + { + IsEqualCharPushed = true; + } + else if (ch == '.' && !IsMemberReferenceDotHandled) + { + // OPTION: CSharpFormattingOptions.AlignToMemberReferenceDot + if (Engine.formattingOptions.AlignToMemberReferenceDot && !Engine.isLineStart) + { + IsMemberReferenceDotHandled = true; + NextLineIndent.RemoveAlignment(); + NextLineIndent.SetAlignment(Engine.column - NextLineIndent.CurIndent - 1, true); + } + else if (Engine.isLineStart) + { + IsMemberReferenceDotHandled = true; + + ThisLineIndent.RemoveAlignment(); + while (ThisLineIndent.CurIndent > PreviousLineIndent && + ThisLineIndent.PopIf(IndentType.Continuation)) ; + ThisLineIndent.Push(IndentType.Continuation); + NextLineIndent = ThisLineIndent.Clone(); + } + } + else if (ch == ':' && Engine.isLineStart && !IsRightHandExpression) + { + // try to capture ': base(...)', ': this(...)' and inherit statements when they are on a new line + ThisLineIndent.Push(IndentType.Continuation); + } + else if (ch == Engine.newLineChar) + { + PreviousLineIndent = ThisLineIndent.CurIndent; + } + + if (Engine.wordToken.ToString() == "else") + { + CheckKeywordOnPush("else"); + } + + base.Push(ch); + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + + // OPTION: IDocumentIndentEngine.EnableCustomIndentLevels + var parent = Parent as BracesBodyState; + if (parent == null || parent.LastBlockIndent == null || !Engine.EnableCustomIndentLevels) + { + NextLineIndent.RemoveAlignment(); + NextLineIndent.PopIf(IndentType.Continuation); + } + else + { + NextLineIndent = parent.LastBlockIndent.Clone(); + } + + if (Engine.isLineStart) + { + ThisLineIndent = NextLineIndent.Clone(); + } + + CurrentBody = extractBody(Parent); + NextBody = Body.None; + CurrentStatement = Statement.None; + + AddIndentation(CurrentBody); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new BracesBodyState(this, engine); + } + + public override void OnExit() + { + if (Parent is BracesBodyState && !((BracesBodyState)Parent).IsRightHandExpression) + { + ((BracesBodyState)Parent).OnStatementExit(); + } + + if (Engine.isLineStart) + { + ThisLineIndent.RemoveAlignment(); + ThisLineIndent.PopTry(); + BraceStyle style; + if (TryGetBraceStyle(this.CurrentBody, out style)) { + if (style == BraceStyle.NextLineShifted || + style == BraceStyle.NextLineShifted2|| + style == BraceStyle.BannerStyle) { + ThisLineIndent.Push(IndentType.Block); + } + } + } + + base.OnExit(); + } + + /// + /// Actions performed when the current statement exits. + /// + public virtual void OnStatementExit() + { + IsRightHandExpression = false; + IsMemberReferenceDotHandled = false; + + NextLineIndent.RemoveAlignment(); + NextLineIndent.PopWhile(IndentType.Continuation); + + CurrentStatement = Statement.None; + NextBody = Body.None; + LastBlockIndent = null; + } + + #region Helpers + + /// + /// Types of braces bodies. + /// + public enum Body + { + None, + Namespace, + Class, + Struct, + Interface, + Enum, + Switch, + Case, + Try, + Catch, + Finally + } + + /// + /// Types of statements. + /// + public enum Statement + { + None, + If, + Else, + Do, + While, + For, + Foreach, + Lock, + Using, + Return + } + + static readonly Dictionary bodies = new Dictionary + { + { "namespace", Body.Namespace }, + { "class", Body.Class }, + { "struct", Body.Struct }, + { "interface", Body.Interface }, + { "enum", Body.Enum }, + { "switch", Body.Switch }, + { "try", Body.Try }, + { "catch", Body.Catch }, + { "finally", Body.Finally }, + }; + + static readonly Dictionary statements = new Dictionary + { + { "if", Statement.If }, + // { "else", Statement.Else }, // should be handled in CheckKeywordAtPush + { "do", Statement.Do }, + { "while", Statement.While }, + { "for", Statement.For }, + { "foreach", Statement.Foreach }, + { "lock", Statement.Lock }, + { "using", Statement.Using }, + { "return", Statement.Return }, + }; + + static readonly HashSet blocks = new HashSet + { + "namespace", + "class", + "struct", + "interface", + "enum", + "switch", + "try", + "catch", + "finally", + "if", + "else", + "do", + "while", + "for", + "foreach", + "lock", + "using", + }; + + readonly string[] caseDefaultKeywords = { + "case", + "default" + }; + + readonly string[] classStructKeywords = { + "class", + "struct" + }; + + /// + /// Checks if the given string is a keyword and sets the + /// and the + /// variables appropriately. + /// + /// + /// A possible keyword. + /// + /// + /// This method is called from + /// + public override void CheckKeywordOnPush(string keyword) + { + if (keyword == "else") + { + CurrentStatement = Statement.Else; + + // OPTION: CSharpFormattingOptions.AlignElseInIfStatements + if (!Engine.formattingOptions.AlignElseInIfStatements && NestedIfStatementLevels.Count > 0) + { + ThisLineIndent = NestedIfStatementLevels.Pop().Clone(); + NextLineIndent = ThisLineIndent.Clone(); + } + + NextLineIndent.Push(IndentType.Continuation); + } + + if (blocks.Contains(keyword) && Engine.NeedsReindent) + { + LastBlockIndent = Indent.ConvertFrom(Engine.CurrentIndent, ThisLineIndent, Engine.textEditorOptions); + } + } + + /// + /// Checks if the given string is a keyword and sets the + /// and the + /// variables appropriately. + /// + /// + /// A possible keyword. + /// + public override void CheckKeyword(string keyword) + { + if (bodies.ContainsKey(keyword)) + { + var isKeywordTemplateConstraint = + classStructKeywords.Contains(keyword) && + (NextBody == Body.Class || NextBody == Body.Struct || NextBody == Body.Interface); + + if (!isKeywordTemplateConstraint) + { + NextBody = bodies[keyword]; + } + } + else if (caseDefaultKeywords.Contains(keyword) && CurrentBody == Body.Switch && Engine.isLineStartBeforeWordToken) + { + ChangeState(); + } + else if (keyword == "where" && Engine.isLineStartBeforeWordToken) + { + // try to capture where (generic type constraint) + ThisLineIndent.Push(IndentType.Continuation); + } + else if (statements.ContainsKey(keyword)) + { + Statement previousStatement = CurrentStatement; + CurrentStatement = statements[keyword]; + + // return if this is a using declaration or alias + if (CurrentStatement == Statement.Using && + (this is GlobalBodyState || CurrentBody == Body.Namespace)) + { + return; + } + + // OPTION: CSharpFormattingOptions.AlignEmbeddedIfStatements + if (Engine.formattingOptions.AlignEmbeddedStatements && + previousStatement == Statement.If && + CurrentStatement == Statement.If) + { + ThisLineIndent.PopIf(IndentType.Continuation); + NextLineIndent.PopIf(IndentType.Continuation); + } + + // OPTION: CSharpFormattingOptions.AlignEmbeddedStatements + if (Engine.formattingOptions.AlignEmbeddedStatements && + previousStatement == Statement.Lock && + CurrentStatement == Statement.Lock) + { + ThisLineIndent.PopIf(IndentType.Continuation); + NextLineIndent.PopIf(IndentType.Continuation); + } + + // OPTION: CSharpFormattingOptions.AlignEmbeddedUsingStatements + if (Engine.formattingOptions.AlignEmbeddedStatements && + previousStatement == Statement.Using && + CurrentStatement == Statement.Using) + { + ThisLineIndent.PopIf(IndentType.Continuation); + NextLineIndent.PopIf(IndentType.Continuation); + } + + // only add continuation for 'else' in 'else if' statement. + if (!(CurrentStatement == Statement.If && previousStatement == Statement.Else && !Engine.isLineStartBeforeWordToken)) + { + NextLineIndent.Push(IndentType.Continuation); + } + + if (CurrentStatement == Statement.If) + { + NestedIfStatementLevels.Push(ThisLineIndent); + } + } + + if (blocks.Contains(keyword) && Engine.NeedsReindent) + { + LastBlockIndent = Indent.ConvertFrom(Engine.CurrentIndent, ThisLineIndent, Engine.textEditorOptions); + } + } + + /// + /// Pushes a new level of indentation depending on the given + /// . + /// + void AddIndentation(BraceStyle braceStyle) + { + switch (braceStyle) + { + case BraceStyle.NextLineShifted: + ThisLineIndent.Push(IndentType.Block); + NextLineIndent.Push(IndentType.Block); + break; + case BraceStyle.DoNotChange: + case BraceStyle.EndOfLine: + case BraceStyle.EndOfLineWithoutSpace: + case BraceStyle.NextLine: + case BraceStyle.BannerStyle: + NextLineIndent.Push(IndentType.Block); + break; + case BraceStyle.NextLineShifted2: + ThisLineIndent.Push(IndentType.Block); + NextLineIndent.Push(IndentType.DoubleBlock); + break; + } + } + + bool TryGetBraceStyle (Body body, out BraceStyle style) + { + style = BraceStyle.DoNotChange; + switch (body) + { + case Body.None: + if (!Engine.formattingOptions.IndentBlocks) + return false; + style = Engine.formattingOptions.StatementBraceStyle; + return true; + case Body.Namespace: + if (!Engine.formattingOptions.IndentNamespaceBody) + return false; + style = Engine.formattingOptions.NamespaceBraceStyle; + return true; + case Body.Class: + if (!Engine.formattingOptions.IndentClassBody) + return false; + style = Engine.formattingOptions.ClassBraceStyle; + return true; + case Body.Struct: + if (!Engine.formattingOptions.IndentStructBody) + return false; + style = Engine.formattingOptions.StructBraceStyle; + return true; + case Body.Interface: + if (!Engine.formattingOptions.IndentInterfaceBody) + return false; + style = Engine.formattingOptions.InterfaceBraceStyle; + return true; + case Body.Enum: + if (!Engine.formattingOptions.IndentEnumBody) + return false; + style = Engine.formattingOptions.EnumBraceStyle; + return true; + case Body.Switch: + if (!Engine.formattingOptions.IndentSwitchBody) + return false; + style = Engine.formattingOptions.StatementBraceStyle; + return true; + case Body.Try: + case Body.Catch: + case Body.Finally: + style = Engine.formattingOptions.StatementBraceStyle; + return true; + } + return false; + } + + /// + /// Pushes a new level of indentation depending on the given + /// . + /// + void AddIndentation(Body body) + { + var isExpression = Parent is ParenthesesBodyState || Parent is SquareBracketsBodyState || + (Parent is BracesBodyState && ((BracesBodyState)Parent).IsRightHandExpression); + if (isExpression && Engine.formattingOptions.IndentBlocksInsideExpressions && Engine.isLineStart) + { + AddIndentation(BraceStyle.NextLineShifted); + } + + BraceStyle style; + if (TryGetBraceStyle(body, out style)) + { + AddIndentation(style); + } else { + NextLineIndent.Push(IndentType.Empty); + } + } + + /// + /// Extracts the from the given state. + /// + /// + /// The correct type for this state. + /// + static Body extractBody(IndentState state) + { + if (state != null && state is BracesBodyState) + { + return ((BracesBodyState)state).NextBody; + } + + return Body.None; + } + + #endregion + } + + #endregion + + #region Global body state + + /// + /// Global body state. + /// + /// + /// Represents the global space of the program. + /// + public class GlobalBodyState : BracesBodyState + { + public override char ClosedBracket + { + get { return '\0'; } + } + + public GlobalBodyState() + { } + + + public GlobalBodyState(CSharpIndentEngine engine) + { + Initialize (engine, null); + } + + public GlobalBodyState(GlobalBodyState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new GlobalBodyState(this, engine); + } + + public override void InitializeState() + { + ThisLineIndent = new Indent(Engine.textEditorOptions); + NextLineIndent = ThisLineIndent.Clone(); + } + } + + #endregion + + #region Switch-case body state + + /// + /// Switch-case statement state. + /// + /// + /// Represents the block of code in one switch case (including default). + /// + public class SwitchCaseState : BracesBodyState + { + public SwitchCaseState() + { } + + public SwitchCaseState(SwitchCaseState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { + // on ClosedBracket both this state (a case or a default statement) + // and also the whole switch block (handled in the base class) must exit. + if (ch == ClosedBracket) + { + ExitState(); + if (Parent is BracesBodyState) + Parent.OnExit(); + } + + base.Push(ch); + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + + // remove all continuations and extra spaces + ThisLineIndent.RemoveAlignment(); + ThisLineIndent.PopWhile(IndentType.Continuation); + + NextLineIndent.RemoveAlignment(); + NextLineIndent.PopWhile(IndentType.Continuation); + + if (Engine.formattingOptions.IndentCaseBody) + { + NextLineIndent.Push(IndentType.Block); + } + else + { + NextLineIndent.Push(IndentType.Empty); + } + } + + static readonly string[] caseDefaultKeywords = { + "case", + "default" + }; + + static readonly string[] breakContinueReturnGotoKeywords = { + "break", + "continue", + "return", + "goto" + }; + + public override void CheckKeyword(string keyword) + { + if (caseDefaultKeywords.Contains(keyword) && Engine.isLineStartBeforeWordToken) + { + ExitState(); + ChangeState(); + } + else if (breakContinueReturnGotoKeywords.Contains(keyword) && Engine.isLineStartBeforeWordToken) + { + // OPTION: Engine.formattingOptions.IndentBreakStatements + if (!Engine.formattingOptions.IndentBreakStatements) + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + } + } + + base.CheckKeyword(keyword); + } + + + public override void OnExit() + { + //Parent.OnExit(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new SwitchCaseState(this, engine); + } + } + + #endregion + + #region Parentheses body state + + /// + /// Parentheses body state. + /// + /// + /// Represents a block of code between ( and ). + /// + public class ParenthesesBodyState : BracketsBodyBaseState + { + /// + /// True if any char has been pushed. + /// + public bool IsSomethingPushed; + + public override char ClosedBracket + { + get { return ')'; } + } + + public ParenthesesBodyState() + { } + + public ParenthesesBodyState(ParenthesesBodyState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsSomethingPushed = prototype.IsSomethingPushed; + } + + public override void Push(char ch) + { + if (ch == Engine.newLineChar) + { + if (Engine.formattingOptions.AnonymousMethodBraceStyle == BraceStyle.EndOfLine || + Engine.formattingOptions.AnonymousMethodBraceStyle == BraceStyle.EndOfLineWithoutSpace) { + if (NextLineIndent.PopIf(IndentType.Continuation)) { + NextLineIndent.Push(IndentType.Block); + } + } + } + else if (!IsSomethingPushed) + { + // OPTION: CSharpFormattingOptions.AlignToFirstMethodCallArgument + if (Engine.formattingOptions.AlignToFirstMethodCallArgument) + { + NextLineIndent.PopTry(); + // align the next line at the beginning of the open bracket + NextLineIndent.ExtraSpaces = Math.Max(0, Engine.column - NextLineIndent.CurIndent - 1); + } + } + + base.Push(ch); + IsSomethingPushed = true; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + NextLineIndent.Push(IndentType.Continuation); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new ParenthesesBodyState(this, engine); + } + + public override void OnExit() + { + if (Engine.isLineStart) + { + if (ThisLineIndent.ExtraSpaces > 0) + { + ThisLineIndent.ExtraSpaces--; + } + else + { + ThisLineIndent.PopTry(); + } + } + + base.OnExit(); + } + } + + #endregion + + #region Square brackets body state + + /// + /// Square brackets body state. + /// + /// + /// Represents a block of code between [ and ]. + /// + public class SquareBracketsBodyState : BracketsBodyBaseState + { + /// + /// True if any char has been pushed. + /// + public bool IsSomethingPushed; + + public override char ClosedBracket + { + get { return ']'; } + } + + public SquareBracketsBodyState() + { } + + public SquareBracketsBodyState(SquareBracketsBodyState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsSomethingPushed = prototype.IsSomethingPushed; + } + + public override void Push(char ch) + { + if (ch == Engine.newLineChar) + { + if (NextLineIndent.PopIf(IndentType.Continuation)) + { + NextLineIndent.Push(IndentType.Block); + } + } + else if (!IsSomethingPushed) + { + // OPTION: CSharpFormattingOptions.AlignToFirstIndexerArgument + if (Engine.formattingOptions.AlignToFirstIndexerArgument) + { + NextLineIndent.PopTry(); + // align the next line at the beginning of the open bracket + NextLineIndent.ExtraSpaces = Math.Max(0, Engine.column - NextLineIndent.CurIndent - 1); + } + } + + base.Push(ch); + IsSomethingPushed = true; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + NextLineIndent.Push(IndentType.Continuation); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new SquareBracketsBodyState(this, engine); + } + + public override void OnExit() + { + if (Engine.isLineStart) + { + if (ThisLineIndent.ExtraSpaces > 0) + { + ThisLineIndent.ExtraSpaces--; + } + else + { + ThisLineIndent.PopTry(); + } + } + + base.OnExit(); + } + } + + #endregion + + #endregion + + #region PreProcessor state + + /// + /// PreProcessor directive state. + /// + /// + /// Activated when the '#' char is pushed and the + /// is true. + /// + public class PreProcessorState : IndentState + { + /// + /// The type of the preprocessor directive. + /// + public PreProcessorDirective DirectiveType; + + /// + /// If is set (not equal to 'None'), this + /// stores the expression of the directive. + /// + public StringBuilder DirectiveStatement; + + public PreProcessorState() + { + DirectiveType = PreProcessorDirective.None; + DirectiveStatement = new StringBuilder(); + } + + public PreProcessorState(PreProcessorState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + DirectiveType = prototype.DirectiveType; + DirectiveStatement = new StringBuilder(prototype.DirectiveStatement.ToString()); + } + + public override void Push(char ch) + { + // HACK: if this change would be left for the CheckKeyword method, we will lose + // it if the next pushed char is newLineChar since ThisLineIndent will be + // immediately replaced with NextLineIndent. As this most likely will + // happen, we check for "endregion" on every push. + if (Engine.wordToken.ToString() == "endregion") + { + CheckKeywordOnPush("endregion"); + } + + base.Push(ch); + + if (DirectiveType != PreProcessorDirective.None) + { + DirectiveStatement.Append(ch); + } + + if (ch == Engine.newLineChar) + { + ExitState(); + switch (DirectiveType) + { + case PreProcessorDirective.If: + Engine.ifDirectiveEvalResults.Push(eval(DirectiveStatement.ToString())); + if (Engine.ifDirectiveEvalResults.Peek()) + { + // the if/elif directive is true -> continue with the previous state + } + else + { + // the if/elif directive is false -> change to a state that will + // ignore any chars until #endif or #elif + ChangeState(); + } + break; + case PreProcessorDirective.Elif: + if (Engine.ifDirectiveEvalResults.Count > 0) + { + if (!Engine.ifDirectiveEvalResults.Peek()) + { + ExitState(); + Engine.ifDirectiveEvalResults.Pop(); + goto case PreProcessorDirective.If; + } + } + // previous if was true -> comment + ChangeState(); + break; + case PreProcessorDirective.Else: + if (Engine.ifDirectiveEvalResults.Count > 0 && Engine.ifDirectiveEvalResults.Peek()) + { + // some if/elif directive was true -> change to a state that will + // ignore any chars until #endif + ChangeState(); + } + else + { + // none if/elif directives were true -> exit comment state. + if (Engine.currentState is PreProcessorCommentState) + ExitState(); + } + break; + case PreProcessorDirective.Define: + var defineSymbol = DirectiveStatement.ToString().Trim(); + if (!Engine.conditionalSymbols.Contains(defineSymbol)) + { + Engine.conditionalSymbols.Add(defineSymbol); + } + break; + case PreProcessorDirective.Undef: + var undefineSymbol = DirectiveStatement.ToString().Trim(); + if (Engine.conditionalSymbols.Contains(undefineSymbol)) + { + Engine.conditionalSymbols.Remove(undefineSymbol); + } + break; + case PreProcessorDirective.Endif: + // marks the end of this block + if (Engine.currentState is PreProcessorCommentState) + ExitState(); + + Engine.ifDirectiveEvalResults.Pop(); + Engine.ifDirectiveIndents.Pop(); + break; + case PreProcessorDirective.Region: + case PreProcessorDirective.Pragma: + case PreProcessorDirective.Warning: + case PreProcessorDirective.Error: + case PreProcessorDirective.Line: + // continue with the previous state + break; + } + } + } + + public override void InitializeState() + { + // OPTION: IndentPreprocessorStatements + if (Engine.formattingOptions.IndentPreprocessorDirectives) + { + if (Engine.ifDirectiveIndents.Count > 0) + { + ThisLineIndent = Engine.ifDirectiveIndents.Peek().Clone(); + } + else + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + } + } + else + { + ThisLineIndent = new Indent(Engine.textEditorOptions); + } + + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + static readonly Dictionary preProcessorDirectives = new Dictionary + { + { "if", PreProcessorDirective.If }, + { "elif", PreProcessorDirective.Elif }, + { "else", PreProcessorDirective.Else }, + { "endif", PreProcessorDirective.Endif }, + { "region", PreProcessorDirective.Region }, + { "endregion", PreProcessorDirective.Endregion }, + { "pragma", PreProcessorDirective.Pragma }, + { "warning", PreProcessorDirective.Warning }, + { "error", PreProcessorDirective.Error }, + { "line", PreProcessorDirective.Line }, + { "define", PreProcessorDirective.Define }, + { "undef", PreProcessorDirective.Undef } + }; + + public override void CheckKeywordOnPush(string keyword) + { + if (keyword == "endregion") + { + DirectiveType = PreProcessorDirective.Endregion; + ThisLineIndent = Parent.NextLineIndent.Clone(); + } + } + + public override void CheckKeyword(string keyword) + { + // check if the directive type has already been set + if (DirectiveType != PreProcessorDirective.None) + { + return; + } + + if (preProcessorDirectives.ContainsKey(keyword)) + { + DirectiveType = preProcessorDirectives[keyword]; + + // adjust the indentation for the region directive + if (DirectiveType == PreProcessorDirective.Region) + { + ThisLineIndent = Parent.NextLineIndent.Clone(); + } + else if (DirectiveType == PreProcessorDirective.If) + { + Engine.ifDirectiveIndents.Push(ThisLineIndent.Clone()); + } + } + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new PreProcessorState(this, engine); + } + + /// + /// Types of preprocessor directives. + /// + public enum PreProcessorDirective + { + None, + If, + Elif, + Else, + Endif, + Region, + Endregion, + Pragma, + Warning, + Error, + Line, + Define, + Undef + } + + #region Pre processor evaluation (from cs-tokenizer.cs) + + static bool is_identifier_start_character(int c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter((char)c); + } + + static bool is_identifier_part_character(char c) + { + if (c >= 'a' && c <= 'z') + return true; + + if (c >= 'A' && c <= 'Z') + return true; + + if (c == '_' || (c >= '0' && c <= '9')) + return true; + + if (c < 0x80) + return false; + + return Char.IsLetter(c) || Char.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation; + } + + bool eval_val(string s) + { + if (s == "true") + return true; + if (s == "false") + return false; + + return Engine.conditionalSymbols != null && Engine.conditionalSymbols.Contains(s) || + Engine.customConditionalSymbols != null && Engine.customConditionalSymbols.Contains(s); + } + + bool pp_primary(ref string s) + { + s = s.Trim(); + int len = s.Length; + + if (len > 0) + { + char c = s[0]; + + if (c == '(') + { + s = s.Substring(1); + bool val = pp_expr(ref s, false); + if (s.Length > 0 && s[0] == ')') + { + s = s.Substring(1); + return val; + } + return false; + } + + if (is_identifier_start_character(c)) + { + int j = 1; + + while (j < len) + { + c = s[j]; + + if (is_identifier_part_character(c)) + { + j++; + continue; + } + bool v = eval_val(s.Substring(0, j)); + s = s.Substring(j); + return v; + } + bool vv = eval_val(s); + s = ""; + return vv; + } + } + return false; + } + + bool pp_unary(ref string s) + { + s = s.Trim(); + int len = s.Length; + + if (len > 0) + { + if (s[0] == '!') + { + if (len > 1 && s[1] == '=') + { + return false; + } + s = s.Substring(1); + return !pp_primary(ref s); + } + else + return pp_primary(ref s); + } + else + { + return false; + } + } + + bool pp_eq(ref string s) + { + bool va = pp_unary(ref s); + + s = s.Trim(); + int len = s.Length; + if (len > 0) + { + if (s[0] == '=') + { + if (len > 2 && s[1] == '=') + { + s = s.Substring(2); + return va == pp_unary(ref s); + } + else + { + return false; + } + } + else if (s[0] == '!' && len > 1 && s[1] == '=') + { + s = s.Substring(2); + + return va != pp_unary(ref s); + + } + } + + return va; + + } + + bool pp_and(ref string s) + { + bool va = pp_eq(ref s); + + s = s.Trim(); + int len = s.Length; + if (len > 0) + { + if (s[0] == '&') + { + if (len > 2 && s[1] == '&') + { + s = s.Substring(2); + return (va & pp_and(ref s)); + } + else + { + return false; + } + } + } + return va; + } + + // + // Evaluates an expression for `#if' or `#elif' + // + bool pp_expr(ref string s, bool isTerm) + { + bool va = pp_and(ref s); + s = s.Trim(); + int len = s.Length; + if (len > 0) + { + char c = s[0]; + + if (c == '|') + { + if (len > 2 && s[1] == '|') + { + s = s.Substring(2); + return va | pp_expr(ref s, isTerm); + } + else + { + + return false; + } + } + if (isTerm) + { + return false; + } + } + + return va; + } + + bool eval(string s) + { + bool v = pp_expr(ref s, true); + s = s.Trim(); + if (s.Length != 0) + { + return false; + } + + return v; + } + + #endregion + } + + #endregion + + #region PreProcessorComment state + + /// + /// PreProcessor comment state. + /// + /// + /// Activates when the #if or #elif directive is false and ignores + /// all pushed chars until the next '#'. + /// + public class PreProcessorCommentState : IndentState + { + public PreProcessorCommentState() + { } + + public PreProcessorCommentState(PreProcessorCommentState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == '#' && Engine.isLineStart) + { + ChangeState(); + } + } + + public override void InitializeState() + { + if (Engine.formattingOptions.IndentPreprocessorDirectives && + Engine.ifDirectiveIndents.Count > 0) + { + ThisLineIndent = Engine.ifDirectiveIndents.Peek().Clone(); + NextLineIndent = ThisLineIndent.Clone(); + } + else + { + ThisLineIndent = Parent.NextLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + } + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new PreProcessorCommentState(this, engine); + } + } + + #endregion + + #region LineComment state + + /// + /// Single-line comment state. + /// + public class LineCommentState : IndentState + { + /// + /// It's possible that this should be the DocComment state: + /// check if the first next pushed char is equal to '/'. + /// + public bool CheckForDocComment = true; + + public LineCommentState() + { + /* if (engine.formattingOptions.KeepCommentsAtFirstColumn && engine.column == 2) + ThisLineIndent.Reset();*/ + } + + public LineCommentState(LineCommentState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + CheckForDocComment = prototype.CheckForDocComment; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == Engine.newLineChar) + { + // to handle cases like //\n/* + // Otherwise line 2 would be treated as line comment. + Engine.previousChar = '\0'; + ExitState(); + } + else if (ch == '/' && CheckForDocComment) + { + // wrong state, should be DocComment. + ExitState(); + ChangeState(); + } + + CheckForDocComment = false; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new LineCommentState(this, engine); + } + } + + #endregion + + #region DocComment state + + /// + /// XML documentation comment state. + /// + public class DocCommentState : IndentState + { + public DocCommentState() + { } + + public DocCommentState(DocCommentState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == Engine.newLineChar) + { + ExitState(); + } + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new DocCommentState(this, engine); + } + } + + #endregion + + #region MultiLineComment state + + /// + /// Multi-line comment state. + /// + public class MultiLineCommentState : IndentState + { + /// + /// True if any char has been pushed to this state. + /// + /// + /// Needed to resolve an issue when the first pushed char is '/'. + /// The state would falsely exit on this sequence of chars '/*/', + /// since it only checks if the last two chars are '/' and '*'. + /// + public bool IsAnyCharPushed; + + public MultiLineCommentState() + { } + + public MultiLineCommentState(MultiLineCommentState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsAnyCharPushed = prototype.IsAnyCharPushed; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == '/' && Engine.previousChar == '*' && IsAnyCharPushed) + { + ExitState(); + } + + IsAnyCharPushed = true; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new MultiLineCommentState(this, engine); + } + } + + #endregion + + #region StringLiteral state + + /// + /// StringLiteral state. + /// + public class StringLiteralState : IndentState + { + /// + /// True if the next char is escaped with '\'. + /// + public bool IsEscaped; + + public StringLiteralState() + { } + + public StringLiteralState(StringLiteralState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsEscaped = prototype.IsEscaped; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == Engine.newLineChar || (!IsEscaped && ch == '"')) { + ExitState(); + } else { + IsEscaped = ch == '\\' && !IsEscaped; + } + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new StringLiteralState(this, engine); + } + } + + #endregion + + #region Verbatim string state + + /// + /// Verbatim string state. + /// + public class VerbatimStringState : IndentState + { + /// + /// True if there is an odd number of '"' in a row. + /// + public bool IsEscaped; + + public VerbatimStringState() + { } + + public VerbatimStringState(VerbatimStringState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsEscaped = prototype.IsEscaped; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (IsEscaped && ch != '"') + { + ExitState(); + // the char has been pushed to the wrong state, push it back + Engine.currentState.Push(ch); + } + + IsEscaped = ch == '"' && !IsEscaped; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = new Indent(Engine.textEditorOptions); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new VerbatimStringState(this, engine); + } + } + + #endregion + + #region Character state + + /// + /// Character state. + /// + public class CharacterState : IndentState + { + /// + /// True if the next char is escaped with '\'. + /// + public bool IsEscaped; + + public CharacterState() + { } + + public CharacterState(CharacterState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsEscaped = prototype.IsEscaped; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == Engine.newLineChar) + { + ExitState(); + } + else if (!IsEscaped && ch == '\'') + { + ExitState(); + } + + IsEscaped = ch == '\\' && !IsEscaped; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new CharacterState(this, engine); + } + } + + #endregion +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/NullIStateMachineIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/NullIStateMachineIndentEngine.cs new file mode 100644 index 000000000..b62932e5b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/NullIStateMachineIndentEngine.cs @@ -0,0 +1,215 @@ +// +// NullIStateMachineIndentEngine.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// 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 +{ + /// + /// An empty IStateMachineIndentEngine implementation that does nothing. + /// + public sealed class NullIStateMachineIndentEngine : IStateMachineIndentEngine + { + readonly ICSharpCode.NRefactory.Editor.IDocument document; + int offset; + + public NullIStateMachineIndentEngine(ICSharpCode.NRefactory.Editor.IDocument document) + { + if (document == null) + throw new ArgumentNullException("document"); + this.document = document; + } + + #region IStateMachineIndentEngine implementation + public IStateMachineIndentEngine Clone() + { + return new NullIStateMachineIndentEngine(document) { offset = this.offset }; + } + + bool IStateMachineIndentEngine.IsInsidePreprocessorDirective { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsidePreprocessorComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideStringLiteral { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideVerbatimString { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideCharacter { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideString { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideLineComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideMultiLineComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideDocLineComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideOrdinaryComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideOrdinaryCommentOrString { + get { + return false; + } + } + + bool IStateMachineIndentEngine.LineBeganInsideVerbatimString { + get { + return false; + } + } + + bool IStateMachineIndentEngine.LineBeganInsideMultiLineComment { + get { + return false; + } + } + #endregion + + #region IDocumentIndentEngine implementation + void IDocumentIndentEngine.Push(char ch) + { + offset++; + } + + void IDocumentIndentEngine.Reset() + { + this.offset = 0; + } + + void IDocumentIndentEngine.Update(int offset) + { + this.offset = offset; + } + + IDocumentIndentEngine IDocumentIndentEngine.Clone() + { + return Clone(); + } + + ICSharpCode.NRefactory.Editor.IDocument IDocumentIndentEngine.Document { + get { + return document; + } + } + + string IDocumentIndentEngine.ThisLineIndent { + get { + return ""; + } + } + + string IDocumentIndentEngine.NextLineIndent { + get { + return ""; + } + } + + string IDocumentIndentEngine.CurrentIndent { + get { + return ""; + } + } + + bool IDocumentIndentEngine.NeedsReindent { + get { + return false; + } + } + + int IDocumentIndentEngine.Offset { + get { + return offset; + } + } + TextLocation IDocumentIndentEngine.Location { + get { + return TextLocation.Empty; + } + } + + /// + public bool EnableCustomIndentLevels + { + get { return false; } + set { } + } + + #endregion + + #region ICloneable implementation + object ICloneable.Clone() + { + return Clone(); + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/TextPasteIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/TextPasteIndentEngine.cs new file mode 100644 index 000000000..9170b029f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/TextPasteIndentEngine.cs @@ -0,0 +1,632 @@ +// +// TextPasteIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a decorator of an IStateMachineIndentEngine instance + /// that provides logic for text paste events. + /// + public class TextPasteIndentEngine : IDocumentIndentEngine, ITextPasteHandler + { + + #region Properties + + /// + /// An instance of IStateMachineIndentEngine which handles + /// the indentation logic. + /// + IStateMachineIndentEngine engine; + /// + /// Text editor options. + /// + internal readonly TextEditorOptions textEditorOptions; + internal readonly CSharpFormattingOptions formattingOptions; + #endregion + + #region Constructors + + /// + /// Creates a new TextPasteIndentEngine instance. + /// + /// + /// An instance of to which the + /// logic for indentation will be delegated. + /// + /// + /// Text editor options for indentation. + /// + /// + /// C# formatting options. + /// + public TextPasteIndentEngine(IStateMachineIndentEngine decoratedEngine, TextEditorOptions textEditorOptions, CSharpFormattingOptions formattingOptions) + { + this.engine = decoratedEngine; + this.textEditorOptions = textEditorOptions; + this.formattingOptions = formattingOptions; + + this.engine.EnableCustomIndentLevels = false; + } + + #endregion + + #region ITextPasteHandler + + /// + string ITextPasteHandler.FormatPlainText(int offset, string text, byte[] copyData) + { + if (copyData != null && copyData.Length == 1) { + var strategy = TextPasteUtils.Strategies [(PasteStrategy)copyData [0]]; + text = strategy.Decode(text); + } + engine.Update(offset); + if (engine.IsInsideStringLiteral) { + int idx = text.IndexOf('"'); + if (idx > 0) { + var o = offset; + while (o < engine.Document.TextLength) { + char ch = engine.Document.GetCharAt(o); + engine.Push(ch); + if (NewLine.IsNewLine(ch)) + break; + o++; + if (!engine.IsInsideStringLiteral) + return TextPasteUtils.StringLiteralStrategy.Encode(text); + } + return TextPasteUtils.StringLiteralStrategy.Encode(text.Substring(0, idx)) + text.Substring(idx); + } + return TextPasteUtils.StringLiteralStrategy.Encode(text); + + } else if (engine.IsInsideVerbatimString) { + + int idx = text.IndexOf('"'); + if (idx > 0) { + var o = offset; + while (o < engine.Document.TextLength) { + char ch = engine.Document.GetCharAt(o); + engine.Push(ch); + o++; + if (!engine.IsInsideVerbatimString) + return TextPasteUtils.VerbatimStringStrategy.Encode(text); + } + return TextPasteUtils.VerbatimStringStrategy.Encode(text.Substring(0, idx)) + text.Substring(idx); + } + + return TextPasteUtils.VerbatimStringStrategy.Encode(text); + } + var line = engine.Document.GetLineByOffset(offset); + var pasteAtLineStart = line.Offset == offset; + var indentedText = new StringBuilder(); + var curLine = new StringBuilder(); + var clonedEngine = engine.Clone(); + bool isNewLine = false, gotNewLine = false; + for (int i = 0; i < text.Length; i++) { + var ch = text [i]; + if (clonedEngine.IsInsideVerbatimString || clonedEngine.IsInsideMultiLineComment) { + clonedEngine.Push(ch); + curLine.Append(ch); + continue; + } + + var delimiterLength = NewLine.GetDelimiterLength(ch, i + 1 < text.Length ? text[i + 1] : ' '); + if (delimiterLength > 0) { + isNewLine = true; + if (gotNewLine || pasteAtLineStart) { + if (curLine.Length > 0 || formattingOptions.EmptyLineFormatting == EmptyLineFormatting.Indent) + indentedText.Append(clonedEngine.ThisLineIndent); + } + indentedText.Append(curLine); + indentedText.Append(textEditorOptions.EolMarker); + curLine.Length = 0; + gotNewLine = true; + i += delimiterLength - 1; + // textEditorOptions.EolMarker[0] is the newLineChar used by the indentation engine. + clonedEngine.Push(textEditorOptions.EolMarker[0]); + } else { + if (isNewLine) { + if (ch == '\t' || ch == ' ') { + clonedEngine.Push(ch); + continue; + } + isNewLine = false; + } + curLine.Append(ch); + clonedEngine.Push(ch); + } + if (clonedEngine.IsInsideVerbatimString || clonedEngine.IsInsideMultiLineComment && + !(clonedEngine.LineBeganInsideVerbatimString || clonedEngine.LineBeganInsideMultiLineComment)) { + if (gotNewLine) { + if (curLine.Length > 0 || formattingOptions.EmptyLineFormatting == EmptyLineFormatting.Indent) + indentedText.Append(clonedEngine.ThisLineIndent); + } + pasteAtLineStart = false; + indentedText.Append(curLine); + curLine.Length = 0; + gotNewLine = false; + continue; + } + } + if (gotNewLine && (!pasteAtLineStart || curLine.Length > 0)) { + indentedText.Append(clonedEngine.ThisLineIndent); + } + if (curLine.Length > 0) { + indentedText.Append(curLine); + } + return indentedText.ToString(); + } + + /// + byte[] ITextPasteHandler.GetCopyData(ISegment segment) + { + engine.Update(segment.Offset); + + if (engine.IsInsideStringLiteral) { + return new[] { (byte)PasteStrategy.StringLiteral }; + } else if (engine.IsInsideVerbatimString) { + return new[] { (byte)PasteStrategy.VerbatimString }; + } + + return null; + } + + #endregion + + #region IDocumentIndentEngine + + /// + public IDocument Document { + get { return engine.Document; } + } + + /// + public string ThisLineIndent { + get { return engine.ThisLineIndent; } + } + + /// + public string NextLineIndent { + get { return engine.NextLineIndent; } + } + + /// + public string CurrentIndent { + get { return engine.CurrentIndent; } + } + + /// + public bool NeedsReindent { + get { return engine.NeedsReindent; } + } + + /// + public int Offset { + get { return engine.Offset; } + } + + /// + public TextLocation Location { + get { return engine.Location; } + } + + /// + public bool EnableCustomIndentLevels { + get { return engine.EnableCustomIndentLevels; } + set { engine.EnableCustomIndentLevels = value; } + } + + /// + public void Push(char ch) + { + engine.Push(ch); + } + + /// + public void Reset() + { + engine.Reset(); + } + + /// + public void Update(int offset) + { + engine.Update(offset); + } + + #endregion + + #region IClonable + + public IDocumentIndentEngine Clone() + { + return new TextPasteIndentEngine(engine, textEditorOptions, formattingOptions); + } + + object ICloneable.Clone() + { + return Clone(); + } + + #endregion + + } + + /// + /// Types of text-paste strategies. + /// + public enum PasteStrategy : byte + { + PlainText = 0, + StringLiteral = 1, + VerbatimString = 2 + } + + /// + /// Defines some helper methods for dealing with text-paste events. + /// + public static class TextPasteUtils + { + /// + /// Collection of text-paste strategies. + /// + public static TextPasteStrategies Strategies = new TextPasteStrategies(); + + /// + /// The interface for a text-paste strategy. + /// + public interface IPasteStrategy + { + /// + /// Formats the given text according with this strategy rules. + /// + /// + /// The text to format. + /// + /// + /// Formatted text. + /// + string Encode(string text); + + /// + /// Converts text formatted according with this strategy rules + /// to its original form. + /// + /// + /// Formatted text to convert. + /// + /// + /// Original form of the given formatted text. + /// + string Decode(string text); + + /// + /// Type of this strategy. + /// + PasteStrategy Type { get; } + } + + /// + /// Wrapper that discovers all defined text-paste strategies and defines a way + /// to easily access them through their type. + /// + public sealed class TextPasteStrategies + { + /// + /// Collection of discovered text-paste strategies. + /// + IDictionary strategies; + + /// + /// Uses reflection to find all types derived from + /// and adds an instance of each strategy to . + /// + public TextPasteStrategies() + { + strategies = Assembly.GetExecutingAssembly() + .GetTypes() + .Where(t => typeof(IPasteStrategy).IsAssignableFrom(t) && t.IsClass) + .Select(t => (IPasteStrategy)t.GetProperty("Instance").GetValue(null, null)) + .ToDictionary(s => s.Type); + } + + /// + /// Checks if there is a strategy of the given type and returns it. + /// + /// + /// Type of the strategy instance. + /// + /// + /// A strategy instance of the requested type, + /// or if it wasn't found. + /// + public IPasteStrategy this [PasteStrategy strategy] { + get { + if (strategies.ContainsKey(strategy)) { + return strategies [strategy]; + } + + return DefaultStrategy; + } + } + } + + /// + /// Doesn't do any formatting. Serves as the default strategy. + /// + public class PlainTextPasteStrategy : IPasteStrategy + { + + #region Singleton + + public static IPasteStrategy Instance { + get { + return instance ?? (instance = new PlainTextPasteStrategy()); + } + } + + static PlainTextPasteStrategy instance; + + protected PlainTextPasteStrategy() + { + } + + #endregion + + /// + public string Encode(string text) + { + return text; + } + + /// + public string Decode(string text) + { + return text; + } + + /// + public PasteStrategy Type { + get { return PasteStrategy.PlainText; } + } + } + + /// + /// Escapes chars in the given text so that they don't + /// break a valid string literal. + /// + public class StringLiteralPasteStrategy : IPasteStrategy + { + + #region Singleton + + public static IPasteStrategy Instance { + get { + return instance ?? (instance = new StringLiteralPasteStrategy()); + } + } + + static StringLiteralPasteStrategy instance; + + protected StringLiteralPasteStrategy() + { + } + + #endregion + + /// + public string Encode(string text) + { + return CSharpOutputVisitor.ConvertString(text); + } + + /// + public string Decode(string text) + { + var result = new StringBuilder(); + bool isEscaped = false; + + for (int i = 0; i < text.Length; i++) { + var ch = text[i]; + if (isEscaped) { + switch (ch) { + case 'a': + result.Append('\a'); + break; + case 'b': + result.Append('\b'); + break; + case 'n': + result.Append('\n'); + break; + case 't': + result.Append('\t'); + break; + case 'v': + result.Append('\v'); + break; + case 'r': + result.Append('\r'); + break; + case '\\': + result.Append('\\'); + break; + case 'f': + result.Append('\f'); + break; + case '0': + result.Append(0); + break; + case '"': + result.Append('"'); + break; + case '\'': + result.Append('\''); + break; + case 'x': + char r; + if (TryGetHex(text, -1, ref i, out r)) { + result.Append(r); + break; + } + goto default; + case 'u': + if (TryGetHex(text, 4, ref i, out r)) { + result.Append(r); + break; + } + goto default; + case 'U': + if (TryGetHex(text, 8, ref i, out r)) { + result.Append(r); + break; + } + goto default; + default: + result.Append('\\'); + result.Append(ch); + break; + } + isEscaped = false; + continue; + } + if (ch != '\\') { + result.Append(ch); + } + else { + isEscaped = true; + } + } + + return result.ToString(); + } + + static bool TryGetHex(string text, int count, ref int idx, out char r) + { + int i; + int total = 0; + int top = count != -1 ? count : 4; + + for (i = 0; i < top; i++) { + int c = text[idx + 1 + i]; + + if (c >= '0' && c <= '9') + c = (int) c - (int) '0'; + else if (c >= 'A' && c <= 'F') + c = (int) c - (int) 'A' + 10; + else if (c >= 'a' && c <= 'f') + c = (int) c - (int) 'a' + 10; + else { + r = '\0'; + return false; + } + total = (total * 16) + c; + } + + if (top == 8) { + if (total > 0x0010FFFF) { + r = '\0'; + return false; + } + + if (total >= 0x00010000) + total = ((total - 0x00010000) / 0x0400 + 0xD800); + } + r = (char)total; + idx += top; + return true; + } + + /// + public PasteStrategy Type { + get { return PasteStrategy.StringLiteral; } + } + } + + /// + /// Escapes chars in the given text so that they don't + /// break a valid verbatim string. + /// + public class VerbatimStringPasteStrategy : IPasteStrategy + { + + #region Singleton + + public static IPasteStrategy Instance { + get { + return instance ?? (instance = new VerbatimStringPasteStrategy()); + } + } + + static VerbatimStringPasteStrategy instance; + + protected VerbatimStringPasteStrategy() + { + } + + #endregion + + static readonly Dictionary> encodeReplace = new Dictionary> { + { '\"', "\"\"" }, + }; + + /// + public string Encode(string text) + { + return string.Concat(text.SelectMany(c => encodeReplace.ContainsKey(c) ? encodeReplace [c] : new[] { c })); + } + + /// + public string Decode(string text) + { + bool isEscaped = false; + return string.Concat(text.Where(c => !(isEscaped = !isEscaped && c == '"'))); + } + + /// + public PasteStrategy Type { + get { return PasteStrategy.VerbatimString; } + } + } + + /// + /// The default text-paste strategy. + /// + public static IPasteStrategy DefaultStrategy = PlainTextPasteStrategy.Instance; + /// + /// String literal text-paste strategy. + /// + public static IPasteStrategy StringLiteralStrategy = StringLiteralPasteStrategy.Instance; + /// + /// Verbatim string text-paste strategy. + /// + public static IPasteStrategy VerbatimStringStrategy = VerbatimStringPasteStrategy.Instance; + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IntroduceQueryExpressions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IntroduceQueryExpressions.cs new file mode 100644 index 000000000..d80ddb82c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IntroduceQueryExpressions.cs @@ -0,0 +1,386 @@ +// +// IntroduceQueryExpressions.cs +// +// Modified by Luís Reis (Copyright (C) 2013) +// +// Copyright header of the original version follows: +// +// Copyright (c) 2011 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.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + static class NRefactoryExtensions + { + public static T Detach(this T node) where T : AstNode + { + node.Remove(); + return node; + } + + public static T CopyAnnotationsFrom(this T node, AstNode other) where T : AstNode + { + foreach (object annotation in other.Annotations) { + node.AddAnnotation(annotation); + } + return node; + } + + public static Expression WithName(this Expression node, string patternGroupName) + { + return new NamedNode(patternGroupName, node); + } + } + + /// + /// Decompiles query expressions. + /// Based on C# 4.0 spec, §7.16.2 Query expression translation + /// + public class IntroduceQueryExpressions + { + static readonly InvocationExpression castPattern = new InvocationExpression { + Target = new MemberReferenceExpression { + Target = new AnyNode("inExpr"), + MemberName = "Cast", + TypeArguments = { new AnyNode("targetType") } + }}; + + public Expression ConvertFluentToQuery(Expression node) + { + node = node.Clone(); + + var artificialParent = new ExpressionStatement(); + artificialParent.Expression = node; + + DecompileQueries(node); + // After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group') + // and fix them, either by adding a degenerate select, or by combining them with another query. + foreach (QueryExpression query in artificialParent.Descendants.OfType()) { + QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); + if (IsDegenerateQuery(query)) { + string identifierName = fromClause.Identifier; + + // introduce select for degenerate query + query.Clauses.Add(new QuerySelectClause { Expression = new IdentifierExpression(identifierName) }); + } + + if (fromClause.Type.IsNull) { + // See if the data source of this query is a degenerate query, + // and combine the queries if possible. + QueryExpression innerQuery = fromClause.Expression as QueryExpression; + while (IsDegenerateQuery(innerQuery)) { + QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First(); + if (fromClause.Identifier != innerFromClause.Identifier && !innerFromClause.Identifier.StartsWith("<>")) + break; + // Replace the fromClause with all clauses from the inner query + fromClause.Remove(); + foreach (var identifierChild in innerQuery.Descendants.OfType().Where(identifier => identifier.Name == innerFromClause.Identifier)) { + //When the identifier is "<>X", then replace it with the outer one + identifierChild.ReplaceWith(fromClause.IdentifierToken.Clone()); + } + QueryClause insertionPos = null; + foreach (var clause in innerQuery.Clauses) { + query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); + } + fromClause = innerFromClause; + innerQuery = fromClause.Expression as QueryExpression; + } + } + } + + return artificialParent.Expression.Clone(); + } + + bool IsDegenerateQuery(QueryExpression query) + { + if (query == null) + return false; + var lastClause = query.Clauses.LastOrDefault(); + return !(lastClause is QuerySelectClause || lastClause is QueryGroupClause); + } + + void DecompileQueries(AstNode node) + { + QueryExpression query = DecompileQuery(node as InvocationExpression); + if (query != null) + node.ReplaceWith(query); + } + + Expression ExtractQuery(MemberReferenceExpression mre) + { + var inExpression = mre.Target.Clone(); + var inContent = DecompileQuery(inExpression as InvocationExpression) ?? inExpression; + return inContent; + } + + QueryExpression DecompileQuery(InvocationExpression invocation) + { + if (invocation == null) + return null; + MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; + if (mre == null) + return null; + + switch (mre.MemberName) { + case "Select": + { + if (invocation.Arguments.Count != 1) + return null; + string parameterName; + Expression body; + if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) { + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QuerySelectClause { Expression = body.Detach() }); + return query; + } + return null; + } + case "Cast": + { + if (invocation.Arguments.Count == 0 && mre.TypeArguments.Count == 1) { + var typeArgument = mre.TypeArguments.First(); + + QueryExpression query = new QueryExpression(); + string varName = GenerateVariableName(); + query.Clauses.Add(new QueryFromClause { + Identifier = varName, + Expression = ExtractQuery(mre), + Type = typeArgument.Detach() + }); + return query; + + } + return null; + } + case "GroupBy": + { + if (invocation.Arguments.Count == 2) { + string parameterName1, parameterName2; + Expression keySelector, elementSelector; + if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName1, out keySelector) + && MatchSimpleLambda(invocation.Arguments.ElementAt(1), out parameterName2, out elementSelector) + && parameterName1 == parameterName2) { + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName1, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() }); + return query; + } + } else if (invocation.Arguments.Count == 1) { + string parameterName; + Expression keySelector; + if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out keySelector)) { + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QueryGroupClause { + Projection = new IdentifierExpression(parameterName), + Key = keySelector.Detach() + }); + return query; + } + } + return null; + } + case "SelectMany": + { + if (invocation.Arguments.Count != 2) + return null; + string parameterName; + Expression collectionSelector; + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName, out collectionSelector)) + return null; + LambdaExpression lambda = invocation.Arguments.ElementAt(1) as LambdaExpression; + if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { + ParameterDeclaration p1 = lambda.Parameters.ElementAt(0); + ParameterDeclaration p2 = lambda.Parameters.ElementAt(1); + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = p1.Name, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QueryFromClause { Identifier = p2.Name, Expression = collectionSelector.Detach() }); + query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }); + return query; + } + return null; + } + case "Where": + { + if (invocation.Arguments.Count != 1) + return null; + string parameterName; + Expression body; + if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) { + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QueryWhereClause { Condition = body.Detach() }); + return query; + } + return null; + } + case "OrderBy": + case "OrderByDescending": + case "ThenBy": + case "ThenByDescending": + { + if (invocation.Arguments.Count != 1) + return null; + string parameterName; + Expression orderExpression; + if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out orderExpression)) { + if (ValidateThenByChain(invocation, parameterName)) { + QueryOrderClause orderClause = new QueryOrderClause(); + InvocationExpression tmp = invocation; + while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") { + // insert new ordering at beginning + orderClause.Orderings.InsertAfter( + null, new QueryOrdering { + Expression = orderExpression.Detach(), + Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) + }); + + tmp = (InvocationExpression)mre.Target; + mre = (MemberReferenceExpression)tmp.Target; + MatchSimpleLambda(tmp.Arguments.Single(), out parameterName, out orderExpression); + } + // insert new ordering at beginning + orderClause.Orderings.InsertAfter( + null, new QueryOrdering { + Expression = orderExpression.Detach(), + Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) + }); + + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = ExtractQuery(mre) }); + query.Clauses.Add(orderClause); + return query; + } + } + return null; + } + case "Join": + case "GroupJoin": + { + if (invocation.Arguments.Count != 4) + return null; + Expression source1 = mre.Target; + Expression source2 = invocation.Arguments.ElementAt(0); + string elementName1, elementName2; + Expression key1, key2; + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out elementName1, out key1)) + return null; + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out elementName2, out key2)) + return null; + LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression; + if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { + ParameterDeclaration p1 = lambda.Parameters.ElementAt(0); + ParameterDeclaration p2 = lambda.Parameters.ElementAt(1); + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = elementName1, Expression = source1.Detach() }); + QueryJoinClause joinClause = new QueryJoinClause(); + + joinClause.JoinIdentifier = elementName2; // join elementName2 + joinClause.InExpression = source2.Detach(); // in source2 + + Match castMatch = castPattern.Match(source2); + if (castMatch.Success) { + Expression target = castMatch.Get("inExpr").Single().Detach(); + joinClause.Type = castMatch.Get("targetType").Single().Detach(); + joinClause.InExpression = target; + } + + joinClause.OnExpression = key1.Detach(); // on key1 + joinClause.EqualsExpression = key2.Detach(); // equals key2 + if (mre.MemberName == "GroupJoin") { + joinClause.IntoIdentifier = p2.Name; // into p2.Name + } + query.Clauses.Add(joinClause); + Expression resultExpr = ((Expression)lambda.Body).Detach(); + if (p1.Name != elementName1) { + foreach (var identifier in resultExpr.Descendants.OfType().Where(id => id.Name == p1.Name)) + { + identifier.Name = elementName1; + } + } + if (p2.Name != elementName2 && mre.MemberName != "GroupJoin") { + foreach (var identifier in resultExpr.Descendants.OfType().Where(id => id.Name == p2.Name)) + { + identifier.Name = elementName2; + } + } + query.Clauses.Add(new QuerySelectClause { Expression = resultExpr }); + return query; + } + return null; + } + default: + return null; + } + } + + int id = 1; + string GenerateVariableName() + { + return "<>" + id++; + } + + /// + /// Ensure that all ThenBy's are correct, and that the list of ThenBy's is terminated by an 'OrderBy' invocation. + /// + bool ValidateThenByChain(InvocationExpression invocation, string expectedParameterName) + { + if (invocation == null || invocation.Arguments.Count != 1) + return false; + MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; + if (mre == null) + return false; + string parameterName; + Expression body; + if (!MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) + return false; + if (parameterName != expectedParameterName) + return false; + + if (mre.MemberName == "OrderBy" || mre.MemberName == "OrderByDescending") + return true; + else if (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") + return ValidateThenByChain(mre.Target as InvocationExpression, expectedParameterName); + else + return false; + } + + /// Matches simple lambdas of the form "a => b" + bool MatchSimpleLambda(Expression expr, out string parameterName, out Expression body) + { + LambdaExpression lambda = expr as LambdaExpression; + if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) { + ParameterDeclaration p = lambda.Parameters.Single(); + if (p.ParameterModifier == ParameterModifier.None) { + parameterName = p.Name; + body = (Expression)lambda.Body; + return true; + } + } + parameterName = null; + body = null; + return false; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/NameLookupMode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/NameLookupMode.cs new file mode 100644 index 000000000..3196dc583 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/NameLookupMode.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2010-2013 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 +{ + public enum NameLookupMode + { + /// + /// Normal name lookup in expressions + /// + Expression, + /// + /// Name lookup in expression, where the expression is the target of an invocation. + /// Such a lookup will only return methods and delegate-typed fields. + /// + InvocationTarget, + /// + /// Normal name lookup in type references. + /// + Type, + /// + /// Name lookup in the type reference inside a using declaration. + /// + TypeInUsingDeclaration, + /// + /// Name lookup for base type references. + /// + BaseTypeReference + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs new file mode 100644 index 000000000..f6c153b1a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs @@ -0,0 +1,312 @@ +// Copyright (c) 2010-2013 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.IO; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// C# ambience. Used to convert type system symbols to text (usually for displaying the symbol to the user; e.g. in editor tooltips) + /// + public class CSharpAmbience : IAmbience + { + public ConversionFlags ConversionFlags { get; set; } + + #region ConvertSymbol + [Obsolete("Use ConvertSymbol() instead")] + public string ConvertEntity(IEntity entity) + { + return ConvertSymbol(entity); + } + + public string ConvertSymbol(ISymbol symbol) + { + if (symbol == null) + throw new ArgumentNullException("symbol"); + + StringWriter writer = new StringWriter(); + ConvertSymbol(symbol, new TextWriterTokenWriter(writer), FormattingOptionsFactory.CreateMono ()); + return writer.ToString(); + } + + [Obsolete("Use ConvertSymbol() instead")] + public void ConvertEntity(IEntity entity, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + ConvertSymbol(entity, writer, formattingPolicy); + } + + public void ConvertSymbol(ISymbol symbol, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + if (symbol == null) + throw new ArgumentNullException("symbol"); + if (writer == null) + throw new ArgumentNullException("writer"); + if (formattingPolicy == null) + throw new ArgumentNullException("formattingPolicy"); + + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + AstNode node = astBuilder.ConvertSymbol(symbol); + EntityDeclaration entityDecl = node as EntityDeclaration; + if (entityDecl != null) + PrintModifiers(entityDecl.Modifiers, writer); + + if ((ConversionFlags & ConversionFlags.ShowDefinitionKeyword) == ConversionFlags.ShowDefinitionKeyword) { + if (node is TypeDeclaration) { + switch (((TypeDeclaration)node).ClassType) { + case ClassType.Class: + writer.WriteKeyword(Roles.ClassKeyword, "class"); + break; + case ClassType.Struct: + writer.WriteKeyword(Roles.StructKeyword, "struct"); + break; + case ClassType.Interface: + writer.WriteKeyword(Roles.InterfaceKeyword, "interface"); + break; + case ClassType.Enum: + writer.WriteKeyword(Roles.EnumKeyword, "enum"); + break; + default: + throw new Exception("Invalid value for ClassType"); + } + writer.Space(); + } else if (node is DelegateDeclaration) { + writer.WriteKeyword(Roles.DelegateKeyword, "delegate"); + writer.Space(); + } else if (node is EventDeclaration) { + writer.WriteKeyword(EventDeclaration.EventKeywordRole, "event"); + writer.Space(); + } else if (node is NamespaceDeclaration) { + writer.WriteKeyword(Roles.NamespaceKeyword, "namespace"); + writer.Space(); + } + } + + if ((ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { + var rt = node.GetChildByRole(Roles.Type); + if (!rt.IsNull) { + rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + writer.Space(); + } + } + + if (symbol is ITypeDefinition) + WriteTypeDeclarationName((ITypeDefinition)symbol, writer, formattingPolicy); + else if (symbol is IMember) + WriteMemberDeclarationName((IMember)symbol, writer, formattingPolicy); + else + writer.WriteIdentifier(Identifier.Create(symbol.Name)); + + if ((ConversionFlags & ConversionFlags.ShowParameterList) == ConversionFlags.ShowParameterList && HasParameters(symbol)) { + writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.LBracket : Roles.LPar, symbol.SymbolKind == SymbolKind.Indexer ? "[" : "("); + bool first = true; + foreach (var param in node.GetChildrenByRole(Roles.Parameter)) { + if (first) { + first = false; + } else { + writer.WriteToken(Roles.Comma, ","); + writer.Space(); + } + param.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + } + writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.RBracket : Roles.RPar, symbol.SymbolKind == SymbolKind.Indexer ? "]" : ")"); + } + + if ((ConversionFlags & ConversionFlags.ShowBody) == ConversionFlags.ShowBody && !(node is TypeDeclaration)) { + IProperty property = symbol as IProperty; + if (property != null) { + writer.Space(); + writer.WriteToken(Roles.LBrace, "{"); + writer.Space(); + if (property.CanGet) { + writer.WriteKeyword(PropertyDeclaration.GetKeywordRole, "get"); + writer.WriteToken(Roles.Semicolon, ";"); + writer.Space(); + } + if (property.CanSet) { + writer.WriteKeyword(PropertyDeclaration.SetKeywordRole, "set"); + writer.WriteToken(Roles.Semicolon, ";"); + writer.Space(); + } + writer.WriteToken(Roles.RBrace, "}"); + } else { + writer.WriteToken(Roles.Semicolon, ";"); + } + } + } + + static bool HasParameters(ISymbol e) + { + switch (e.SymbolKind) { + case SymbolKind.TypeDefinition: + return ((ITypeDefinition)e).Kind == TypeKind.Delegate; + case SymbolKind.Indexer: + case SymbolKind.Method: + case SymbolKind.Operator: + case SymbolKind.Constructor: + case SymbolKind.Destructor: + return true; + default: + return false; + } + } + + TypeSystemAstBuilder CreateAstBuilder() + { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(); + astBuilder.AddAnnotations = true; + astBuilder.ShowModifiers = (ConversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers; + astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames; + astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; + return astBuilder; + } + + void WriteTypeDeclarationName(ITypeDefinition typeDef, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + EntityDeclaration node = astBuilder.ConvertEntity(typeDef); + if (typeDef.DeclaringTypeDefinition != null) { + WriteTypeDeclarationName(typeDef.DeclaringTypeDefinition, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } else if ((ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) == ConversionFlags.UseFullyQualifiedEntityNames) { + if (!string.IsNullOrEmpty(typeDef.Namespace)) { + WriteQualifiedName(typeDef.Namespace, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } + } + writer.WriteIdentifier(node.NameToken); + if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList) { + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + outputVisitor.WriteTypeParameters(node.GetChildrenByRole(Roles.TypeParameter)); + } + } + + void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + EntityDeclaration node = astBuilder.ConvertEntity(member); + if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType) { + ConvertType(member.DeclaringType, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } + switch (member.SymbolKind) { + case SymbolKind.Indexer: + writer.WriteKeyword(Roles.Identifier, "this"); + break; + case SymbolKind.Constructor: + WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); + break; + case SymbolKind.Destructor: + writer.WriteToken(DestructorDeclaration.TildeRole, "~"); + WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); + break; + case SymbolKind.Operator: + switch (member.Name) { + case "op_Implicit": + writer.WriteKeyword(OperatorDeclaration.ImplicitRole, "implicit"); + writer.Space(); + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + ConvertType(member.ReturnType, writer, formattingPolicy); + break; + case "op_Explicit": + writer.WriteKeyword(OperatorDeclaration.ExplicitRole, "explicit"); + writer.Space(); + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + ConvertType(member.ReturnType, writer, formattingPolicy); + break; + default: + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + var operatorType = OperatorDeclaration.GetOperatorType(member.Name); + if (operatorType.HasValue) + writer.WriteToken(OperatorDeclaration.GetRole(operatorType.Value), OperatorDeclaration.GetToken(operatorType.Value)); + else + writer.WriteIdentifier(node.NameToken); + break; + } + break; + default: + writer.WriteIdentifier(Identifier.Create(member.Name)); + break; + } + if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList && member.SymbolKind == SymbolKind.Method) { + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + outputVisitor.WriteTypeParameters(node.GetChildrenByRole(Roles.TypeParameter)); + } + } + + void PrintModifiers(Modifiers modifiers, TokenWriter writer) + { + foreach (var m in CSharpModifierToken.AllModifiers) { + if ((modifiers & m) == m) { + writer.WriteKeyword(EntityDeclaration.ModifierRole, CSharpModifierToken.GetModifierName(m)); + writer.Space(); + } + } + } + + void WriteQualifiedName(string name, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + var node = AstType.Create(name); + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + node.AcceptVisitor(outputVisitor); + } + #endregion + + public string ConvertVariable(IVariable v) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + AstNode astNode = astBuilder.ConvertVariable(v); + return astNode.ToString().TrimEnd(';', '\r', '\n', (char)8232); + } + + public string ConvertType(IType type) + { + if (type == null) + throw new ArgumentNullException("type"); + + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; + AstType astType = astBuilder.ConvertType(type); + return astType.ToString(); + } + + public void ConvertType(IType type, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; + AstType astType = astBuilder.ConvertType(type); + astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + } + + public string ConvertConstantValue(object constantValue) + { + return TextWriterTokenWriter.PrintPrimitiveValue(constantValue); + } + + public string WrapComment(string comment) + { + return "// " + comment; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs new file mode 100644 index 000000000..4fc5ed9f7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -0,0 +1,2401 @@ +// Copyright (c) 2010-2013 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.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Outputs the AST. + /// + public class CSharpOutputVisitor : IAstVisitor + { + readonly TokenWriter writer; + readonly CSharpFormattingOptions policy; + readonly Stack containerStack = new Stack (); + + public CSharpOutputVisitor (TextWriter textWriter, CSharpFormattingOptions formattingPolicy) + { + if (textWriter == null) { + throw new ArgumentNullException ("textWriter"); + } + if (formattingPolicy == null) { + throw new ArgumentNullException ("formattingPolicy"); + } + this.writer = TokenWriter.Create(textWriter); + this.policy = formattingPolicy; + } + + public CSharpOutputVisitor (TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + if (writer == null) { + throw new ArgumentNullException ("writer"); + } + if (formattingPolicy == null) { + throw new ArgumentNullException ("formattingPolicy"); + } + this.writer = new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(writer)); + this.policy = formattingPolicy; + } + + #region StartNode/EndNode + void StartNode(AstNode node) + { + // Ensure that nodes are visited in the proper nested order. + // Jumps to different subtrees are allowed only for the child of a placeholder node. + Debug.Assert(containerStack.Count == 0 || node.Parent == containerStack.Peek() || containerStack.Peek().NodeType == NodeType.Pattern); + containerStack.Push(node); + writer.StartNode(node); + } + + void EndNode(AstNode node) + { + Debug.Assert(node == containerStack.Peek()); + containerStack.Pop(); + writer.EndNode(node); + } + #endregion + + #region Comma + /// + /// Writes a comma. + /// + /// The next node after the comma. + /// When set prevents printing a space after comma. + void Comma(AstNode nextNode, bool noSpaceAfterComma = false) + { + Space(policy.SpaceBeforeBracketComma); + // TODO: Comma policy has changed. + writer.WriteToken(Roles.Comma, ","); + Space(!noSpaceAfterComma && policy.SpaceAfterBracketComma); + // TODO: Comma policy has changed. + } + + /// + /// Writes an optional comma, e.g. at the end of an enum declaration or in an array initializer + /// + void OptionalComma(AstNode pos) + { + // Look if there's a comma after the current node, and insert it if it exists. + while (pos != null && pos.NodeType == NodeType.Whitespace) { + pos = pos.NextSibling; + } + if (pos != null && pos.Role == Roles.Comma) { + Comma(null, noSpaceAfterComma: true); + } + } + + /// + /// Writes an optional semicolon, e.g. at the end of a type or namespace declaration. + /// + void OptionalSemicolon(AstNode pos) + { + // Look if there's a semicolon after the current node, and insert it if it exists. + while (pos != null && pos.NodeType == NodeType.Whitespace) { + pos = pos.PrevSibling; + } + if (pos != null && pos.Role == Roles.Semicolon) { + Semicolon(); + } + } + + void WriteCommaSeparatedList(IEnumerable list) + { + bool isFirst = true; + foreach (AstNode node in list) { + if (isFirst) { + isFirst = false; + } else { + Comma(node); + } + node.AcceptVisitor(this); + } + } + + void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + LPar(); + if (list.Any()) { + Space(spaceWithin); + WriteCommaSeparatedList(list); + Space(spaceWithin); + } + RPar(); + } + + #if DOTNET35 + void WriteCommaSeparatedList(IEnumerable list) + { + WriteCommaSeparatedList(list.SafeCast()); + } + + void WriteCommaSeparatedList(IEnumerable list) + { + WriteCommaSeparatedList(list.SafeCast()); + } + + void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); + } + + void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); + } + + #endif + + void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) + { + WriteToken(Roles.LBracket); + if (list.Any()) { + Space(spaceWithin); + WriteCommaSeparatedList(list); + Space(spaceWithin); + } + WriteToken(Roles.RBracket); + } + + void WriteCommaSeparatedListInBrackets(IEnumerable list) + { + WriteToken(Roles.LBracket); + if (list.Any()) { + Space(policy.SpacesWithinBrackets); + WriteCommaSeparatedList(list); + Space(policy.SpacesWithinBrackets); + } + WriteToken(Roles.RBracket); + } + #endregion + + #region Write tokens + bool isAtStartOfLine = true; + + /// + /// Writes a keyword, and all specials up to + /// + void WriteKeyword(TokenRole tokenRole) + { + WriteKeyword(tokenRole.Token, tokenRole); + } + + void WriteKeyword(string token, Role tokenRole = null) + { + writer.WriteKeyword(tokenRole, token); + isAtStartOfLine = false; + } + + void WriteIdentifier(Identifier identifier) + { + writer.WriteIdentifier(identifier); + isAtStartOfLine = false; + } + + void WriteIdentifier(string identifier) + { + AstType.Create(identifier).AcceptVisitor(this); + isAtStartOfLine = false; + } + + void WriteToken(TokenRole tokenRole) + { + WriteToken(tokenRole.Token, tokenRole); + } + + void WriteToken(string token, Role tokenRole) + { + writer.WriteToken(tokenRole, token); + isAtStartOfLine = false; + } + + void LPar() + { + WriteToken(Roles.LPar); + } + + void RPar() + { + WriteToken(Roles.RPar); + } + + /// + /// Marks the end of a statement + /// + void Semicolon() + { + Role role = containerStack.Peek().Role; + // get the role of the current node + if (!(role == ForStatement.InitializerRole || role == ForStatement.IteratorRole || role == UsingStatement.ResourceAcquisitionRole)) { + WriteToken(Roles.Semicolon); + NewLine(); + } + } + + /// + /// Writes a space depending on policy. + /// + void Space(bool addSpace = true) + { + if (addSpace) { + writer.Space(); + } + } + + void NewLine() + { + writer.NewLine(); + isAtStartOfLine = true; + } + + void OpenBrace(BraceStyle style) + { + switch (style) { + case BraceStyle.DoNotChange: + case BraceStyle.EndOfLine: + case BraceStyle.BannerStyle: + if (!isAtStartOfLine) + writer.Space(); + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.EndOfLineWithoutSpace: + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.NextLine: + if (!isAtStartOfLine) + NewLine(); + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.NextLineShifted: + NewLine(); + writer.Indent(); + writer.WriteToken(Roles.LBrace, "{"); + NewLine(); + return; + case BraceStyle.NextLineShifted2: + NewLine(); + writer.Indent(); + writer.WriteToken(Roles.LBrace, "{"); + break; + default: + throw new ArgumentOutOfRangeException (); + } + writer.Indent(); + NewLine(); + } + + void CloseBrace(BraceStyle style) + { + switch (style) { + case BraceStyle.DoNotChange: + case BraceStyle.EndOfLine: + case BraceStyle.EndOfLineWithoutSpace: + case BraceStyle.NextLine: + writer.Unindent(); + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + break; + case BraceStyle.BannerStyle: + case BraceStyle.NextLineShifted: + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + writer.Unindent(); + break; + case BraceStyle.NextLineShifted2: + writer.Unindent(); + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + writer.Unindent(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + #endregion + + #region IsKeyword Test + static readonly HashSet unconditionalKeywords = new HashSet { + "abstract", "as", "base", "bool", "break", "byte", "case", "catch", + "char", "checked", "class", "const", "continue", "decimal", "default", "delegate", + "do", "double", "else", "enum", "event", "explicit", "extern", "false", + "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", + "in", "int", "interface", "internal", "is", "lock", "long", "namespace", + "new", "null", "object", "operator", "out", "override", "params", "private", + "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", + "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", + "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", + "using", "virtual", "void", "volatile", "while" + }; + static readonly HashSet queryKeywords = new HashSet { + "from", "where", "join", "on", "equals", "into", "let", "orderby", + "ascending", "descending", "select", "group", "by" + }; + + /// + /// Determines whether the specified identifier is a keyword in the given context. + /// + public static bool IsKeyword(string identifier, AstNode context) + { + if (unconditionalKeywords.Contains(identifier)) { + return true; + } + foreach (AstNode ancestor in context.Ancestors) { + if (ancestor is QueryExpression && queryKeywords.Contains(identifier)) { + return true; + } + if (identifier == "await") { + // with lambdas/anonymous methods, + if (ancestor is LambdaExpression) { + return ((LambdaExpression)ancestor).IsAsync; + } + if (ancestor is AnonymousMethodExpression) { + return ((AnonymousMethodExpression)ancestor).IsAsync; + } + if (ancestor is EntityDeclaration) { + return (((EntityDeclaration)ancestor).Modifiers & Modifiers.Async) == Modifiers.Async; + } + } + } + return false; + } + #endregion + + #region Write constructs + void WriteTypeArguments(IEnumerable typeArguments) + { + if (typeArguments.Any()) { + WriteToken(Roles.LChevron); + WriteCommaSeparatedList(typeArguments); + WriteToken(Roles.RChevron); + } + } + + public void WriteTypeParameters(IEnumerable typeParameters) + { + if (typeParameters.Any()) { + WriteToken(Roles.LChevron); + WriteCommaSeparatedList(typeParameters); + WriteToken(Roles.RChevron); + } + } + + void WriteModifiers(IEnumerable modifierTokens) + { + foreach (CSharpModifierToken modifier in modifierTokens) { + modifier.AcceptVisitor(this); + } + } + + void WriteQualifiedIdentifier(IEnumerable identifiers) + { + bool first = true; + foreach (Identifier ident in identifiers) { + if (first) { + first = false; + } else { + writer.WriteToken(Roles.Dot, "."); + } + writer.WriteIdentifier(ident); + } + } + + void WriteEmbeddedStatement(Statement embeddedStatement) + { + if (embeddedStatement.IsNull) { + NewLine(); + return; + } + BlockStatement block = embeddedStatement as BlockStatement; + if (block != null) { + VisitBlockStatement(block); + } else { + NewLine(); + writer.Indent(); + embeddedStatement.AcceptVisitor(this); + writer.Unindent(); + } + } + + void WriteMethodBody(BlockStatement body) + { + if (body.IsNull) { + Semicolon(); + } else { + VisitBlockStatement(body); + } + } + + void WriteAttributes(IEnumerable attributes) + { + foreach (AttributeSection attr in attributes) { + attr.AcceptVisitor(this); + } + } + + void WritePrivateImplementationType(AstType privateImplementationType) + { + if (!privateImplementationType.IsNull) { + privateImplementationType.AcceptVisitor(this); + WriteToken(Roles.Dot); + } + } + + #endregion + + #region Expressions + public void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + StartNode(anonymousMethodExpression); + if (anonymousMethodExpression.IsAsync) { + WriteKeyword(AnonymousMethodExpression.AsyncModifierRole); + Space(); + } + WriteKeyword(AnonymousMethodExpression.DelegateKeywordRole); + if (anonymousMethodExpression.HasParameterList) { + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(anonymousMethodExpression.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } + anonymousMethodExpression.Body.AcceptVisitor(this); + EndNode(anonymousMethodExpression); + } + + public void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) + { + StartNode(undocumentedExpression); + switch (undocumentedExpression.UndocumentedExpressionType) { + case UndocumentedExpressionType.ArgList: + case UndocumentedExpressionType.ArgListAccess: + WriteKeyword(UndocumentedExpression.ArglistKeywordRole); + break; + case UndocumentedExpressionType.MakeRef: + WriteKeyword(UndocumentedExpression.MakerefKeywordRole); + break; + case UndocumentedExpressionType.RefType: + WriteKeyword(UndocumentedExpression.ReftypeKeywordRole); + break; + case UndocumentedExpressionType.RefValue: + WriteKeyword(UndocumentedExpression.RefvalueKeywordRole); + break; + } + if (undocumentedExpression.Arguments.Count > 0) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(undocumentedExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + } + EndNode(undocumentedExpression); + } + + public void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) + { + StartNode(arrayCreateExpression); + WriteKeyword(ArrayCreateExpression.NewKeywordRole); + arrayCreateExpression.Type.AcceptVisitor(this); + if (arrayCreateExpression.Arguments.Count > 0) { + WriteCommaSeparatedListInBrackets(arrayCreateExpression.Arguments); + } + foreach (var specifier in arrayCreateExpression.AdditionalArraySpecifiers) { + specifier.AcceptVisitor(this); + } + arrayCreateExpression.Initializer.AcceptVisitor(this); + EndNode(arrayCreateExpression); + } + + public void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + StartNode(arrayInitializerExpression); + // "new List { { 1 } }" and "new List { 1 }" are the same semantically. + // We also use the same AST for both: we always use two nested ArrayInitializerExpressions + // for collection initializers, even if the user did not write nested brackets. + // The output visitor will output nested braces only if they are necessary, + // or if the braces tokens exist in the AST. + bool bracesAreOptional = arrayInitializerExpression.Elements.Count == 1 + && IsObjectOrCollectionInitializer(arrayInitializerExpression.Parent) + && !CanBeConfusedWithObjectInitializer(arrayInitializerExpression.Elements.Single()); + if (bracesAreOptional && arrayInitializerExpression.LBraceToken.IsNull) { + arrayInitializerExpression.Elements.Single().AcceptVisitor(this); + } else { + PrintInitializerElements(arrayInitializerExpression.Elements); + } + EndNode(arrayInitializerExpression); + } + + bool CanBeConfusedWithObjectInitializer(Expression expr) + { + // "int a; new List { a = 1 };" is an object initalizers and invalid, but + // "int a; new List { { a = 1 } };" is a valid collection initializer. + AssignmentExpression ae = expr as AssignmentExpression; + return ae != null && ae.Operator == AssignmentOperatorType.Assign; + } + + bool IsObjectOrCollectionInitializer(AstNode node) + { + if (!(node is ArrayInitializerExpression)) { + return false; + } + if (node.Parent is ObjectCreateExpression) { + return node.Role == ObjectCreateExpression.InitializerRole; + } + if (node.Parent is NamedExpression) { + return node.Role == Roles.Expression; + } + return false; + } + + void PrintInitializerElements(AstNodeCollection elements) + { + BraceStyle style; + if (policy.ArrayInitializerWrapping == Wrapping.WrapAlways) { + style = BraceStyle.NextLine; + } else { + style = BraceStyle.EndOfLine; + } + OpenBrace(style); + bool isFirst = true; + AstNode last = null; + foreach (AstNode node in elements) { + if (isFirst) { + isFirst = false; + } else { + Comma(node, noSpaceAfterComma: true); + NewLine(); + } + last = node; + node.AcceptVisitor(this); + } + if (last != null) + OptionalComma(last.NextSibling); + NewLine(); + CloseBrace(style); + } + + public void VisitAsExpression(AsExpression asExpression) + { + StartNode(asExpression); + asExpression.Expression.AcceptVisitor(this); + Space(); + WriteKeyword(AsExpression.AsKeywordRole); + Space(); + asExpression.Type.AcceptVisitor(this); + EndNode(asExpression); + } + + public void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + StartNode(assignmentExpression); + assignmentExpression.Left.AcceptVisitor(this); + Space(policy.SpaceAroundAssignment); + WriteToken(AssignmentExpression.GetOperatorRole(assignmentExpression.Operator)); + Space(policy.SpaceAroundAssignment); + assignmentExpression.Right.AcceptVisitor(this); + EndNode(assignmentExpression); + } + + public void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) + { + StartNode(baseReferenceExpression); + WriteKeyword("base", baseReferenceExpression.Role); + EndNode(baseReferenceExpression); + } + + public void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + StartNode(binaryOperatorExpression); + binaryOperatorExpression.Left.AcceptVisitor(this); + bool spacePolicy; + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.BitwiseAnd: + case BinaryOperatorType.BitwiseOr: + case BinaryOperatorType.ExclusiveOr: + spacePolicy = policy.SpaceAroundBitwiseOperator; + break; + case BinaryOperatorType.ConditionalAnd: + case BinaryOperatorType.ConditionalOr: + spacePolicy = policy.SpaceAroundLogicalOperator; + break; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThanOrEqual: + case BinaryOperatorType.LessThan: + spacePolicy = policy.SpaceAroundRelationalOperator; + break; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + spacePolicy = policy.SpaceAroundEqualityOperator; + break; + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + spacePolicy = policy.SpaceAroundAdditiveOperator; + break; + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + spacePolicy = policy.SpaceAroundMultiplicativeOperator; + break; + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + spacePolicy = policy.SpaceAroundShiftOperator; + break; + case BinaryOperatorType.NullCoalescing: + spacePolicy = true; + break; + default: + throw new NotSupportedException ("Invalid value for BinaryOperatorType"); + } + Space(spacePolicy); + WriteToken(BinaryOperatorExpression.GetOperatorRole(binaryOperatorExpression.Operator)); + Space(spacePolicy); + binaryOperatorExpression.Right.AcceptVisitor(this); + EndNode(binaryOperatorExpression); + } + + public void VisitCastExpression(CastExpression castExpression) + { + StartNode(castExpression); + LPar(); + Space(policy.SpacesWithinCastParentheses); + castExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinCastParentheses); + RPar(); + Space(policy.SpaceAfterTypecast); + castExpression.Expression.AcceptVisitor(this); + EndNode(castExpression); + } + + public void VisitCheckedExpression(CheckedExpression checkedExpression) + { + StartNode(checkedExpression); + WriteKeyword(CheckedExpression.CheckedKeywordRole); + LPar(); + Space(policy.SpacesWithinCheckedExpressionParantheses); + checkedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinCheckedExpressionParantheses); + RPar(); + EndNode(checkedExpression); + } + + public void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + StartNode(conditionalExpression); + conditionalExpression.Condition.AcceptVisitor(this); + + Space(policy.SpaceBeforeConditionalOperatorCondition); + WriteToken(ConditionalExpression.QuestionMarkRole); + Space(policy.SpaceAfterConditionalOperatorCondition); + + conditionalExpression.TrueExpression.AcceptVisitor(this); + + Space(policy.SpaceBeforeConditionalOperatorSeparator); + WriteToken(ConditionalExpression.ColonRole); + Space(policy.SpaceAfterConditionalOperatorSeparator); + + conditionalExpression.FalseExpression.AcceptVisitor(this); + + EndNode(conditionalExpression); + } + + public void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) + { + StartNode(defaultValueExpression); + + WriteKeyword(DefaultValueExpression.DefaultKeywordRole); + LPar(); + Space(policy.SpacesWithinTypeOfParentheses); + defaultValueExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinTypeOfParentheses); + RPar(); + + EndNode(defaultValueExpression); + } + + public void VisitDirectionExpression(DirectionExpression directionExpression) + { + StartNode(directionExpression); + + switch (directionExpression.FieldDirection) { + case FieldDirection.Out: + WriteKeyword(DirectionExpression.OutKeywordRole); + break; + case FieldDirection.Ref: + WriteKeyword(DirectionExpression.RefKeywordRole); + break; + default: + throw new NotSupportedException ("Invalid value for FieldDirection"); + } + Space(); + directionExpression.Expression.AcceptVisitor(this); + + EndNode(directionExpression); + } + + public void VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + StartNode(identifierExpression); + WriteIdentifier(identifierExpression.IdentifierToken); + WriteTypeArguments(identifierExpression.TypeArguments); + EndNode(identifierExpression); + } + + public void VisitIndexerExpression(IndexerExpression indexerExpression) + { + StartNode(indexerExpression); + indexerExpression.Target.AcceptVisitor(this); + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInBrackets(indexerExpression.Arguments); + EndNode(indexerExpression); + } + + public void VisitInvocationExpression(InvocationExpression invocationExpression) + { + StartNode(invocationExpression); + invocationExpression.Target.AcceptVisitor(this); + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(invocationExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + EndNode(invocationExpression); + } + + public void VisitIsExpression(IsExpression isExpression) + { + StartNode(isExpression); + isExpression.Expression.AcceptVisitor(this); + Space(); + WriteKeyword(IsExpression.IsKeywordRole); + isExpression.Type.AcceptVisitor(this); + EndNode(isExpression); + } + + public void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + StartNode(lambdaExpression); + if (lambdaExpression.IsAsync) { + WriteKeyword(LambdaExpression.AsyncModifierRole); + Space(); + } + if (LambdaNeedsParenthesis(lambdaExpression)) { + WriteCommaSeparatedListInParenthesis(lambdaExpression.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } else { + lambdaExpression.Parameters.Single().AcceptVisitor(this); + } + Space(); + WriteToken(LambdaExpression.ArrowRole); + Space(); + lambdaExpression.Body.AcceptVisitor(this); + EndNode(lambdaExpression); + } + + bool LambdaNeedsParenthesis(LambdaExpression lambdaExpression) + { + if (lambdaExpression.Parameters.Count != 1) { + return true; + } + var p = lambdaExpression.Parameters.Single(); + return !(p.Type.IsNull && p.ParameterModifier == ParameterModifier.None); + } + + public void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + StartNode(memberReferenceExpression); + memberReferenceExpression.Target.AcceptVisitor(this); + WriteToken(Roles.Dot); + WriteIdentifier(memberReferenceExpression.MemberNameToken); + WriteTypeArguments(memberReferenceExpression.TypeArguments); + EndNode(memberReferenceExpression); + } + + public void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + StartNode(namedArgumentExpression); + WriteIdentifier(namedArgumentExpression.NameToken); + WriteToken(Roles.Colon); + Space(); + namedArgumentExpression.Expression.AcceptVisitor(this); + EndNode(namedArgumentExpression); + } + + public void VisitNamedExpression(NamedExpression namedExpression) + { + StartNode(namedExpression); + WriteIdentifier(namedExpression.NameToken); + Space(); + WriteToken(Roles.Assign); + Space(); + namedExpression.Expression.AcceptVisitor(this); + EndNode(namedExpression); + } + + public void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) + { + StartNode(nullReferenceExpression); + writer.WritePrimitiveValue(null); + EndNode(nullReferenceExpression); + } + + public void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + StartNode(objectCreateExpression); + WriteKeyword(ObjectCreateExpression.NewKeywordRole); + objectCreateExpression.Type.AcceptVisitor(this); + bool useParenthesis = objectCreateExpression.Arguments.Any() || objectCreateExpression.Initializer.IsNull; + // also use parenthesis if there is an '(' token + if (!objectCreateExpression.LParToken.IsNull) { + useParenthesis = true; + } + if (useParenthesis) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(objectCreateExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + } + objectCreateExpression.Initializer.AcceptVisitor(this); + EndNode(objectCreateExpression); + } + + public void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + StartNode(anonymousTypeCreateExpression); + WriteKeyword(AnonymousTypeCreateExpression.NewKeywordRole); + PrintInitializerElements(anonymousTypeCreateExpression.Initializers); + EndNode(anonymousTypeCreateExpression); + } + + public void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + StartNode(parenthesizedExpression); + LPar(); + Space(policy.SpacesWithinParentheses); + parenthesizedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinParentheses); + RPar(); + EndNode(parenthesizedExpression); + } + + public void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + StartNode(pointerReferenceExpression); + pointerReferenceExpression.Target.AcceptVisitor(this); + WriteToken(PointerReferenceExpression.ArrowRole); + WriteIdentifier(pointerReferenceExpression.MemberNameToken); + WriteTypeArguments(pointerReferenceExpression.TypeArguments); + EndNode(pointerReferenceExpression); + } + + #region VisitPrimitiveExpression + public void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + StartNode(primitiveExpression); + writer.WritePrimitiveValue(primitiveExpression.Value, primitiveExpression.UnsafeLiteralValue); + EndNode(primitiveExpression); + } + #endregion + + public void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + StartNode(sizeOfExpression); + + WriteKeyword(SizeOfExpression.SizeofKeywordRole); + LPar(); + Space(policy.SpacesWithinSizeOfParentheses); + sizeOfExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinSizeOfParentheses); + RPar(); + + EndNode(sizeOfExpression); + } + + public void VisitStackAllocExpression(StackAllocExpression stackAllocExpression) + { + StartNode(stackAllocExpression); + WriteKeyword(StackAllocExpression.StackallocKeywordRole); + stackAllocExpression.Type.AcceptVisitor(this); + WriteCommaSeparatedListInBrackets(new[] { stackAllocExpression.CountExpression }); + EndNode(stackAllocExpression); + } + + public void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + StartNode(thisReferenceExpression); + WriteKeyword("this", thisReferenceExpression.Role); + EndNode(thisReferenceExpression); + } + + public void VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + StartNode(typeOfExpression); + + WriteKeyword(TypeOfExpression.TypeofKeywordRole); + LPar(); + Space(policy.SpacesWithinTypeOfParentheses); + typeOfExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinTypeOfParentheses); + RPar(); + + EndNode(typeOfExpression); + } + + public void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + StartNode(typeReferenceExpression); + typeReferenceExpression.Type.AcceptVisitor(this); + EndNode(typeReferenceExpression); + } + + public void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + StartNode(unaryOperatorExpression); + UnaryOperatorType opType = unaryOperatorExpression.Operator; + var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); + if (opType == UnaryOperatorType.Await) { + WriteKeyword(opSymbol); + } else if (!(opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement)) { + WriteToken(opSymbol); + } + unaryOperatorExpression.Expression.AcceptVisitor(this); + if (opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement) { + WriteToken(opSymbol); + } + EndNode(unaryOperatorExpression); + } + + public void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + StartNode(uncheckedExpression); + WriteKeyword(UncheckedExpression.UncheckedKeywordRole); + LPar(); + Space(policy.SpacesWithinCheckedExpressionParantheses); + uncheckedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinCheckedExpressionParantheses); + RPar(); + EndNode(uncheckedExpression); + } + + #endregion + + #region Query Expressions + public void VisitQueryExpression(QueryExpression queryExpression) + { + StartNode(queryExpression); + bool indent = queryExpression.Parent is QueryClause && !(queryExpression.Parent is QueryContinuationClause); + if (indent) { + writer.Indent(); + NewLine(); + } + bool first = true; + foreach (var clause in queryExpression.Clauses) { + if (first) { + first = false; + } else { + if (!(clause is QueryContinuationClause)) { + NewLine(); + } + } + clause.AcceptVisitor(this); + } + if (indent) { + writer.Unindent(); + } + EndNode(queryExpression); + } + + public void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + StartNode(queryContinuationClause); + queryContinuationClause.PrecedingQuery.AcceptVisitor(this); + Space(); + WriteKeyword(QueryContinuationClause.IntoKeywordRole); + Space(); + WriteIdentifier(queryContinuationClause.IdentifierToken); + EndNode(queryContinuationClause); + } + + public void VisitQueryFromClause(QueryFromClause queryFromClause) + { + StartNode(queryFromClause); + WriteKeyword(QueryFromClause.FromKeywordRole); + queryFromClause.Type.AcceptVisitor(this); + Space(); + WriteIdentifier(queryFromClause.IdentifierToken); + Space(); + WriteKeyword(QueryFromClause.InKeywordRole); + Space(); + queryFromClause.Expression.AcceptVisitor(this); + EndNode(queryFromClause); + } + + public void VisitQueryLetClause(QueryLetClause queryLetClause) + { + StartNode(queryLetClause); + WriteKeyword(QueryLetClause.LetKeywordRole); + Space(); + WriteIdentifier(queryLetClause.IdentifierToken); + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + queryLetClause.Expression.AcceptVisitor(this); + EndNode(queryLetClause); + } + + public void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + StartNode(queryWhereClause); + WriteKeyword(QueryWhereClause.WhereKeywordRole); + Space(); + queryWhereClause.Condition.AcceptVisitor(this); + EndNode(queryWhereClause); + } + + public void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + StartNode(queryJoinClause); + WriteKeyword(QueryJoinClause.JoinKeywordRole); + queryJoinClause.Type.AcceptVisitor(this); + Space(); + WriteIdentifier(queryJoinClause.JoinIdentifierToken); + Space(); + WriteKeyword(QueryJoinClause.InKeywordRole); + Space(); + queryJoinClause.InExpression.AcceptVisitor(this); + Space(); + WriteKeyword(QueryJoinClause.OnKeywordRole); + Space(); + queryJoinClause.OnExpression.AcceptVisitor(this); + Space(); + WriteKeyword(QueryJoinClause.EqualsKeywordRole); + Space(); + queryJoinClause.EqualsExpression.AcceptVisitor(this); + if (queryJoinClause.IsGroupJoin) { + Space(); + WriteKeyword(QueryJoinClause.IntoKeywordRole); + WriteIdentifier(queryJoinClause.IntoIdentifierToken); + } + EndNode(queryJoinClause); + } + + public void VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + StartNode(queryOrderClause); + WriteKeyword(QueryOrderClause.OrderbyKeywordRole); + Space(); + WriteCommaSeparatedList(queryOrderClause.Orderings); + EndNode(queryOrderClause); + } + + public void VisitQueryOrdering(QueryOrdering queryOrdering) + { + StartNode(queryOrdering); + queryOrdering.Expression.AcceptVisitor(this); + switch (queryOrdering.Direction) { + case QueryOrderingDirection.Ascending: + Space(); + WriteKeyword(QueryOrdering.AscendingKeywordRole); + break; + case QueryOrderingDirection.Descending: + Space(); + WriteKeyword(QueryOrdering.DescendingKeywordRole); + break; + } + EndNode(queryOrdering); + } + + public void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + StartNode(querySelectClause); + WriteKeyword(QuerySelectClause.SelectKeywordRole); + Space(); + querySelectClause.Expression.AcceptVisitor(this); + EndNode(querySelectClause); + } + + public void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + StartNode(queryGroupClause); + WriteKeyword(QueryGroupClause.GroupKeywordRole); + Space(); + queryGroupClause.Projection.AcceptVisitor(this); + Space(); + WriteKeyword(QueryGroupClause.ByKeywordRole); + Space(); + queryGroupClause.Key.AcceptVisitor(this); + EndNode(queryGroupClause); + } + + #endregion + + #region GeneralScope + public void VisitAttribute(Attribute attribute) + { + StartNode(attribute); + attribute.Type.AcceptVisitor(this); + if (attribute.Arguments.Count != 0 || !attribute.GetChildByRole(Roles.LPar).IsNull) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(attribute.Arguments, policy.SpaceWithinMethodCallParentheses); + } + EndNode(attribute); + } + + public void VisitAttributeSection(AttributeSection attributeSection) + { + StartNode(attributeSection); + WriteToken(Roles.LBracket); + if (!string.IsNullOrEmpty(attributeSection.AttributeTarget)) { + WriteIdentifier(attributeSection.AttributeTargetToken); + WriteToken(Roles.Colon); + Space(); + } + WriteCommaSeparatedList(attributeSection.Attributes); + WriteToken(Roles.RBracket); + if (attributeSection.Parent is ParameterDeclaration || attributeSection.Parent is TypeParameterDeclaration) { + Space(); + } else { + NewLine(); + } + EndNode(attributeSection); + } + + public void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + StartNode(delegateDeclaration); + WriteAttributes(delegateDeclaration.Attributes); + WriteModifiers(delegateDeclaration.ModifierTokens); + WriteKeyword(Roles.DelegateKeyword); + delegateDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteIdentifier(delegateDeclaration.NameToken); + WriteTypeParameters(delegateDeclaration.TypeParameters); + Space(policy.SpaceBeforeDelegateDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(delegateDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + foreach (Constraint constraint in delegateDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + Semicolon(); + EndNode(delegateDeclaration); + } + + public void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + StartNode(namespaceDeclaration); + WriteKeyword(Roles.NamespaceKeyword); + namespaceDeclaration.NamespaceName.AcceptVisitor (this); + OpenBrace(policy.NamespaceBraceStyle); + foreach (var member in namespaceDeclaration.Members) { + member.AcceptVisitor(this); + MaybeNewLinesAfterUsings(member); + } + CloseBrace(policy.NamespaceBraceStyle); + OptionalSemicolon(namespaceDeclaration.LastChild); + NewLine(); + EndNode(namespaceDeclaration); + } + + public void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + StartNode(typeDeclaration); + WriteAttributes(typeDeclaration.Attributes); + WriteModifiers(typeDeclaration.ModifierTokens); + BraceStyle braceStyle; + switch (typeDeclaration.ClassType) { + case ClassType.Enum: + WriteKeyword(Roles.EnumKeyword); + braceStyle = policy.EnumBraceStyle; + break; + case ClassType.Interface: + WriteKeyword(Roles.InterfaceKeyword); + braceStyle = policy.InterfaceBraceStyle; + break; + case ClassType.Struct: + WriteKeyword(Roles.StructKeyword); + braceStyle = policy.StructBraceStyle; + break; + default: + WriteKeyword(Roles.ClassKeyword); + braceStyle = policy.ClassBraceStyle; + break; + } + WriteIdentifier(typeDeclaration.NameToken); + WriteTypeParameters(typeDeclaration.TypeParameters); + if (typeDeclaration.BaseTypes.Any()) { + Space(); + WriteToken(Roles.Colon); + Space(); + WriteCommaSeparatedList(typeDeclaration.BaseTypes); + } + foreach (Constraint constraint in typeDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + OpenBrace(braceStyle); + if (typeDeclaration.ClassType == ClassType.Enum) { + bool first = true; + AstNode last = null; + foreach (var member in typeDeclaration.Members) { + if (first) { + first = false; + } else { + Comma(member, noSpaceAfterComma: true); + NewLine(); + } + last = member; + member.AcceptVisitor(this); + } + if (last != null) + OptionalComma(last.NextSibling); + NewLine(); + } else { + bool first = true; + foreach (var member in typeDeclaration.Members) { + if (!first) { + for (int i = 0; i < policy.MinimumBlankLinesBetweenMembers; i++) + NewLine(); + } + first = false; + member.AcceptVisitor(this); + } + } + CloseBrace(braceStyle); + OptionalSemicolon(typeDeclaration.LastChild); + NewLine(); + EndNode(typeDeclaration); + } + + public void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration) + { + StartNode(usingAliasDeclaration); + WriteKeyword(UsingAliasDeclaration.UsingKeywordRole); + WriteIdentifier(usingAliasDeclaration.GetChildByRole(UsingAliasDeclaration.AliasRole)); + Space(policy.SpaceAroundEqualityOperator); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundEqualityOperator); + usingAliasDeclaration.Import.AcceptVisitor(this); + Semicolon(); + EndNode(usingAliasDeclaration); + } + + public void VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + StartNode(usingDeclaration); + WriteKeyword(UsingDeclaration.UsingKeywordRole); + usingDeclaration.Import.AcceptVisitor(this); + Semicolon(); + EndNode(usingDeclaration); + } + + public void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + StartNode(externAliasDeclaration); + WriteKeyword(Roles.ExternKeyword); + Space(); + WriteKeyword(Roles.AliasKeyword); + Space(); + WriteIdentifier(externAliasDeclaration.NameToken); + Semicolon(); + EndNode(externAliasDeclaration); + } + + #endregion + + #region Statements + public void VisitBlockStatement(BlockStatement blockStatement) + { + StartNode(blockStatement); + BraceStyle style; + if (blockStatement.Parent is AnonymousMethodExpression || blockStatement.Parent is LambdaExpression) { + style = policy.AnonymousMethodBraceStyle; + } else if (blockStatement.Parent is ConstructorDeclaration) { + style = policy.ConstructorBraceStyle; + } else if (blockStatement.Parent is DestructorDeclaration) { + style = policy.DestructorBraceStyle; + } else if (blockStatement.Parent is MethodDeclaration) { + style = policy.MethodBraceStyle; + } else if (blockStatement.Parent is Accessor) { + if (blockStatement.Parent.Role == PropertyDeclaration.GetterRole) { + style = policy.PropertyGetBraceStyle; + } else if (blockStatement.Parent.Role == PropertyDeclaration.SetterRole) { + style = policy.PropertySetBraceStyle; + } else if (blockStatement.Parent.Role == CustomEventDeclaration.AddAccessorRole) { + style = policy.EventAddBraceStyle; + } else if (blockStatement.Parent.Role == CustomEventDeclaration.RemoveAccessorRole) { + style = policy.EventRemoveBraceStyle; + } else { + style = policy.StatementBraceStyle; + } + } else { + style = policy.StatementBraceStyle; + } + OpenBrace(style); + foreach (var node in blockStatement.Statements) { + node.AcceptVisitor(this); + } + EndNode(blockStatement); + CloseBrace(style); + if (!(blockStatement.Parent is Expression)) + NewLine(); + } + + public void VisitBreakStatement(BreakStatement breakStatement) + { + StartNode(breakStatement); + WriteKeyword("break", BreakStatement.BreakKeywordRole); + Semicolon(); + EndNode(breakStatement); + } + + public void VisitCheckedStatement(CheckedStatement checkedStatement) + { + StartNode(checkedStatement); + WriteKeyword(CheckedStatement.CheckedKeywordRole); + checkedStatement.Body.AcceptVisitor(this); + EndNode(checkedStatement); + } + + public void VisitContinueStatement(ContinueStatement continueStatement) + { + StartNode(continueStatement); + WriteKeyword("continue", ContinueStatement.ContinueKeywordRole); + Semicolon(); + EndNode(continueStatement); + } + + public void VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + StartNode(doWhileStatement); + WriteKeyword(DoWhileStatement.DoKeywordRole); + WriteEmbeddedStatement(doWhileStatement.EmbeddedStatement); + WriteKeyword(DoWhileStatement.WhileKeywordRole); + Space(policy.SpaceBeforeWhileParentheses); + LPar(); + Space(policy.SpacesWithinWhileParentheses); + doWhileStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinWhileParentheses); + RPar(); + Semicolon(); + EndNode(doWhileStatement); + } + + public void VisitEmptyStatement(EmptyStatement emptyStatement) + { + StartNode(emptyStatement); + Semicolon(); + EndNode(emptyStatement); + } + + public void VisitExpressionStatement(ExpressionStatement expressionStatement) + { + StartNode(expressionStatement); + expressionStatement.Expression.AcceptVisitor(this); + Semicolon(); + EndNode(expressionStatement); + } + + public void VisitFixedStatement(FixedStatement fixedStatement) + { + StartNode(fixedStatement); + WriteKeyword(FixedStatement.FixedKeywordRole); + Space(policy.SpaceBeforeUsingParentheses); + LPar(); + Space(policy.SpacesWithinUsingParentheses); + fixedStatement.Type.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fixedStatement.Variables); + Space(policy.SpacesWithinUsingParentheses); + RPar(); + WriteEmbeddedStatement(fixedStatement.EmbeddedStatement); + EndNode(fixedStatement); + } + + public void VisitForeachStatement(ForeachStatement foreachStatement) + { + StartNode(foreachStatement); + WriteKeyword(ForeachStatement.ForeachKeywordRole); + Space(policy.SpaceBeforeForeachParentheses); + LPar(); + Space(policy.SpacesWithinForeachParentheses); + foreachStatement.VariableType.AcceptVisitor(this); + Space(); + WriteIdentifier(foreachStatement.VariableNameToken); + WriteKeyword(ForeachStatement.InKeywordRole); + Space(); + foreachStatement.InExpression.AcceptVisitor(this); + Space(policy.SpacesWithinForeachParentheses); + RPar(); + WriteEmbeddedStatement(foreachStatement.EmbeddedStatement); + EndNode(foreachStatement); + } + + public void VisitForStatement(ForStatement forStatement) + { + StartNode(forStatement); + WriteKeyword(ForStatement.ForKeywordRole); + Space(policy.SpaceBeforeForParentheses); + LPar(); + Space(policy.SpacesWithinForParentheses); + + WriteCommaSeparatedList(forStatement.Initializers); + Space(policy.SpaceBeforeForSemicolon); + WriteToken(Roles.Semicolon); + Space(policy.SpaceAfterForSemicolon); + + forStatement.Condition.AcceptVisitor(this); + Space(policy.SpaceBeforeForSemicolon); + WriteToken(Roles.Semicolon); + if (forStatement.Iterators.Any()) { + Space(policy.SpaceAfterForSemicolon); + WriteCommaSeparatedList(forStatement.Iterators); + } + + Space(policy.SpacesWithinForParentheses); + RPar(); + WriteEmbeddedStatement(forStatement.EmbeddedStatement); + EndNode(forStatement); + } + + public void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) + { + StartNode(gotoCaseStatement); + WriteKeyword(GotoCaseStatement.GotoKeywordRole); + WriteKeyword(GotoCaseStatement.CaseKeywordRole); + Space(); + gotoCaseStatement.LabelExpression.AcceptVisitor(this); + Semicolon(); + EndNode(gotoCaseStatement); + } + + public void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) + { + StartNode(gotoDefaultStatement); + WriteKeyword(GotoDefaultStatement.GotoKeywordRole); + WriteKeyword(GotoDefaultStatement.DefaultKeywordRole); + Semicolon(); + EndNode(gotoDefaultStatement); + } + + public void VisitGotoStatement(GotoStatement gotoStatement) + { + StartNode(gotoStatement); + WriteKeyword(GotoStatement.GotoKeywordRole); + WriteIdentifier(gotoStatement.GetChildByRole(Roles.Identifier)); + Semicolon(); + EndNode(gotoStatement); + } + + public void VisitIfElseStatement(IfElseStatement ifElseStatement) + { + StartNode(ifElseStatement); + WriteKeyword(IfElseStatement.IfKeywordRole); + Space(policy.SpaceBeforeIfParentheses); + LPar(); + Space(policy.SpacesWithinIfParentheses); + ifElseStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinIfParentheses); + RPar(); + WriteEmbeddedStatement(ifElseStatement.TrueStatement); + if (!ifElseStatement.FalseStatement.IsNull) { + WriteKeyword(IfElseStatement.ElseKeywordRole); + if (ifElseStatement.FalseStatement is IfElseStatement) { + // don't put newline between 'else' and 'if' + ifElseStatement.FalseStatement.AcceptVisitor(this); + } else { + WriteEmbeddedStatement(ifElseStatement.FalseStatement); + } + } + EndNode(ifElseStatement); + } + + public void VisitLabelStatement(LabelStatement labelStatement) + { + StartNode(labelStatement); + WriteIdentifier(labelStatement.GetChildByRole(Roles.Identifier)); + WriteToken(Roles.Colon); + bool foundLabelledStatement = false; + for (AstNode tmp = labelStatement.NextSibling; tmp != null; tmp = tmp.NextSibling) { + if (tmp.Role == labelStatement.Role) { + foundLabelledStatement = true; + } + } + if (!foundLabelledStatement) { + // introduce an EmptyStatement so that the output becomes syntactically valid + WriteToken(Roles.Semicolon); + } + NewLine(); + EndNode(labelStatement); + } + + public void VisitLockStatement(LockStatement lockStatement) + { + StartNode(lockStatement); + WriteKeyword(LockStatement.LockKeywordRole); + Space(policy.SpaceBeforeLockParentheses); + LPar(); + Space(policy.SpacesWithinLockParentheses); + lockStatement.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinLockParentheses); + RPar(); + WriteEmbeddedStatement(lockStatement.EmbeddedStatement); + EndNode(lockStatement); + } + + public void VisitReturnStatement(ReturnStatement returnStatement) + { + StartNode(returnStatement); + WriteKeyword(ReturnStatement.ReturnKeywordRole); + if (!returnStatement.Expression.IsNull) { + Space(); + returnStatement.Expression.AcceptVisitor(this); + } + Semicolon(); + EndNode(returnStatement); + } + + public void VisitSwitchStatement(SwitchStatement switchStatement) + { + StartNode(switchStatement); + WriteKeyword(SwitchStatement.SwitchKeywordRole); + Space(policy.SpaceBeforeSwitchParentheses); + LPar(); + Space(policy.SpacesWithinSwitchParentheses); + switchStatement.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinSwitchParentheses); + RPar(); + OpenBrace(policy.StatementBraceStyle); + if (!policy.IndentSwitchBody) { + writer.Unindent(); + } + + foreach (var section in switchStatement.SwitchSections) { + section.AcceptVisitor(this); + } + + if (!policy.IndentSwitchBody) { + writer.Indent(); + } + CloseBrace(policy.StatementBraceStyle); + NewLine(); + EndNode(switchStatement); + } + + public void VisitSwitchSection(SwitchSection switchSection) + { + StartNode(switchSection); + bool first = true; + foreach (var label in switchSection.CaseLabels) { + if (!first) { + NewLine(); + } + label.AcceptVisitor(this); + first = false; + } + bool isBlock = switchSection.Statements.Count == 1 && switchSection.Statements.Single() is BlockStatement; + if (policy.IndentCaseBody && !isBlock) { + writer.Indent(); + } + + if (!isBlock) + NewLine(); + + foreach (var statement in switchSection.Statements) { + statement.AcceptVisitor(this); + } + + if (policy.IndentCaseBody && !isBlock) { + writer.Unindent(); + } + + EndNode(switchSection); + } + + public void VisitCaseLabel(CaseLabel caseLabel) + { + StartNode(caseLabel); + if (caseLabel.Expression.IsNull) { + WriteKeyword(CaseLabel.DefaultKeywordRole); + } else { + WriteKeyword(CaseLabel.CaseKeywordRole); + Space(); + caseLabel.Expression.AcceptVisitor(this); + } + WriteToken(Roles.Colon); + EndNode(caseLabel); + } + + public void VisitThrowStatement(ThrowStatement throwStatement) + { + StartNode(throwStatement); + WriteKeyword(ThrowStatement.ThrowKeywordRole); + if (!throwStatement.Expression.IsNull) { + Space(); + throwStatement.Expression.AcceptVisitor(this); + } + Semicolon(); + EndNode(throwStatement); + } + + public void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + StartNode(tryCatchStatement); + WriteKeyword(TryCatchStatement.TryKeywordRole); + tryCatchStatement.TryBlock.AcceptVisitor(this); + foreach (var catchClause in tryCatchStatement.CatchClauses) { + catchClause.AcceptVisitor(this); + } + if (!tryCatchStatement.FinallyBlock.IsNull) { + WriteKeyword(TryCatchStatement.FinallyKeywordRole); + tryCatchStatement.FinallyBlock.AcceptVisitor(this); + } + EndNode(tryCatchStatement); + } + + public void VisitCatchClause(CatchClause catchClause) + { + StartNode(catchClause); + WriteKeyword(CatchClause.CatchKeywordRole); + if (!catchClause.Type.IsNull) { + Space(policy.SpaceBeforeCatchParentheses); + LPar(); + Space(policy.SpacesWithinCatchParentheses); + catchClause.Type.AcceptVisitor(this); + if (!string.IsNullOrEmpty(catchClause.VariableName)) { + Space(); + WriteIdentifier(catchClause.VariableNameToken); + } + Space(policy.SpacesWithinCatchParentheses); + RPar(); + } + catchClause.Body.AcceptVisitor(this); + EndNode(catchClause); + } + + public void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + StartNode(uncheckedStatement); + WriteKeyword(UncheckedStatement.UncheckedKeywordRole); + uncheckedStatement.Body.AcceptVisitor(this); + EndNode(uncheckedStatement); + } + + public void VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + StartNode(unsafeStatement); + WriteKeyword(UnsafeStatement.UnsafeKeywordRole); + unsafeStatement.Body.AcceptVisitor(this); + EndNode(unsafeStatement); + } + + public void VisitUsingStatement(UsingStatement usingStatement) + { + StartNode(usingStatement); + WriteKeyword(UsingStatement.UsingKeywordRole); + Space(policy.SpaceBeforeUsingParentheses); + LPar(); + Space(policy.SpacesWithinUsingParentheses); + + usingStatement.ResourceAcquisition.AcceptVisitor(this); + + Space(policy.SpacesWithinUsingParentheses); + RPar(); + + WriteEmbeddedStatement(usingStatement.EmbeddedStatement); + + EndNode(usingStatement); + } + + public void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + StartNode(variableDeclarationStatement); + WriteModifiers(variableDeclarationStatement.GetChildrenByRole(VariableDeclarationStatement.ModifierRole)); + variableDeclarationStatement.Type.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(variableDeclarationStatement.Variables); + Semicolon(); + EndNode(variableDeclarationStatement); + } + + public void VisitWhileStatement(WhileStatement whileStatement) + { + StartNode(whileStatement); + WriteKeyword(WhileStatement.WhileKeywordRole); + Space(policy.SpaceBeforeWhileParentheses); + LPar(); + Space(policy.SpacesWithinWhileParentheses); + whileStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinWhileParentheses); + RPar(); + WriteEmbeddedStatement(whileStatement.EmbeddedStatement); + EndNode(whileStatement); + } + + public void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + StartNode(yieldBreakStatement); + WriteKeyword(YieldBreakStatement.YieldKeywordRole); + WriteKeyword(YieldBreakStatement.BreakKeywordRole); + Semicolon(); + EndNode(yieldBreakStatement); + } + + public void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement) + { + StartNode(yieldReturnStatement); + WriteKeyword(YieldReturnStatement.YieldKeywordRole); + WriteKeyword(YieldReturnStatement.ReturnKeywordRole); + Space(); + yieldReturnStatement.Expression.AcceptVisitor(this); + Semicolon(); + EndNode(yieldReturnStatement); + } + + #endregion + + #region TypeMembers + public void VisitAccessor(Accessor accessor) + { + StartNode(accessor); + WriteAttributes(accessor.Attributes); + WriteModifiers(accessor.ModifierTokens); + if (accessor.Role == PropertyDeclaration.GetterRole) { + WriteKeyword("get", PropertyDeclaration.GetKeywordRole); + } else if (accessor.Role == PropertyDeclaration.SetterRole) { + WriteKeyword("set", PropertyDeclaration.SetKeywordRole); + } else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) { + WriteKeyword("add", CustomEventDeclaration.AddKeywordRole); + } else if (accessor.Role == CustomEventDeclaration.RemoveAccessorRole) { + WriteKeyword("remove", CustomEventDeclaration.RemoveKeywordRole); + } + WriteMethodBody(accessor.Body); + EndNode(accessor); + } + + public void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + StartNode(constructorDeclaration); + WriteAttributes(constructorDeclaration.Attributes); + WriteModifiers(constructorDeclaration.ModifierTokens); + TypeDeclaration type = constructorDeclaration.Parent as TypeDeclaration; + if (type != null && type.Name != constructorDeclaration.Name) + WriteIdentifier((Identifier)type.NameToken.Clone()); + else + WriteIdentifier(constructorDeclaration.NameToken); + Space(policy.SpaceBeforeConstructorDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(constructorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + if (!constructorDeclaration.Initializer.IsNull) { + Space(); + constructorDeclaration.Initializer.AcceptVisitor(this); + } + WriteMethodBody(constructorDeclaration.Body); + EndNode(constructorDeclaration); + } + + public void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + StartNode(constructorInitializer); + WriteToken(Roles.Colon); + Space(); + if (constructorInitializer.ConstructorInitializerType == ConstructorInitializerType.This) { + WriteKeyword(ConstructorInitializer.ThisKeywordRole); + } else { + WriteKeyword(ConstructorInitializer.BaseKeywordRole); + } + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(constructorInitializer.Arguments, policy.SpaceWithinMethodCallParentheses); + EndNode(constructorInitializer); + } + + public void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + StartNode(destructorDeclaration); + WriteAttributes(destructorDeclaration.Attributes); + WriteModifiers(destructorDeclaration.ModifierTokens); + WriteToken(DestructorDeclaration.TildeRole); + TypeDeclaration type = destructorDeclaration.Parent as TypeDeclaration; + if (type != null && type.Name != destructorDeclaration.Name) + WriteIdentifier((Identifier)type.NameToken.Clone()); + else + WriteIdentifier(destructorDeclaration.NameToken); + Space(policy.SpaceBeforeConstructorDeclarationParentheses); + LPar(); + RPar(); + WriteMethodBody(destructorDeclaration.Body); + EndNode(destructorDeclaration); + } + + public void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + StartNode(enumMemberDeclaration); + WriteAttributes(enumMemberDeclaration.Attributes); + WriteModifiers(enumMemberDeclaration.ModifierTokens); + WriteIdentifier(enumMemberDeclaration.NameToken); + if (!enumMemberDeclaration.Initializer.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + enumMemberDeclaration.Initializer.AcceptVisitor(this); + } + EndNode(enumMemberDeclaration); + } + + public void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + StartNode(eventDeclaration); + WriteAttributes(eventDeclaration.Attributes); + WriteModifiers(eventDeclaration.ModifierTokens); + WriteKeyword(EventDeclaration.EventKeywordRole); + eventDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(eventDeclaration.Variables); + Semicolon(); + EndNode(eventDeclaration); + } + + public void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration) + { + StartNode(customEventDeclaration); + WriteAttributes(customEventDeclaration.Attributes); + WriteModifiers(customEventDeclaration.ModifierTokens); + WriteKeyword(CustomEventDeclaration.EventKeywordRole); + customEventDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType); + WriteIdentifier(customEventDeclaration.NameToken); + OpenBrace(policy.EventBraceStyle); + // output add/remove in their original order + foreach (AstNode node in customEventDeclaration.Children) { + if (node.Role == CustomEventDeclaration.AddAccessorRole || node.Role == CustomEventDeclaration.RemoveAccessorRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.EventBraceStyle); + NewLine(); + EndNode(customEventDeclaration); + } + + public void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + StartNode(fieldDeclaration); + WriteAttributes(fieldDeclaration.Attributes); + WriteModifiers(fieldDeclaration.ModifierTokens); + fieldDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fieldDeclaration.Variables); + Semicolon(); + EndNode(fieldDeclaration); + } + + public void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + StartNode(fixedFieldDeclaration); + WriteAttributes(fixedFieldDeclaration.Attributes); + WriteModifiers(fixedFieldDeclaration.ModifierTokens); + WriteKeyword(FixedFieldDeclaration.FixedKeywordRole); + Space(); + fixedFieldDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fixedFieldDeclaration.Variables); + Semicolon(); + EndNode(fixedFieldDeclaration); + } + + public void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) + { + StartNode(fixedVariableInitializer); + WriteIdentifier(fixedVariableInitializer.NameToken); + if (!fixedVariableInitializer.CountExpression.IsNull) { + WriteToken(Roles.LBracket); + Space(policy.SpacesWithinBrackets); + fixedVariableInitializer.CountExpression.AcceptVisitor(this); + Space(policy.SpacesWithinBrackets); + WriteToken(Roles.RBracket); + } + EndNode(fixedVariableInitializer); + } + + public void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + StartNode(indexerDeclaration); + WriteAttributes(indexerDeclaration.Attributes); + WriteModifiers(indexerDeclaration.ModifierTokens); + indexerDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); + WriteKeyword(IndexerDeclaration.ThisKeywordRole); + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInBrackets(indexerDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + OpenBrace(policy.PropertyBraceStyle); + // output get/set in their original order + foreach (AstNode node in indexerDeclaration.Children) { + if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.PropertyBraceStyle); + NewLine(); + EndNode(indexerDeclaration); + } + + public void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + StartNode(methodDeclaration); + WriteAttributes(methodDeclaration.Attributes); + WriteModifiers(methodDeclaration.ModifierTokens); + methodDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(methodDeclaration.PrivateImplementationType); + WriteIdentifier(methodDeclaration.NameToken); + WriteTypeParameters(methodDeclaration.TypeParameters); + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(methodDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + foreach (Constraint constraint in methodDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + WriteMethodBody(methodDeclaration.Body); + EndNode(methodDeclaration); + } + + public void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + StartNode(operatorDeclaration); + WriteAttributes(operatorDeclaration.Attributes); + WriteModifiers(operatorDeclaration.ModifierTokens); + if (operatorDeclaration.OperatorType == OperatorType.Explicit) { + WriteKeyword(OperatorDeclaration.ExplicitRole); + } else if (operatorDeclaration.OperatorType == OperatorType.Implicit) { + WriteKeyword(OperatorDeclaration.ImplicitRole); + } else { + operatorDeclaration.ReturnType.AcceptVisitor(this); + } + WriteKeyword(OperatorDeclaration.OperatorKeywordRole); + Space(); + if (operatorDeclaration.OperatorType == OperatorType.Explicit + || operatorDeclaration.OperatorType == OperatorType.Implicit) { + operatorDeclaration.ReturnType.AcceptVisitor(this); + } else { + WriteToken(OperatorDeclaration.GetToken(operatorDeclaration.OperatorType), OperatorDeclaration.GetRole(operatorDeclaration.OperatorType)); + } + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(operatorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + WriteMethodBody(operatorDeclaration.Body); + EndNode(operatorDeclaration); + } + + public void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + StartNode(parameterDeclaration); + WriteAttributes(parameterDeclaration.Attributes); + switch (parameterDeclaration.ParameterModifier) { + case ParameterModifier.Ref: + WriteKeyword(ParameterDeclaration.RefModifierRole); + break; + case ParameterModifier.Out: + WriteKeyword(ParameterDeclaration.OutModifierRole); + break; + case ParameterModifier.Params: + WriteKeyword(ParameterDeclaration.ParamsModifierRole); + break; + case ParameterModifier.This: + WriteKeyword(ParameterDeclaration.ThisModifierRole); + break; + } + parameterDeclaration.Type.AcceptVisitor(this); + if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) { + Space(); + } + if (!string.IsNullOrEmpty(parameterDeclaration.Name)) { + WriteIdentifier(parameterDeclaration.NameToken); + } + if (!parameterDeclaration.DefaultExpression.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + parameterDeclaration.DefaultExpression.AcceptVisitor(this); + } + EndNode(parameterDeclaration); + } + + public void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + StartNode(propertyDeclaration); + WriteAttributes(propertyDeclaration.Attributes); + WriteModifiers(propertyDeclaration.ModifierTokens); + propertyDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); + WriteIdentifier(propertyDeclaration.NameToken); + OpenBrace(policy.PropertyBraceStyle); + // output get/set in their original order + foreach (AstNode node in propertyDeclaration.Children) { + if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.PropertyBraceStyle); + NewLine(); + EndNode(propertyDeclaration); + } + + #endregion + + #region Other nodes + public void VisitVariableInitializer(VariableInitializer variableInitializer) + { + StartNode(variableInitializer); + WriteIdentifier(variableInitializer.NameToken); + if (!variableInitializer.Initializer.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + variableInitializer.Initializer.AcceptVisitor(this); + } + EndNode(variableInitializer); + } + + void MaybeNewLinesAfterUsings(AstNode node) + { + var nextSibling = node.NextSibling; + while (nextSibling is WhitespaceNode || nextSibling is NewLineNode) + nextSibling = nextSibling.NextSibling; + + if ((node is UsingDeclaration || node is UsingAliasDeclaration) && !(nextSibling is UsingDeclaration || nextSibling is UsingAliasDeclaration)) { + for (int i = 0; i < policy.MinimumBlankLinesAfterUsings; i++) + NewLine(); + } + } + + public void VisitSyntaxTree(SyntaxTree syntaxTree) + { + // don't do node tracking as we visit all children directly + foreach (AstNode node in syntaxTree.Children) { + node.AcceptVisitor(this); + MaybeNewLinesAfterUsings(node); + } + } + + public void VisitSimpleType(SimpleType simpleType) + { + StartNode(simpleType); + WriteIdentifier(simpleType.IdentifierToken); + WriteTypeArguments(simpleType.TypeArguments); + EndNode(simpleType); + } + + public void VisitMemberType(MemberType memberType) + { + StartNode(memberType); + memberType.Target.AcceptVisitor(this); + if (memberType.IsDoubleColon) { + WriteToken(Roles.DoubleColon); + } else { + WriteToken(Roles.Dot); + } + WriteIdentifier(memberType.MemberNameToken); + WriteTypeArguments(memberType.TypeArguments); + EndNode(memberType); + } + + public void VisitComposedType(ComposedType composedType) + { + StartNode(composedType); + composedType.BaseType.AcceptVisitor(this); + if (composedType.HasNullableSpecifier) { + WriteToken(ComposedType.NullableRole); + } + for (int i = 0; i < composedType.PointerRank; i++) { + WriteToken(ComposedType.PointerRole); + } + foreach (var node in composedType.ArraySpecifiers) { + node.AcceptVisitor(this); + } + EndNode(composedType); + } + + public void VisitArraySpecifier(ArraySpecifier arraySpecifier) + { + StartNode(arraySpecifier); + WriteToken(Roles.LBracket); + foreach (var comma in arraySpecifier.GetChildrenByRole(Roles.Comma)) { + writer.WriteToken(Roles.Comma, ","); + } + WriteToken(Roles.RBracket); + EndNode(arraySpecifier); + } + + public void VisitPrimitiveType(PrimitiveType primitiveType) + { + StartNode(primitiveType); + writer.WritePrimitiveType(primitiveType.Keyword); + EndNode(primitiveType); + } + + public void VisitComment(Comment comment) + { + writer.StartNode(comment); + writer.WriteComment(comment.CommentType, comment.Content); + writer.EndNode(comment); + } + + public void VisitNewLine(NewLineNode newLineNode) + { +// formatter.StartNode(newLineNode); +// formatter.NewLine(); +// formatter.EndNode(newLineNode); + } + + public void VisitWhitespace(WhitespaceNode whitespaceNode) + { + // unused + } + + public void VisitText(TextNode textNode) + { + // unused + } + + public void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + writer.StartNode(preProcessorDirective); + writer.WritePreProcessorDirective(preProcessorDirective.Type, preProcessorDirective.Argument); + writer.EndNode(preProcessorDirective); + } + + public void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + StartNode(typeParameterDeclaration); + WriteAttributes(typeParameterDeclaration.Attributes); + switch (typeParameterDeclaration.Variance) { + case VarianceModifier.Invariant: + break; + case VarianceModifier.Covariant: + WriteKeyword(TypeParameterDeclaration.OutVarianceKeywordRole); + break; + case VarianceModifier.Contravariant: + WriteKeyword(TypeParameterDeclaration.InVarianceKeywordRole); + break; + default: + throw new NotSupportedException ("Invalid value for VarianceModifier"); + } + WriteIdentifier(typeParameterDeclaration.NameToken); + EndNode(typeParameterDeclaration); + } + + public void VisitConstraint(Constraint constraint) + { + StartNode(constraint); + Space(); + WriteKeyword(Roles.WhereKeyword); + constraint.TypeParameter.AcceptVisitor(this); + Space(); + WriteToken(Roles.Colon); + Space(); + WriteCommaSeparatedList(constraint.BaseTypes); + EndNode(constraint); + } + + public void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) + { + CSharpModifierToken mod = cSharpTokenNode as CSharpModifierToken; + if (mod != null) { + // ITokenWriter assumes that each node processed between a + // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. + WriteKeyword(CSharpModifierToken.GetModifierName(mod.Modifier), cSharpTokenNode.Role); + } else { + throw new NotSupportedException ("Should never visit individual tokens"); + } + } + + public void VisitIdentifier(Identifier identifier) + { + // Do not call StartNode and EndNode for Identifier, because they are handled by the ITokenWriter. + // ITokenWriter assumes that each node processed between a + // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. + WriteIdentifier(identifier); + } + + void IAstVisitor.VisitNullNode(AstNode nullNode) + { + } + + void IAstVisitor.VisitErrorNode(AstNode errorNode) + { + StartNode(errorNode); + EndNode(errorNode); + } + #endregion + + #region Pattern Nodes + public void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + StartNode(placeholder); + VisitNodeInPattern(pattern); + EndNode(placeholder); + } + + void VisitAnyNode(AnyNode anyNode) + { + if (!string.IsNullOrEmpty(anyNode.GroupName)) { + WriteIdentifier(anyNode.GroupName); + WriteToken(Roles.Colon); + } + } + + void VisitBackreference(Backreference backreference) + { + WriteKeyword("backreference"); + LPar(); + WriteIdentifier(backreference.ReferencedGroupName); + RPar(); + } + + void VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference) + { + WriteKeyword("identifierBackreference"); + LPar(); + WriteIdentifier(identifierExpressionBackreference.ReferencedGroupName); + RPar(); + } + + void VisitChoice(Choice choice) + { + WriteKeyword("choice"); + Space(); + LPar(); + NewLine(); + writer.Indent(); + foreach (INode alternative in choice) { + VisitNodeInPattern(alternative); + if (alternative != choice.Last()) { + WriteToken(Roles.Comma); + } + NewLine(); + } + writer.Unindent(); + RPar(); + } + + void VisitNamedNode(NamedNode namedNode) + { + if (!string.IsNullOrEmpty(namedNode.GroupName)) { + WriteIdentifier(namedNode.GroupName); + WriteToken(Roles.Colon); + } + VisitNodeInPattern(namedNode.ChildNode); + } + + void VisitRepeat(Repeat repeat) + { + WriteKeyword("repeat"); + LPar(); + if (repeat.MinCount != 0 || repeat.MaxCount != int.MaxValue) { + WriteIdentifier(repeat.MinCount.ToString()); + WriteToken(Roles.Comma); + WriteIdentifier(repeat.MaxCount.ToString()); + WriteToken(Roles.Comma); + } + VisitNodeInPattern(repeat.ChildNode); + RPar(); + } + + void VisitOptionalNode(OptionalNode optionalNode) + { + WriteKeyword("optional"); + LPar(); + VisitNodeInPattern(optionalNode.ChildNode); + RPar(); + } + + void VisitNodeInPattern(INode childNode) + { + if (childNode is AstNode) { + ((AstNode)childNode).AcceptVisitor(this); + } else if (childNode is IdentifierExpressionBackreference) { + VisitIdentifierExpressionBackreference((IdentifierExpressionBackreference)childNode); + } else if (childNode is Choice) { + VisitChoice((Choice)childNode); + } else if (childNode is AnyNode) { + VisitAnyNode((AnyNode)childNode); + } else if (childNode is Backreference) { + VisitBackreference((Backreference)childNode); + } else if (childNode is NamedNode) { + VisitNamedNode((NamedNode)childNode); + } else if (childNode is OptionalNode) { + VisitOptionalNode((OptionalNode)childNode); + } else if (childNode is Repeat) { + VisitRepeat((Repeat)childNode); + } else { + TextWriterTokenWriter.PrintPrimitiveValue(childNode); + } + } + #endregion + + #region Documentation Reference + public void VisitDocumentationReference(DocumentationReference documentationReference) + { + StartNode(documentationReference); + if (!documentationReference.DeclaringType.IsNull) { + documentationReference.DeclaringType.AcceptVisitor(this); + if (documentationReference.SymbolKind != SymbolKind.TypeDefinition) { + WriteToken(Roles.Dot); + } + } + switch (documentationReference.SymbolKind) { + case SymbolKind.TypeDefinition: + // we already printed the DeclaringType + break; + case SymbolKind.Indexer: + WriteKeyword(IndexerDeclaration.ThisKeywordRole); + break; + case SymbolKind.Operator: + var opType = documentationReference.OperatorType; + if (opType == OperatorType.Explicit) { + WriteKeyword(OperatorDeclaration.ExplicitRole); + } else if (opType == OperatorType.Implicit) { + WriteKeyword(OperatorDeclaration.ImplicitRole); + } + WriteKeyword(OperatorDeclaration.OperatorKeywordRole); + Space(); + if (opType == OperatorType.Explicit || opType == OperatorType.Implicit) { + documentationReference.ConversionOperatorReturnType.AcceptVisitor(this); + } else { + WriteToken(OperatorDeclaration.GetToken(opType), OperatorDeclaration.GetRole(opType)); + } + break; + default: + WriteIdentifier(documentationReference.GetChildByRole(Roles.Identifier)); + break; + } + WriteTypeArguments(documentationReference.TypeArguments); + if (documentationReference.HasParameterList) { + Space(policy.SpaceBeforeMethodDeclarationParentheses); + if (documentationReference.SymbolKind == SymbolKind.Indexer) { + WriteCommaSeparatedListInBrackets(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } else { + WriteCommaSeparatedListInParenthesis(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } + } + EndNode(documentationReference); + } + #endregion + + /// + /// Converts special characters to escape sequences within the given string. + /// + public static string ConvertString(string text) + { + return TextWriterTokenWriter.ConvertString(text); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs new file mode 100644 index 000000000..bd82827d8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs @@ -0,0 +1,1429 @@ +// Copyright (c) 2010-2013 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.CodeDom; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Converts from C# AST to CodeDom. + /// + /// + /// The conversion is intended for use in the SharpDevelop forms designer. + /// + public class CodeDomConvertVisitor : IAstVisitor + { + CSharpAstResolver resolver; + + /// + /// Gets/Sets whether the visitor should convert short type names into + /// fully qualified type names. + /// The default is false. + /// + public bool UseFullyQualifiedTypeNames { get; set; } + + /// + /// Gets whether the visitor is allowed to produce snippet nodes for + /// code that cannot be converted. + /// The default is true. If this property is set to false, + /// unconvertible code will throw a NotSupportedException. + /// + public bool AllowSnippetNodes { get; set; } + + public CodeDomConvertVisitor() + { + this.AllowSnippetNodes = true; + } + + /// + /// Converts a syntax tree to CodeDom. + /// + /// The input syntax tree. + /// The current compilation. + /// CSharpUnresolvedFile, used for resolving. + /// Converted CodeCompileUnit + /// + /// This conversion process requires a resolver because it needs to distinguish field/property/event references etc. + /// + public CodeCompileUnit Convert(ICompilation compilation, SyntaxTree syntaxTree, CSharpUnresolvedFile unresolvedFile) + { + if (syntaxTree == null) + throw new ArgumentNullException("syntaxTree"); + if (compilation == null) + throw new ArgumentNullException("compilation"); + + CSharpAstResolver resolver = new CSharpAstResolver(compilation, syntaxTree, unresolvedFile); + return (CodeCompileUnit)Convert(syntaxTree, resolver); + } + + /// + /// Converts a C# AST node to CodeDom. + /// + /// The input node. + /// The AST resolver. + /// The node converted into CodeDom + /// + /// This conversion process requires a resolver because it needs to distinguish field/property/event references etc. + /// + public CodeObject Convert(AstNode node, CSharpAstResolver resolver) + { + if (node == null) + throw new ArgumentNullException("node"); + if (resolver == null) + throw new ArgumentNullException("resolver"); + try { + this.resolver = resolver; + return node.AcceptVisitor(this); + } finally { + this.resolver = null; + } + } + + ResolveResult Resolve(AstNode node) + { + if (resolver == null) + return ErrorResolveResult.UnknownError; + else + return resolver.Resolve(node); + } + + CodeExpression Convert(Expression expr) + { + return (CodeExpression)expr.AcceptVisitor(this); + } + + CodeExpression[] Convert(IEnumerable expressions) + { + List result = new List(); + foreach (Expression expr in expressions) { + CodeExpression e = Convert(expr); + if (e != null) + result.Add(e); + } + return result.ToArray(); + } + + CodeTypeReference Convert(AstType type) + { + return (CodeTypeReference)type.AcceptVisitor(this); + } + + CodeTypeReference[] Convert(IEnumerable types) + { + List result = new List(); + foreach (AstType type in types) { + CodeTypeReference e = Convert(type); + if (e != null) + result.Add(e); + } + return result.ToArray(); + } + + public CodeTypeReference Convert(IType type) + { + if (type.Kind == TypeKind.Array) { + ArrayType a = (ArrayType)type; + return new CodeTypeReference(Convert(a.ElementType), a.Dimensions); + } else if (type is ParameterizedType) { + var pt = (ParameterizedType)type; + return new CodeTypeReference(pt.GetDefinition().ReflectionName, pt.TypeArguments.Select(Convert).ToArray()); + } else { + return new CodeTypeReference(type.ReflectionName); + } + } + + CodeStatement Convert(Statement stmt) + { + return (CodeStatement)stmt.AcceptVisitor(this); + } + + CodeStatement[] ConvertBlock(BlockStatement block) + { + List result = new List(); + foreach (Statement stmt in block.Statements) { + if (stmt is EmptyStatement) + continue; + CodeStatement s = Convert(stmt); + if (s != null) + result.Add(s); + } + return result.ToArray(); + } + + CodeStatement[] ConvertEmbeddedStatement(Statement embeddedStatement) + { + BlockStatement block = embeddedStatement as BlockStatement; + if (block != null) { + return ConvertBlock(block); + } else if (embeddedStatement is EmptyStatement) { + return new CodeStatement[0]; + } + CodeStatement s = Convert(embeddedStatement); + if (s != null) + return new CodeStatement[] { s }; + else + return new CodeStatement[0]; + } + + string MakeSnippet(AstNode node) + { + if (!AllowSnippetNodes) + throw new NotSupportedException(); + StringWriter w = new StringWriter(); + CSharpOutputVisitor v = new CSharpOutputVisitor(w, FormattingOptionsFactory.CreateMono ()); + node.AcceptVisitor(v); + return w.ToString(); + } + + /// + /// Converts an expression by storing it as C# snippet. + /// This is used for expressions that cannot be represented in CodeDom. + /// + CodeSnippetExpression MakeSnippetExpression(Expression expr) + { + return new CodeSnippetExpression(MakeSnippet(expr)); + } + + CodeSnippetStatement MakeSnippetStatement(Statement stmt) + { + return new CodeSnippetStatement(MakeSnippet(stmt)); + } + + CodeObject IAstVisitor.VisitNullNode(AstNode nullNode) + { + return null; + } + + CodeObject IAstVisitor.VisitErrorNode(AstNode errorNode) + { + return null; + } + + CodeObject IAstVisitor.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + return MakeSnippetExpression(anonymousMethodExpression); + } + + CodeObject IAstVisitor.VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) + { + return MakeSnippetExpression(undocumentedExpression); + } + + CodeObject IAstVisitor.VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) + { + CodeArrayCreateExpression ace = new CodeArrayCreateExpression(); + int dimensions = arrayCreateExpression.Arguments.Count; + int nestingDepth = arrayCreateExpression.AdditionalArraySpecifiers.Count; + if (dimensions > 0) + nestingDepth++; + if (nestingDepth > 1 || dimensions > 1) { + // CodeDom does not support jagged or multi-dimensional arrays + return MakeSnippetExpression(arrayCreateExpression); + } + if (arrayCreateExpression.Type.IsNull) { + ace.CreateType = Convert(Resolve(arrayCreateExpression).Type); + } else { + ace.CreateType = Convert(arrayCreateExpression.Type); + } + if (arrayCreateExpression.Arguments.Count == 1) { + ace.SizeExpression = Convert(arrayCreateExpression.Arguments.Single()); + } + ace.Initializers.AddRange(Convert(arrayCreateExpression.Initializer.Elements)); + return ace; + } + + CodeObject IAstVisitor.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + // Array initializers should be handled by the parent node + return MakeSnippetExpression(arrayInitializerExpression); + } + + CodeObject IAstVisitor.VisitAsExpression(AsExpression asExpression) + { + return MakeSnippetExpression(asExpression); + } + + CodeObject IAstVisitor.VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + // assignments are only supported as statements, not as expressions + return MakeSnippetExpression(assignmentExpression); + } + + CodeObject IAstVisitor.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) + { + return new CodeBaseReferenceExpression(); + } + + CodeObject IAstVisitor.VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + CodeBinaryOperatorType op; + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.BitwiseAnd: + op = CodeBinaryOperatorType.BitwiseAnd; + break; + case BinaryOperatorType.BitwiseOr: + op = CodeBinaryOperatorType.BitwiseOr; + break; + case BinaryOperatorType.ConditionalAnd: + op = CodeBinaryOperatorType.BooleanAnd; + break; + case BinaryOperatorType.ConditionalOr: + op = CodeBinaryOperatorType.BooleanOr; + break; + case BinaryOperatorType.GreaterThan: + op = CodeBinaryOperatorType.GreaterThan; + break; + case BinaryOperatorType.GreaterThanOrEqual: + op = CodeBinaryOperatorType.GreaterThanOrEqual; + break; + case BinaryOperatorType.LessThan: + op = CodeBinaryOperatorType.LessThan; + break; + case BinaryOperatorType.LessThanOrEqual: + op = CodeBinaryOperatorType.LessThanOrEqual; + break; + case BinaryOperatorType.Add: + op = CodeBinaryOperatorType.Add; + break; + case BinaryOperatorType.Subtract: + op = CodeBinaryOperatorType.Subtract; + break; + case BinaryOperatorType.Multiply: + op = CodeBinaryOperatorType.Multiply; + break; + case BinaryOperatorType.Divide: + op = CodeBinaryOperatorType.Divide; + break; + case BinaryOperatorType.Modulus: + op = CodeBinaryOperatorType.Modulus; + break; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + OperatorResolveResult rr = Resolve(binaryOperatorExpression) as OperatorResolveResult; + if (rr != null && rr.GetChildResults().Any(cr => cr.Type.IsReferenceType == true)) { + if (binaryOperatorExpression.Operator == BinaryOperatorType.Equality) + op = CodeBinaryOperatorType.IdentityEquality; + else + op = CodeBinaryOperatorType.IdentityInequality; + } else { + if (binaryOperatorExpression.Operator == BinaryOperatorType.Equality) { + op = CodeBinaryOperatorType.ValueEquality; + } else { + // CodeDom is retarded and does not support ValueInequality, so we'll simulate it using + // ValueEquality and Not... but CodeDom doesn't have Not either, so we use + // '(a == b) == false' + return new CodeBinaryOperatorExpression( + new CodeBinaryOperatorExpression( + Convert(binaryOperatorExpression.Left), + CodeBinaryOperatorType.ValueEquality, + Convert(binaryOperatorExpression.Right) + ), + CodeBinaryOperatorType.ValueEquality, + new CodePrimitiveExpression(false) + ); + } + } + break; + default: + // not supported: xor, shift, null coalescing + return MakeSnippetExpression(binaryOperatorExpression); + } + return new CodeBinaryOperatorExpression(Convert(binaryOperatorExpression.Left), op, Convert(binaryOperatorExpression.Right)); + } + + CodeObject IAstVisitor.VisitCastExpression(CastExpression castExpression) + { + return new CodeCastExpression(Convert(castExpression.Type), Convert(castExpression.Expression)); + } + + CodeObject IAstVisitor.VisitCheckedExpression(CheckedExpression checkedExpression) + { + return MakeSnippetExpression(checkedExpression); + } + + CodeObject IAstVisitor.VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + return MakeSnippetExpression(conditionalExpression); + } + + CodeObject IAstVisitor.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) + { + return new CodeDefaultValueExpression(Convert(defaultValueExpression.Type)); + } + + CodeObject IAstVisitor.VisitDirectionExpression(DirectionExpression directionExpression) + { + System.CodeDom.FieldDirection direction; + if (directionExpression.FieldDirection == FieldDirection.Out) { + direction = System.CodeDom.FieldDirection.Out; + } else { + direction = System.CodeDom.FieldDirection.Ref; + } + return new CodeDirectionExpression(direction, Convert(directionExpression.Expression)); + } + + CodeObject IAstVisitor.VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + ResolveResult rr = Resolve(identifierExpression); + LocalResolveResult lrr = rr as LocalResolveResult; + if (lrr != null && lrr.IsParameter) { + if (lrr.Variable.Name == "value" && identifierExpression.Ancestors.Any(a => a is Accessor)) { + return new CodePropertySetValueReferenceExpression(); + } else { + return new CodeArgumentReferenceExpression(lrr.Variable.Name); + } + } + MemberResolveResult mrr = rr as MemberResolveResult; + if (mrr != null) { + return HandleMemberReference(null, identifierExpression.Identifier, identifierExpression.TypeArguments, mrr); + } + TypeResolveResult trr = rr as TypeResolveResult; + if (trr != null) { + CodeTypeReference typeRef; + if (UseFullyQualifiedTypeNames) { + typeRef = Convert(trr.Type); + } else { + typeRef = new CodeTypeReference(identifierExpression.Identifier); + typeRef.TypeArguments.AddRange(Convert(identifierExpression.TypeArguments)); + } + return new CodeTypeReferenceExpression(typeRef); + } + MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; + if (mgrr != null || identifierExpression.TypeArguments.Any()) { + return new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), identifierExpression.Identifier, Convert(identifierExpression.TypeArguments)); + } + return new CodeVariableReferenceExpression(identifierExpression.Identifier); + } + + CodeObject IAstVisitor.VisitIndexerExpression(IndexerExpression indexerExpression) + { + if (Resolve(indexerExpression) is ArrayAccessResolveResult) + return new CodeArrayIndexerExpression(Convert(indexerExpression.Target), Convert(indexerExpression.Arguments)); + else + return new CodeIndexerExpression(Convert(indexerExpression.Target), Convert(indexerExpression.Arguments)); + } + + CodeObject IAstVisitor.VisitInvocationExpression(InvocationExpression invocationExpression) + { + MemberResolveResult rr = Resolve(invocationExpression) as MemberResolveResult; + CSharpInvocationResolveResult csRR = rr as CSharpInvocationResolveResult; + if (csRR != null && csRR.IsDelegateInvocation) { + return new CodeDelegateInvokeExpression(Convert(invocationExpression.Target), Convert(invocationExpression.Arguments)); + } + + Expression methodExpr = invocationExpression.Target; + while (methodExpr is ParenthesizedExpression) + methodExpr = ((ParenthesizedExpression)methodExpr).Expression; + CodeMethodReferenceExpression mr = null; + MemberReferenceExpression mre = methodExpr as MemberReferenceExpression; + if (mre != null) { + mr = new CodeMethodReferenceExpression(Convert(mre.Target), mre.MemberName, Convert(mre.TypeArguments)); + } + IdentifierExpression id = methodExpr as IdentifierExpression; + if (id != null) { + CodeExpression target; + if (rr != null && rr.Member.IsStatic) + target = new CodeTypeReferenceExpression(Convert(rr.Member.DeclaringType)); + else + target = new CodeThisReferenceExpression(); + + mr = new CodeMethodReferenceExpression(target, id.Identifier, Convert(id.TypeArguments)); + } + if (mr != null) + return new CodeMethodInvokeExpression(mr, Convert(invocationExpression.Arguments)); + else + return MakeSnippetExpression(invocationExpression); + } + + CodeObject IAstVisitor.VisitIsExpression(IsExpression isExpression) + { + return MakeSnippetExpression(isExpression); + } + + CodeObject IAstVisitor.VisitLambdaExpression(LambdaExpression lambdaExpression) + { + return MakeSnippetExpression(lambdaExpression); + } + + CodeObject IAstVisitor.VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + CodeExpression target = Convert(memberReferenceExpression.Target); + ResolveResult rr = Resolve(memberReferenceExpression); + MemberResolveResult mrr = rr as MemberResolveResult; + TypeResolveResult trr = rr as TypeResolveResult; + if (mrr != null) { + return HandleMemberReference(target, memberReferenceExpression.MemberName, memberReferenceExpression.TypeArguments, mrr); + } else if (trr != null) { + return new CodeTypeReferenceExpression(Convert(trr.Type)); + } else { + if (memberReferenceExpression.TypeArguments.Any() || rr is MethodGroupResolveResult) { + return new CodeMethodReferenceExpression(target, memberReferenceExpression.MemberName, Convert(memberReferenceExpression.TypeArguments)); + } else { + return new CodePropertyReferenceExpression(target, memberReferenceExpression.MemberName); + } + } + } + + CodeExpression HandleMemberReference(CodeExpression target, string identifier, AstNodeCollection typeArguments, MemberResolveResult mrr) + { + if (target == null) { + if (mrr.Member.IsStatic) + target = new CodeTypeReferenceExpression(Convert(mrr.Member.DeclaringType)); + else + target = new CodeThisReferenceExpression(); + } + if (mrr.Member is IField) { + return new CodeFieldReferenceExpression(target, identifier); + } else if (mrr.Member is IMethod) { + return new CodeMethodReferenceExpression(target, identifier, Convert(typeArguments)); + } else if (mrr.Member is IEvent) { + return new CodeEventReferenceExpression(target, identifier); + } else { + return new CodePropertyReferenceExpression(target, identifier); + } + } + + CodeObject IAstVisitor.VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + return MakeSnippetExpression(namedArgumentExpression); + } + + CodeObject IAstVisitor.VisitNamedExpression(NamedExpression namedExpression) + { + return MakeSnippetExpression(namedExpression); + } + + CodeObject IAstVisitor.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) + { + return new CodePrimitiveExpression(null); + } + + CodeObject IAstVisitor.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + if (!objectCreateExpression.Initializer.IsNull) + return MakeSnippetExpression(objectCreateExpression); + return new CodeObjectCreateExpression(Convert(objectCreateExpression.Type), Convert(objectCreateExpression.Arguments)); + } + + CodeObject IAstVisitor.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + return MakeSnippetExpression(anonymousTypeCreateExpression); + } + + CodeObject IAstVisitor.VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + // CodeDom generators will insert parentheses where necessary + return Convert(parenthesizedExpression.Expression); + } + + CodeObject IAstVisitor.VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + return MakeSnippetExpression(pointerReferenceExpression); + } + + CodeObject IAstVisitor.VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + return new CodePrimitiveExpression(primitiveExpression.Value); + } + + CodeObject IAstVisitor.VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + return MakeSnippetExpression(sizeOfExpression); + } + + CodeObject IAstVisitor.VisitStackAllocExpression(StackAllocExpression stackAllocExpression) + { + return MakeSnippetExpression(stackAllocExpression); + } + + CodeObject IAstVisitor.VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + return new CodeThisReferenceExpression(); + } + + CodeObject IAstVisitor.VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + return new CodeTypeOfExpression(Convert(typeOfExpression.Type)); + } + + CodeObject IAstVisitor.VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + return new CodeTypeReferenceExpression(Convert(typeReferenceExpression.Type)); + } + + CodeObject IAstVisitor.VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + switch (unaryOperatorExpression.Operator) { + case UnaryOperatorType.Not: + return new CodeBinaryOperatorExpression( + Convert(unaryOperatorExpression.Expression), + CodeBinaryOperatorType.ValueEquality, + new CodePrimitiveExpression(false)); + case UnaryOperatorType.Minus: + return new CodeBinaryOperatorExpression( + new CodePrimitiveExpression(0), + CodeBinaryOperatorType.Subtract, + Convert(unaryOperatorExpression.Expression)); + case UnaryOperatorType.Plus: + return Convert(unaryOperatorExpression.Expression); + default: + return MakeSnippetExpression(unaryOperatorExpression); + } + } + + CodeObject IAstVisitor.VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + return MakeSnippetExpression(uncheckedExpression); + } + + CodeObject IAstVisitor.VisitQueryExpression(QueryExpression queryExpression) + { + return MakeSnippetExpression(queryExpression); + } + + CodeObject IAstVisitor.VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryFromClause(QueryFromClause queryFromClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryLetClause(QueryLetClause queryLetClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryOrdering(QueryOrdering queryOrdering) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitAttribute(Attribute attribute) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitAttributeSection(AttributeSection attributeSection) + { + throw new NotSupportedException(); + } + + CodeAttributeDeclaration Convert(Attribute attribute) + { + var attr = new CodeAttributeDeclaration(Convert(attribute.Type)); + foreach (Expression expr in attribute.Arguments) { + NamedExpression ne = expr as NamedExpression; + if (ne != null) + attr.Arguments.Add(new CodeAttributeArgument(ne.Name, Convert(ne.Expression))); + else + attr.Arguments.Add(new CodeAttributeArgument(Convert(expr))); + } + return attr; + } + + CodeAttributeDeclaration[] Convert(IEnumerable attributeSections) + { + List result = new List(); + foreach (AttributeSection section in attributeSections) { + foreach (Attribute attr in section.Attributes) { + CodeAttributeDeclaration attrDecl = Convert(attr); + if (attrDecl != null) + result.Add(attrDecl); + } + } + return result.ToArray(); + } + + CodeObject IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + CodeTypeDelegate d = new CodeTypeDelegate(delegateDeclaration.Name); + d.Attributes = ConvertMemberAttributes(delegateDeclaration.Modifiers, SymbolKind.TypeDefinition); + d.CustomAttributes.AddRange(Convert(delegateDeclaration.Attributes)); + d.ReturnType = Convert(delegateDeclaration.ReturnType); + d.Parameters.AddRange(Convert(delegateDeclaration.Parameters)); + d.TypeParameters.AddRange(ConvertTypeParameters(delegateDeclaration.TypeParameters, delegateDeclaration.Constraints)); + return d; + } + + MemberAttributes ConvertMemberAttributes(Modifiers modifiers, SymbolKind symbolKind) + { + MemberAttributes a = 0; + if ((modifiers & Modifiers.Abstract) != 0) + a |= MemberAttributes.Abstract; + if ((modifiers & Modifiers.Sealed) != 0) + a |= MemberAttributes.Final; + if (symbolKind != SymbolKind.TypeDefinition && (modifiers & (Modifiers.Abstract | Modifiers.Override | Modifiers.Virtual)) == 0) + a |= MemberAttributes.Final; + if ((modifiers & Modifiers.Static) != 0) + a |= MemberAttributes.Static; + if ((modifiers & Modifiers.Override) != 0) + a |= MemberAttributes.Override; + if ((modifiers & Modifiers.Const) != 0) + a |= MemberAttributes.Const; + if ((modifiers & Modifiers.New) != 0) + a |= MemberAttributes.New; + + if ((modifiers & Modifiers.Public) != 0) + a |= MemberAttributes.Public; + else if ((modifiers & (Modifiers.Protected | Modifiers.Internal)) == (Modifiers.Protected | Modifiers.Internal)) + a |= MemberAttributes.FamilyOrAssembly; + else if ((modifiers & Modifiers.Protected) != 0) + a |= MemberAttributes.Family; + else if ((modifiers & Modifiers.Internal) != 0) + a |= MemberAttributes.Assembly; + else if ((modifiers & Modifiers.Private) != 0) + a |= MemberAttributes.Private; + + return a; + } + + CodeObject IAstVisitor.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + CodeNamespace ns = new CodeNamespace(namespaceDeclaration.Name); + foreach (AstNode node in namespaceDeclaration.Members) { + CodeObject r = node.AcceptVisitor(this); + + CodeNamespaceImport import = r as CodeNamespaceImport; + if (import != null) + ns.Imports.Add(import); + + CodeTypeDeclaration typeDecl = r as CodeTypeDeclaration; + if (typeDecl != null) + ns.Types.Add(typeDecl); + } + return ns; + } + + Stack typeStack = new Stack(); + + CodeObject IAstVisitor.VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + //bool isNestedType = typeStack.Count > 0; + CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(typeDeclaration.Name); + typeDecl.Attributes = ConvertMemberAttributes(typeDeclaration.Modifiers, SymbolKind.TypeDefinition); + typeDecl.CustomAttributes.AddRange(Convert(typeDeclaration.Attributes)); + + switch (typeDeclaration.ClassType) { + case ClassType.Struct: + typeDecl.IsStruct = true; + break; + case ClassType.Interface: + typeDecl.IsInterface = true; + break; + case ClassType.Enum: + typeDecl.IsEnum = true; + break; + default: + typeDecl.IsClass = true; + break; + } + typeDecl.IsPartial = (typeDeclaration.Modifiers & Modifiers.Partial) == Modifiers.Partial; + + typeDecl.BaseTypes.AddRange(Convert(typeDeclaration.BaseTypes)); + typeDecl.TypeParameters.AddRange(ConvertTypeParameters(typeDeclaration.TypeParameters, typeDeclaration.Constraints)); + + typeStack.Push(typeDecl); + foreach (var member in typeDeclaration.Members) { + CodeTypeMember m = member.AcceptVisitor(this) as CodeTypeMember; + if (m != null) + typeDecl.Members.Add(m); + } + typeStack.Pop(); + return typeDecl; + } + + void AddTypeMember(CodeTypeMember member) + { + if (typeStack.Count != 0) + typeStack.Peek().Members.Add(member); + } + + CodeObject IAstVisitor.VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(usingAliasDeclaration)); + } + + CodeObject IAstVisitor.VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + return new CodeNamespaceImport(usingDeclaration.Namespace); + } + + CodeObject IAstVisitor.VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(externAliasDeclaration)); + } + + CodeObject IAstVisitor.VisitBlockStatement(BlockStatement blockStatement) + { + return new CodeConditionStatement(new CodePrimitiveExpression(true), ConvertBlock(blockStatement)); + } + + CodeObject IAstVisitor.VisitBreakStatement(BreakStatement breakStatement) + { + return MakeSnippetStatement(breakStatement); + } + + CodeObject IAstVisitor.VisitCheckedStatement(CheckedStatement checkedStatement) + { + return MakeSnippetStatement(checkedStatement); + } + + CodeObject IAstVisitor.VisitContinueStatement(ContinueStatement continueStatement) + { + return MakeSnippetStatement(continueStatement); + } + + CodeObject IAstVisitor.VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + // do { } while (expr); + // + // emulate with: + // for (bool _do = true; _do; _do = expr) {} + string varName = "_do" + doWhileStatement.Ancestors.OfType().Count(); + return new CodeIterationStatement( + new CodeVariableDeclarationStatement(typeof(bool), varName, new CodePrimitiveExpression(true)), + new CodeVariableReferenceExpression(varName), + new CodeAssignStatement(new CodeVariableReferenceExpression(varName), Convert(doWhileStatement.Condition)), + ConvertEmbeddedStatement(doWhileStatement.EmbeddedStatement) + ); + } + + CodeObject IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement) + { + return EmptyStatement(); + } + + CodeStatement EmptyStatement() + { + return new CodeExpressionStatement(new CodeObjectCreateExpression(new CodeTypeReference(typeof(object)))); + } + + CodeObject IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement) + { + AssignmentExpression assignment = expressionStatement.Expression as AssignmentExpression; + if (assignment != null && assignment.Operator == AssignmentOperatorType.Assign) { + return new CodeAssignStatement(Convert(assignment.Left), Convert(assignment.Right)); + } else if (assignment != null && CanBeDuplicatedForCompoundAssignment(assignment.Left)) { + CodeBinaryOperatorType op; + switch (assignment.Operator) { + case AssignmentOperatorType.Add: + op = CodeBinaryOperatorType.Add; + break; + case AssignmentOperatorType.Subtract: + op = CodeBinaryOperatorType.Subtract; + break; + case AssignmentOperatorType.Multiply: + op = CodeBinaryOperatorType.Multiply; + break; + case AssignmentOperatorType.Divide: + op = CodeBinaryOperatorType.Divide; + break; + case AssignmentOperatorType.Modulus: + op = CodeBinaryOperatorType.Modulus; + break; + case AssignmentOperatorType.BitwiseAnd: + op = CodeBinaryOperatorType.BitwiseAnd; + break; + case AssignmentOperatorType.BitwiseOr: + op = CodeBinaryOperatorType.BitwiseOr; + break; + default: + return MakeSnippetStatement(expressionStatement); + } + var cboe = new CodeBinaryOperatorExpression(Convert(assignment.Left), op, Convert(assignment.Right)); + return new CodeAssignStatement(Convert(assignment.Left), cboe); + } + UnaryOperatorExpression unary = expressionStatement.Expression as UnaryOperatorExpression; + if (unary != null && CanBeDuplicatedForCompoundAssignment(unary.Expression)) { + var op = unary.Operator; + if (op == UnaryOperatorType.Increment || op == UnaryOperatorType.PostIncrement) { + var cboe = new CodeBinaryOperatorExpression(Convert(unary.Expression), CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)); + return new CodeAssignStatement(Convert(unary.Expression), cboe); + } else if (op == UnaryOperatorType.Decrement || op == UnaryOperatorType.PostDecrement) { + var cboe = new CodeBinaryOperatorExpression(Convert(unary.Expression), CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(1)); + return new CodeAssignStatement(Convert(unary.Expression), cboe); + } + } + if (assignment != null && assignment.Operator == AssignmentOperatorType.Add) { + var rr = Resolve(assignment.Left); + if (!rr.IsError && rr.Type.Kind == TypeKind.Delegate) { + var expr = (MemberReferenceExpression)assignment.Left; + var memberRef = (CodeEventReferenceExpression)HandleMemberReference(Convert(expr.Target), expr.MemberName, expr.TypeArguments, (MemberResolveResult)rr); + return new CodeAttachEventStatement(memberRef, Convert(assignment.Right)); + } + } + return new CodeExpressionStatement(Convert(expressionStatement.Expression)); + } + + bool CanBeDuplicatedForCompoundAssignment(Expression expr) + { + return expr is IdentifierExpression; + } + + CodeObject IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement) + { + return MakeSnippetStatement(fixedStatement); + } + + CodeObject IAstVisitor.VisitForeachStatement(ForeachStatement foreachStatement) + { + return MakeSnippetStatement(foreachStatement); + } + + CodeObject IAstVisitor.VisitForStatement(ForStatement forStatement) + { + if (forStatement.Initializers.Count != 1 || forStatement.Iterators.Count != 1) + return MakeSnippetStatement(forStatement); + return new CodeIterationStatement( + Convert(forStatement.Initializers.Single()), + Convert(forStatement.Condition), + Convert(forStatement.Iterators.Single()), + ConvertEmbeddedStatement(forStatement.EmbeddedStatement) + ); + } + + CodeObject IAstVisitor.VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) + { + return MakeSnippetStatement(gotoCaseStatement); + } + + CodeObject IAstVisitor.VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) + { + return MakeSnippetStatement(gotoDefaultStatement); + } + + CodeObject IAstVisitor.VisitGotoStatement(GotoStatement gotoStatement) + { + return new CodeGotoStatement(gotoStatement.Label); + } + + CodeObject IAstVisitor.VisitIfElseStatement(IfElseStatement ifElseStatement) + { + return new CodeConditionStatement( + Convert(ifElseStatement.Condition), + ConvertEmbeddedStatement(ifElseStatement.TrueStatement), + ConvertEmbeddedStatement(ifElseStatement.FalseStatement)); + } + + CodeObject IAstVisitor.VisitLabelStatement(LabelStatement labelStatement) + { + return new CodeLabeledStatement(labelStatement.Label); + } + + CodeObject IAstVisitor.VisitLockStatement(LockStatement lockStatement) + { + return MakeSnippetStatement(lockStatement); + } + + CodeObject IAstVisitor.VisitReturnStatement(ReturnStatement returnStatement) + { + return new CodeMethodReturnStatement(Convert(returnStatement.Expression)); + } + + CodeObject IAstVisitor.VisitSwitchStatement(SwitchStatement switchStatement) + { + return MakeSnippetStatement(switchStatement); + } + + CodeObject IAstVisitor.VisitSwitchSection(SwitchSection switchSection) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitCaseLabel(CaseLabel caseLabel) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitThrowStatement(ThrowStatement throwStatement) + { + return new CodeThrowExceptionStatement(Convert(throwStatement.Expression)); + } + + CodeObject IAstVisitor.VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + List catchClauses = new List(); + foreach (var catchClause in tryCatchStatement.CatchClauses) { + catchClauses.Add(new CodeCatchClause(catchClause.VariableName, Convert(catchClause.Type), ConvertBlock(catchClause.Body))); + } + return new CodeTryCatchFinallyStatement( + ConvertBlock(tryCatchStatement.TryBlock), + catchClauses.ToArray(), + ConvertBlock(tryCatchStatement.FinallyBlock)); + } + + CodeObject IAstVisitor.VisitCatchClause(CatchClause catchClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + return MakeSnippetStatement(uncheckedStatement); + } + + CodeObject IAstVisitor.VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + return MakeSnippetStatement(unsafeStatement); + } + + CodeObject IAstVisitor.VisitUsingStatement(UsingStatement usingStatement) + { + return MakeSnippetStatement(usingStatement); + } + + CodeObject IAstVisitor.VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + if (variableDeclarationStatement.Variables.Count != 1) + return MakeSnippetStatement(variableDeclarationStatement); + VariableInitializer vi = variableDeclarationStatement.Variables.Single(); + return new CodeVariableDeclarationStatement( + Convert(variableDeclarationStatement.Type), + vi.Name, + ConvertVariableInitializer(vi.Initializer, variableDeclarationStatement.Type)); + } + + CodeExpression ConvertVariableInitializer(Expression expr, AstType type) + { + ArrayInitializerExpression aie = expr as ArrayInitializerExpression; + if (aie != null) { + return new CodeArrayCreateExpression(Convert(type), Convert(aie.Elements)); + } else { + return Convert(expr); + } + } + + CodeObject IAstVisitor.VisitWhileStatement(WhileStatement whileStatement) + { + return new CodeIterationStatement(EmptyStatement(), Convert(whileStatement.Condition), EmptyStatement(), ConvertEmbeddedStatement(whileStatement.EmbeddedStatement)); + } + + CodeObject IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + return MakeSnippetStatement(yieldBreakStatement); + } + + CodeObject IAstVisitor.VisitYieldReturnStatement(YieldReturnStatement yieldStatement) + { + return MakeSnippetStatement(yieldStatement); + } + + CodeObject IAstVisitor.VisitAccessor(Accessor accessor) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + CodeConstructor ctor = new CodeConstructor(); + ctor.Attributes = ConvertMemberAttributes(constructorDeclaration.Modifiers, SymbolKind.Constructor); + ctor.CustomAttributes.AddRange(Convert(constructorDeclaration.Attributes)); + if (constructorDeclaration.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) { + ctor.ChainedConstructorArgs.AddRange(Convert(constructorDeclaration.Initializer.Arguments)); + } else { + ctor.BaseConstructorArgs.AddRange(Convert(constructorDeclaration.Initializer.Arguments)); + } + ctor.Parameters.AddRange(Convert(constructorDeclaration.Parameters)); + + ctor.Statements.AddRange(ConvertBlock(constructorDeclaration.Body)); + return ctor; + } + + CodeObject IAstVisitor.VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(destructorDeclaration)); + } + + CodeObject IAstVisitor.VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + TypeDeclaration td = enumMemberDeclaration.Parent as TypeDeclaration; + CodeMemberField f = new CodeMemberField(td != null ? td.Name : "Enum", enumMemberDeclaration.Name); + f.Attributes = MemberAttributes.Public | MemberAttributes.Static; + f.CustomAttributes.AddRange(Convert(enumMemberDeclaration.Attributes)); + f.InitExpression = Convert(enumMemberDeclaration.Initializer); + return f; + } + + CodeObject IAstVisitor.VisitEventDeclaration(EventDeclaration eventDeclaration) + { + foreach (VariableInitializer vi in eventDeclaration.Variables) { + if (!vi.Initializer.IsNull) { + AddTypeMember(new CodeSnippetTypeMember(MakeSnippet(eventDeclaration))); + continue; + } + + CodeMemberEvent e = new CodeMemberEvent(); + e.Attributes = ConvertMemberAttributes(eventDeclaration.Modifiers, SymbolKind.Event); + e.CustomAttributes.AddRange(Convert(eventDeclaration.Attributes)); + e.Name = vi.Name; + e.Type = Convert(eventDeclaration.ReturnType); + AddTypeMember(e); + } + return null; + } + + CodeObject IAstVisitor.VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(customEventDeclaration)); + } + + CodeObject IAstVisitor.VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + foreach (VariableInitializer vi in fieldDeclaration.Variables) { + CodeMemberField f = new CodeMemberField(Convert(fieldDeclaration.ReturnType), vi.Name); + f.Attributes = ConvertMemberAttributes(fieldDeclaration.Modifiers, SymbolKind.Field); + f.CustomAttributes.AddRange(Convert(fieldDeclaration.Attributes)); + f.InitExpression = ConvertVariableInitializer(vi.Initializer, fieldDeclaration.ReturnType); + AddTypeMember(f); + } + return null; + } + + CodeObject IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + CodeMemberProperty p = new CodeMemberProperty(); + p.Attributes = ConvertMemberAttributes(indexerDeclaration.Modifiers, SymbolKind.Indexer); + p.CustomAttributes.AddRange(Convert(indexerDeclaration.Attributes)); + p.Name = "Items"; + p.PrivateImplementationType = Convert(indexerDeclaration.PrivateImplementationType); + p.Parameters.AddRange(Convert(indexerDeclaration.Parameters)); + p.Type = Convert(indexerDeclaration.ReturnType); + + if (!indexerDeclaration.Getter.IsNull) { + p.HasGet = true; + p.GetStatements.AddRange(ConvertBlock(indexerDeclaration.Getter.Body)); + } + if (!indexerDeclaration.Setter.IsNull) { + p.HasSet = true; + p.SetStatements.AddRange(ConvertBlock(indexerDeclaration.Setter.Body)); + } + return p; + } + + CodeObject IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + CodeMemberMethod m = new CodeMemberMethod(); + m.Attributes = ConvertMemberAttributes(methodDeclaration.Modifiers, SymbolKind.Method); + + m.CustomAttributes.AddRange(Convert(methodDeclaration.Attributes.Where(a => a.AttributeTarget != "return"))); + m.ReturnTypeCustomAttributes.AddRange(Convert(methodDeclaration.Attributes.Where(a => a.AttributeTarget == "return"))); + + m.ReturnType = Convert(methodDeclaration.ReturnType); + m.PrivateImplementationType = Convert(methodDeclaration.PrivateImplementationType); + m.Name = methodDeclaration.Name; + m.TypeParameters.AddRange(ConvertTypeParameters(methodDeclaration.TypeParameters, methodDeclaration.Constraints)); + m.Parameters.AddRange(Convert(methodDeclaration.Parameters)); + + m.Statements.AddRange(ConvertBlock(methodDeclaration.Body)); + return m; + } + + CodeObject IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + CodeMemberMethod m = new CodeMemberMethod(); + m.Attributes = ConvertMemberAttributes(operatorDeclaration.Modifiers, SymbolKind.Method); + + m.CustomAttributes.AddRange(Convert(operatorDeclaration.Attributes.Where(a => a.AttributeTarget != "return"))); + m.ReturnTypeCustomAttributes.AddRange(Convert(operatorDeclaration.Attributes.Where(a => a.AttributeTarget == "return"))); + + m.ReturnType = Convert(operatorDeclaration.ReturnType); + m.Name = operatorDeclaration.Name; + m.Parameters.AddRange(Convert(operatorDeclaration.Parameters)); + + m.Statements.AddRange(ConvertBlock(operatorDeclaration.Body)); + return m; + } + + CodeObject IAstVisitor.VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + var p = new CodeParameterDeclarationExpression(Convert(parameterDeclaration.Type), parameterDeclaration.Name); + p.CustomAttributes.AddRange(Convert(parameterDeclaration.Attributes)); + switch (parameterDeclaration.ParameterModifier) { + case ParameterModifier.Ref: + p.Direction = System.CodeDom.FieldDirection.Ref; + break; + case ParameterModifier.Out: + p.Direction = System.CodeDom.FieldDirection.Out; + break; + } + return p; + } + + CodeParameterDeclarationExpression[] Convert(IEnumerable parameters) + { + List result = new List(); + foreach (ParameterDeclaration pd in parameters) { + CodeParameterDeclarationExpression pde = pd.AcceptVisitor(this) as CodeParameterDeclarationExpression; + if (pde != null) + result.Add(pde); + } + return result.ToArray(); + } + + CodeObject IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + CodeMemberProperty p = new CodeMemberProperty(); + p.Attributes = ConvertMemberAttributes(propertyDeclaration.Modifiers, SymbolKind.Property); + p.CustomAttributes.AddRange(Convert(propertyDeclaration.Attributes)); + p.Name = propertyDeclaration.Name; + p.PrivateImplementationType = Convert(propertyDeclaration.PrivateImplementationType); + p.Type = Convert(propertyDeclaration.ReturnType); + + if (!propertyDeclaration.Getter.IsNull) { + p.HasGet = true; + p.GetStatements.AddRange(ConvertBlock(propertyDeclaration.Getter.Body)); + } + if (!propertyDeclaration.Setter.IsNull) { + p.HasSet = true; + p.SetStatements.AddRange(ConvertBlock(propertyDeclaration.Setter.Body)); + } + return p; + } + + CodeObject IAstVisitor.VisitVariableInitializer(VariableInitializer variableInitializer) + { + throw new NotSupportedException(); // should be handled by the parent node + } + + CodeObject IAstVisitor.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(fixedFieldDeclaration)); + } + + CodeObject IAstVisitor.VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) + { + throw new NotSupportedException(); // should be handled by the parent node + } + + CodeObject IAstVisitor.VisitSyntaxTree(SyntaxTree syntaxTree) + { + CodeCompileUnit cu = new CodeCompileUnit(); + var globalImports = new List (); + foreach (AstNode node in syntaxTree.Children) { + CodeObject o = node.AcceptVisitor(this); + + CodeNamespace ns = o as CodeNamespace; + if (ns != null) { + cu.Namespaces.Add(ns); + } + CodeTypeDeclaration td = o as CodeTypeDeclaration; + if (td != null) { + cu.Namespaces.Add(new CodeNamespace() { Types = { td } }); + } + + var import = o as CodeNamespaceImport; + if (import != null) + globalImports.Add (import); + } + foreach (var gi in globalImports) { + for (int j = 0; j < cu.Namespaces.Count; j++) { + var cn = cu.Namespaces [j]; + bool found = cn.Imports + .Cast () + .Any (ns => ns.Namespace == gi.Namespace); + if (!found) + cn.Imports.Add (gi); + } + } + return cu; + } + + CodeObject IAstVisitor.VisitSimpleType(SimpleType simpleType) + { + if (UseFullyQualifiedTypeNames) { + IType type = Resolve(simpleType).Type; + if (type.Kind != TypeKind.Unknown) + return Convert(type); + } + var tr = new CodeTypeReference(simpleType.Identifier); + tr.TypeArguments.AddRange(Convert(simpleType.TypeArguments)); + return tr; + } + + CodeObject IAstVisitor.VisitMemberType(MemberType memberType) + { + if (memberType.IsDoubleColon && new SimpleType("global").IsMatch(memberType.Target)) { + var tr = new CodeTypeReference(memberType.MemberName, CodeTypeReferenceOptions.GlobalReference); + tr.TypeArguments.AddRange(Convert(memberType.TypeArguments)); + return tr; + } + if (UseFullyQualifiedTypeNames || memberType.IsDoubleColon) { + IType type = Resolve(memberType).Type; + if (type.Kind != TypeKind.Unknown) + return Convert(type); + } + CodeTypeReference target = Convert(memberType.Target); + if (target == null) + return null; + target.BaseType = target.BaseType + "." + memberType.MemberName; + target.TypeArguments.AddRange(Convert(memberType.TypeArguments)); + return target; + } + + CodeObject IAstVisitor.VisitComposedType(ComposedType composedType) + { + CodeTypeReference typeRef = Convert(composedType.BaseType); + if (typeRef == null) + return null; + if (composedType.HasNullableSpecifier) { + typeRef = new CodeTypeReference("System.Nullable") { TypeArguments = { typeRef } }; + } + foreach (ArraySpecifier s in composedType.ArraySpecifiers.Reverse()) { + typeRef = new CodeTypeReference(typeRef, s.Dimensions); + } + return typeRef; + } + + CodeObject IAstVisitor.VisitArraySpecifier(ArraySpecifier arraySpecifier) + { + throw new NotSupportedException(); // handled by parent node + } + + CodeObject IAstVisitor.VisitPrimitiveType(PrimitiveType primitiveType) + { + KnownTypeCode typeCode = primitiveType.KnownTypeCode; + if (typeCode != KnownTypeCode.None) { + KnownTypeReference ktr = KnownTypeReference.Get(typeCode); + return new CodeTypeReference(ktr.Namespace + "." + ktr.Name); + } + return new CodeTypeReference(primitiveType.Keyword); + } + + CodeObject IAstVisitor.VisitComment (Comment comment) + { + return new CodeComment (comment.Content, comment.CommentType == CommentType.Documentation); + } + + CodeObject IAstVisitor.VisitNewLine(NewLineNode newLineNode) + { + return null; + } + + CodeObject IAstVisitor.VisitWhitespace(WhitespaceNode whitespaceNode) + { + return null; + } + + CodeObject IAstVisitor.VisitText(TextNode textNode) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) + { + return new CodeComment ("#" + preProcessorDirective.Type.ToString ().ToLowerInvariant ()); + } + + CodeObject IAstVisitor.VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + throw new NotSupportedException(); // type parameters and constraints are handled together + } + + CodeObject IAstVisitor.VisitConstraint(Constraint constraint) + { + throw new NotSupportedException(); + } + + CodeTypeParameter[] ConvertTypeParameters(IEnumerable typeParameters, IEnumerable constraints) + { + List result = new List(); + foreach (TypeParameterDeclaration tpDecl in typeParameters) { + CodeTypeParameter tp = new CodeTypeParameter(tpDecl.Name); + tp.CustomAttributes.AddRange(Convert(tpDecl.Attributes)); + foreach (Constraint constraint in constraints) { + if (constraint.TypeParameter.Identifier == tp.Name) { + foreach (AstType baseType in constraint.BaseTypes) { + if (baseType is PrimitiveType && ((PrimitiveType)baseType).Keyword == "new") { + tp.HasConstructorConstraint = true; + } else { + CodeTypeReference tr = Convert(baseType); + if (tr != null) + tp.Constraints.Add(tr); + } + } + } + } + result.Add(tp); + } + return result.ToArray(); + } + + CodeObject IAstVisitor.VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) + { + return null; + } + + CodeObject IAstVisitor.VisitIdentifier(Identifier identifier) + { + return null; + } + + CodeObject IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, Pattern pattern) + { + return null; + } + + CodeObject IAstVisitor.VisitDocumentationReference(DocumentationReference documentationReference) + { + return null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/ITokenWriter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/ITokenWriter.cs new file mode 100644 index 000000000..31b73f987 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/ITokenWriter.cs @@ -0,0 +1,161 @@ +// Copyright (c) 2010-2013 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.IO; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class TokenWriter + { + public abstract void StartNode(AstNode node); + public abstract void EndNode(AstNode node); + + /// + /// Writes an identifier. + /// + public abstract void WriteIdentifier(Identifier identifier); + + /// + /// Writes a keyword to the output. + /// + public abstract void WriteKeyword(Role role, string keyword); + + /// + /// Writes a token to the output. + /// + public abstract void WriteToken(Role role, string token); + + /// + /// Writes a primitive/literal value + /// + public abstract void WritePrimitiveValue(object value, string literalValue = null); + + public abstract void WritePrimitiveType(string type); + + public abstract void Space(); + public abstract void Indent(); + public abstract void Unindent(); + public abstract void NewLine(); + + public abstract void WriteComment(CommentType commentType, string content); + public abstract void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument); + + public static TokenWriter Create(TextWriter writer, string indentation = "\t") + { + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new TextWriterTokenWriter(writer) { IndentationString = indentation })); + } + + public static TokenWriter CreateWriterThatSetsLocationsInAST(TextWriter writer, string indentation = "\t") + { + var target = new TextWriterTokenWriter(writer) { IndentationString = indentation }; + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(target, target))); + } + + public static TokenWriter WrapInWriterThatSetsLocationsInAST(TokenWriter writer) + { + if (!(writer is ILocatable)) + throw new InvalidOperationException("writer does not provide locations!"); + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(writer, (ILocatable)writer))); + } + } + + public interface ILocatable + { + TextLocation Location { get; } + } + + public abstract class DecoratingTokenWriter : TokenWriter + { + TokenWriter decoratedWriter; + + protected DecoratingTokenWriter(TokenWriter decoratedWriter) + { + if (decoratedWriter == null) + throw new ArgumentNullException("decoratedWriter"); + this.decoratedWriter = decoratedWriter; + } + + public override void StartNode(AstNode node) + { + decoratedWriter.StartNode(node); + } + + public override void EndNode(AstNode node) + { + decoratedWriter.EndNode(node); + } + + public override void WriteIdentifier(Identifier identifier) + { + decoratedWriter.WriteIdentifier(identifier); + } + + public override void WriteKeyword(Role role, string keyword) + { + decoratedWriter.WriteKeyword(role, keyword); + } + + public override void WriteToken(Role role, string token) + { + decoratedWriter.WriteToken(role, token); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + decoratedWriter.WritePrimitiveValue(value, literalValue); + } + + public override void WritePrimitiveType(string type) + { + decoratedWriter.WritePrimitiveType(type); + } + + public override void Space() + { + decoratedWriter.Space(); + } + + public override void Indent() + { + decoratedWriter.Indent(); + } + + public override void Unindent() + { + decoratedWriter.Unindent(); + } + + public override void NewLine() + { + decoratedWriter.NewLine(); + } + + public override void WriteComment(CommentType commentType, string content) + { + decoratedWriter.WriteComment(commentType, content); + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + decoratedWriter.WritePreProcessorDirective(type, argument); + } + } +} + + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertMissingTokensDecorator.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertMissingTokensDecorator.cs new file mode 100644 index 000000000..3f9200145 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertMissingTokensDecorator.cs @@ -0,0 +1,123 @@ +// Copyright (c) 2010-2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertMissingTokensDecorator : DecoratingTokenWriter + { + readonly Stack> nodes = new Stack>(); + List currentList; + readonly ILocatable locationProvider; + + public InsertMissingTokensDecorator(TokenWriter writer, ILocatable locationProvider) + : base(writer) + { + this.locationProvider = locationProvider; + currentList = new List(); + } + + public override void StartNode(AstNode node) + { + currentList.Add(node); + nodes.Push(currentList); + currentList = new List(); + base.StartNode(node); + } + + public override void EndNode(AstNode node) + { + System.Diagnostics.Debug.Assert(currentList != null); + foreach (var removable in node.Children.Where(n => n is CSharpTokenNode)) { + removable.Remove(); + } + foreach (var child in currentList) { + System.Diagnostics.Debug.Assert(child.Parent == null || node == child.Parent); + child.Remove(); + node.AddChildWithExistingRole(child); + } + currentList = nodes.Pop(); + base.EndNode(node); + } + + public override void WriteToken(Role role, string token) + { + CSharpTokenNode t = new CSharpTokenNode(locationProvider.Location, (TokenRole)role); + t.Role = role; + EmptyStatement node = nodes.Peek().LastOrDefault() as EmptyStatement; + if (node == null) + currentList.Add(t); + else { + node.Location = locationProvider.Location; + } + base.WriteToken(role, token); + } + + public override void WriteKeyword(Role role, string keyword) + { + TextLocation start = locationProvider.Location; + CSharpTokenNode t = null; + if (role is TokenRole) + t = new CSharpTokenNode(start, (TokenRole)role); + else if (role == EntityDeclaration.ModifierRole) + t = new CSharpModifierToken(start, CSharpModifierToken.GetModifierValue(keyword)); + else if (keyword == "this") { + ThisReferenceExpression node = nodes.Peek().LastOrDefault() as ThisReferenceExpression; + if (node != null) + node.Location = start; + } else if (keyword == "base") { + BaseReferenceExpression node = nodes.Peek().LastOrDefault() as BaseReferenceExpression; + if (node != null) + node.Location = start; + } + if (t != null) currentList.Add(t); + base.WriteKeyword(role, keyword); + } + + public override void WriteIdentifier(Identifier identifier) + { + if (!identifier.IsNull) + identifier.SetStartLocation(locationProvider.Location); + currentList.Add(identifier); + base.WriteIdentifier(identifier); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + Expression node = nodes.Peek().LastOrDefault() as Expression; + if (node is PrimitiveExpression) { + ((PrimitiveExpression)node).SetStartLocation(locationProvider.Location); + } + if (node is NullReferenceExpression) { + ((NullReferenceExpression)node).SetStartLocation(locationProvider.Location); + } + base.WritePrimitiveValue(value, literalValue); + } + + public override void WritePrimitiveType(string type) + { + PrimitiveType node = nodes.Peek().LastOrDefault() as PrimitiveType; + if (node != null) + node.SetStartLocation(locationProvider.Location); + base.WritePrimitiveType(type); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs new file mode 100644 index 000000000..6d622de23 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -0,0 +1,345 @@ +// Copyright (c) 2010-2013 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 +{ + /// + /// Inserts the parentheses into the AST that are needed to ensure the AST can be printed correctly. + /// For example, if the AST contains + /// BinaryOperatorExpresson(2, Mul, BinaryOperatorExpression(1, Add, 1))); printing that AST + /// would incorrectly result in "2 * 1 + 1". By running InsertParenthesesVisitor, the necessary + /// parentheses are inserted: "2 * (1 + 1)". + /// + public class InsertParenthesesVisitor : DepthFirstAstVisitor + { + /// + /// Gets/Sets whether the visitor should insert parentheses to make the code better looking. + /// If this property is false, it will insert parentheses only where strictly required by the language spec. + /// + public bool InsertParenthesesForReadability { get; set; } + + const int Primary = 16; + const int QueryOrLambda = 15; + const int Unary = 14; + const int RelationalAndTypeTesting = 10; + const int Equality = 9; + const int Conditional = 2; + const int Assignment = 1; + + /// + /// Gets the row number in the C# 4.0 spec operator precedence table. + /// + static int GetPrecedence(Expression expr) + { + // Note: the operator precedence table on MSDN is incorrect + if (expr is QueryExpression) { + // Not part of the table in the C# spec, but we need to ensure that queries within + // primary expressions get parenthesized. + return QueryOrLambda; + } + UnaryOperatorExpression uoe = expr as UnaryOperatorExpression; + if (uoe != null) { + if (uoe.Operator == UnaryOperatorType.PostDecrement || uoe.Operator == UnaryOperatorType.PostIncrement) + return Primary; + else + return Unary; + } + if (expr is CastExpression) + return Unary; + BinaryOperatorExpression boe = expr as BinaryOperatorExpression; + if (boe != null) { + switch (boe.Operator) { + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + return 13; // multiplicative + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + return 12; // additive + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + return 11; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThan: + case BinaryOperatorType.LessThanOrEqual: + return RelationalAndTypeTesting; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + return Equality; + case BinaryOperatorType.BitwiseAnd: + return 8; + case BinaryOperatorType.ExclusiveOr: + return 7; + case BinaryOperatorType.BitwiseOr: + return 6; + case BinaryOperatorType.ConditionalAnd: + return 5; + case BinaryOperatorType.ConditionalOr: + return 4; + case BinaryOperatorType.NullCoalescing: + return 3; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + if (expr is IsExpression || expr is AsExpression) + return RelationalAndTypeTesting; + if (expr is ConditionalExpression) + return Conditional; + if (expr is AssignmentExpression || expr is LambdaExpression) + return Assignment; + // anything else: primary expression + return Primary; + } + + /// + /// Parenthesizes the expression if it does not have the minimum required precedence. + /// + static void ParenthesizeIfRequired(Expression expr, int minimumPrecedence) + { + if (GetPrecedence(expr) < minimumPrecedence) { + Parenthesize(expr); + } + } + + static void Parenthesize(Expression expr) + { + expr.ReplaceWith(e => new ParenthesizedExpression { Expression = e }); + } + + // Primary expressions + public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + ParenthesizeIfRequired(memberReferenceExpression.Target, Primary); + base.VisitMemberReferenceExpression(memberReferenceExpression); + } + + public override void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + ParenthesizeIfRequired(pointerReferenceExpression.Target, Primary); + base.VisitPointerReferenceExpression(pointerReferenceExpression); + } + + public override void VisitInvocationExpression(InvocationExpression invocationExpression) + { + ParenthesizeIfRequired(invocationExpression.Target, Primary); + base.VisitInvocationExpression(invocationExpression); + } + + public override void VisitIndexerExpression(IndexerExpression indexerExpression) + { + ParenthesizeIfRequired(indexerExpression.Target, Primary); + ArrayCreateExpression ace = indexerExpression.Target as ArrayCreateExpression; + if (ace != null && (InsertParenthesesForReadability || ace.Initializer.IsNull)) { + // require parentheses for "(new int[1])[0]" + Parenthesize(indexerExpression.Target); + } + base.VisitIndexerExpression(indexerExpression); + } + + // Unary expressions + public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + ParenthesizeIfRequired(unaryOperatorExpression.Expression, GetPrecedence(unaryOperatorExpression)); + UnaryOperatorExpression child = unaryOperatorExpression.Expression as UnaryOperatorExpression; + if (child != null && InsertParenthesesForReadability) + Parenthesize(child); + base.VisitUnaryOperatorExpression(unaryOperatorExpression); + } + + public override void VisitCastExpression(CastExpression castExpression) + { + ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? Primary : Unary); + // There's a nasty issue in the C# grammar: cast expressions including certain operators are ambiguous in some cases + // "(int)-1" is fine, but "(A)-b" is not a cast. + UnaryOperatorExpression uoe = castExpression.Expression as UnaryOperatorExpression; + if (uoe != null && !(uoe.Operator == UnaryOperatorType.BitNot || uoe.Operator == UnaryOperatorType.Not)) { + if (TypeCanBeMisinterpretedAsExpression(castExpression.Type)) { + Parenthesize(castExpression.Expression); + } + } + // The above issue can also happen with PrimitiveExpressions representing negative values: + PrimitiveExpression pe = castExpression.Expression as PrimitiveExpression; + if (pe != null && pe.Value != null && TypeCanBeMisinterpretedAsExpression(castExpression.Type)) { + TypeCode typeCode = Type.GetTypeCode(pe.Value.GetType()); + switch (typeCode) { + case TypeCode.SByte: + if ((sbyte)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int16: + if ((short)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int32: + if ((int)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int64: + if ((long)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Single: + if ((float)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Double: + if ((double)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Decimal: + if ((decimal)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + } + } + base.VisitCastExpression(castExpression); + } + + static bool TypeCanBeMisinterpretedAsExpression(AstType type) + { + // SimpleTypes can always be misinterpreted as IdentifierExpressions + // MemberTypes can be misinterpreted as MemberReferenceExpressions if they don't use double colon + // PrimitiveTypes or ComposedTypes can never be misinterpreted as expressions. + MemberType mt = type as MemberType; + if (mt != null) + return !mt.IsDoubleColon; + else + return type is SimpleType; + } + + // Binary Operators + public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + int precedence = GetPrecedence(binaryOperatorExpression); + if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { + if (InsertParenthesesForReadability) { + ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary); + ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary); + } else { + // ?? is right-associative + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence + 1); + ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence); + } + } else { + if (InsertParenthesesForReadability && precedence < Equality) { + // In readable mode, boost the priority of the left-hand side if the operator + // there isn't the same as the operator on this expression. + if (GetBinaryOperatorType(binaryOperatorExpression.Left) == binaryOperatorExpression.Operator) { + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); + } else { + ParenthesizeIfRequired(binaryOperatorExpression.Left, Equality); + } + ParenthesizeIfRequired(binaryOperatorExpression.Right, Equality); + } else { + // all other binary operators are left-associative + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); + ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence + 1); + } + } + base.VisitBinaryOperatorExpression(binaryOperatorExpression); + } + + BinaryOperatorType? GetBinaryOperatorType(Expression expr) + { + BinaryOperatorExpression boe = expr as BinaryOperatorExpression; + if (boe != null) + return boe.Operator; + else + return null; + } + + public override void VisitIsExpression(IsExpression isExpression) + { + if (InsertParenthesesForReadability) { + // few people know the precedence of 'is', so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(isExpression.Expression, Primary); + } else { + ParenthesizeIfRequired(isExpression.Expression, RelationalAndTypeTesting); + } + base.VisitIsExpression(isExpression); + } + + public override void VisitAsExpression(AsExpression asExpression) + { + if (InsertParenthesesForReadability) { + // few people know the precedence of 'as', so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(asExpression.Expression, Primary); + } else { + ParenthesizeIfRequired(asExpression.Expression, RelationalAndTypeTesting); + } + base.VisitAsExpression(asExpression); + } + + // Conditional operator + public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + // Associativity here is a bit tricky: + // (a ? b : c ? d : e) == (a ? b : (c ? d : e)) + // (a ? b ? c : d : e) == (a ? (b ? c : d) : e) + // Only ((a ? b : c) ? d : e) strictly needs the additional parentheses + if (InsertParenthesesForReadability) { + // Precedence of ?: can be confusing; so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(conditionalExpression.Condition, Primary); + ParenthesizeIfRequired(conditionalExpression.TrueExpression, Primary); + ParenthesizeIfRequired(conditionalExpression.FalseExpression, Primary); + } else { + ParenthesizeIfRequired(conditionalExpression.Condition, Conditional + 1); + ParenthesizeIfRequired(conditionalExpression.TrueExpression, Conditional); + ParenthesizeIfRequired(conditionalExpression.FalseExpression, Conditional); + } + base.VisitConditionalExpression(conditionalExpression); + } + + public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + // assignment is right-associative + ParenthesizeIfRequired(assignmentExpression.Left, Assignment + 1); + if (InsertParenthesesForReadability) { + ParenthesizeIfRequired(assignmentExpression.Right, RelationalAndTypeTesting + 1); + } else { + ParenthesizeIfRequired(assignmentExpression.Right, Assignment); + } + base.VisitAssignmentExpression(assignmentExpression); + } + + // don't need to handle lambdas, they have lowest precedence and unambiguous associativity + + public override void VisitQueryExpression(QueryExpression queryExpression) + { + // Query expressions are strange beasts: + // "var a = -from b in c select d;" is valid, so queries bind stricter than unary expressions. + // However, the end of the query is greedy. So their start sort of has a high precedence, + // while their end has a very low precedence. We handle this by checking whether a query is used + // as left part of a binary operator, and parenthesize it if required. + if (queryExpression.Role == BinaryOperatorExpression.LeftRole) + Parenthesize(queryExpression); + if (queryExpression.Parent is IsExpression || queryExpression.Parent is AsExpression) + Parenthesize(queryExpression); + if (InsertParenthesesForReadability) { + // when readability is desired, always parenthesize query expressions within unary or binary operators + if (queryExpression.Parent is UnaryOperatorExpression || queryExpression.Parent is BinaryOperatorExpression) + Parenthesize(queryExpression); + } + base.VisitQueryExpression(queryExpression); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs new file mode 100644 index 000000000..e9aca4bf5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs @@ -0,0 +1,184 @@ +// Copyright (c) 2010-2013 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.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertRequiredSpacesDecorator : DecoratingTokenWriter + { + /// + /// Used to insert the minimal amount of spaces so that the lexer recognizes the tokens that were written. + /// + LastWritten lastWritten; + + enum LastWritten + { + Whitespace, + Other, + KeywordOrIdentifier, + Plus, + Minus, + Ampersand, + QuestionMark, + Division + } + + public InsertRequiredSpacesDecorator(TokenWriter writer) + : base(writer) + { + } + + public override void WriteIdentifier(Identifier identifier) + { + if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + // this space is not strictly required, so we call Space() + Space(); + } + } else if (lastWritten == LastWritten.KeywordOrIdentifier) { + // this space is strictly required, so we directly call the formatter + base.Space(); + } + base.WriteIdentifier(identifier); + lastWritten = LastWritten.KeywordOrIdentifier; + } + + public override void WriteKeyword(Role role, string keyword) + { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + Space(); + } + base.WriteKeyword(role, keyword); + lastWritten = LastWritten.KeywordOrIdentifier; + } + + public override void WriteToken(Role role, string token) + { + // Avoid that two +, - or ? tokens are combined into a ++, -- or ?? token. + // Note that we don't need to handle tokens like = because there's no valid + // C# program that contains the single token twice in a row. + // (for +, - and &, this can happen with unary operators; + // for ?, this can happen in "a is int? ? b : c" or "a as int? ?? 0"; + // and for /, this can happen with "1/ *ptr" or "1/ //comment".) + if (lastWritten == LastWritten.Plus && token[0] == '+' || + lastWritten == LastWritten.Minus && token[0] == '-' || + lastWritten == LastWritten.Ampersand && token[0] == '&' || + lastWritten == LastWritten.QuestionMark && token[0] == '?' || + lastWritten == LastWritten.Division && token[0] == '*') { + base.Space(); + } + base.WriteToken(role, token); + if (token == "+") { + lastWritten = LastWritten.Plus; + } else if (token == "-") { + lastWritten = LastWritten.Minus; + } else if (token == "&") { + lastWritten = LastWritten.Ampersand; + } else if (token == "?") { + lastWritten = LastWritten.QuestionMark; + } else if (token == "/") { + lastWritten = LastWritten.Division; + } else { + lastWritten = LastWritten.Other; + } + } + + public override void Space() + { + base.Space(); + lastWritten = LastWritten.Whitespace; + } + + public override void NewLine() + { + base.NewLine(); + lastWritten = LastWritten.Whitespace; + } + + public override void WriteComment(CommentType commentType, string content) + { + if (lastWritten == LastWritten.Division) { + // When there's a comment starting after a division operator + // "1.0 / /*comment*/a", then we need to insert a space in front of the comment. + base.Space(); + } + base.WriteComment(commentType, content); + lastWritten = LastWritten.Whitespace; + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + base.WritePreProcessorDirective(type, argument); + lastWritten = LastWritten.Whitespace; + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + base.WritePrimitiveValue(value, literalValue); + if (value == null || value is bool) + return; + if (value is string) { + lastWritten = LastWritten.Other; + } else if (value is char) { + lastWritten = LastWritten.Other; + } else if (value is decimal) { + lastWritten = LastWritten.Other; + } else if (value is float) { + float f = (float)value; + if (float.IsInfinity(f) || float.IsNaN(f)) return; + lastWritten = LastWritten.Other; + } else if (value is double) { + double f = (double)value; + if (double.IsInfinity(f) || double.IsNaN(f)) return; + // needs space if identifier follows number; + // this avoids mistaking the following identifier as type suffix + lastWritten = LastWritten.KeywordOrIdentifier; + } else if (value is IFormattable) { + // needs space if identifier follows number; + // this avoids mistaking the following identifier as type suffix + lastWritten = LastWritten.KeywordOrIdentifier; + } else { + lastWritten = LastWritten.Other; + } + } + + public override void WritePrimitiveType(string type) + { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + Space(); + } + base.WritePrimitiveType(type); + if (type == "new") { + lastWritten = LastWritten.Other; + } else { + lastWritten = LastWritten.KeywordOrIdentifier; + } + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertSpecialsDecorator.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertSpecialsDecorator.cs new file mode 100644 index 000000000..0fafdeef0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertSpecialsDecorator.cs @@ -0,0 +1,157 @@ +// Copyright (c) 2010-2013 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.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertSpecialsDecorator : DecoratingTokenWriter + { + readonly Stack positionStack = new Stack(); + int visitorWroteNewLine = 0; + + public InsertSpecialsDecorator(TokenWriter writer) : base(writer) + { + } + + public override void StartNode(AstNode node) + { + if (positionStack.Count > 0) { + WriteSpecialsUpToNode(node); + } + positionStack.Push(node.FirstChild); + base.StartNode(node); + } + + public override void EndNode(AstNode node) + { + base.EndNode(node); + AstNode pos = positionStack.Pop(); + Debug.Assert(pos == null || pos.Parent == node); + WriteSpecials(pos, null); + } + + public override void WriteKeyword(Role role, string keyword) + { + if (role != null) { + WriteSpecialsUpToRole(role); + } + base.WriteKeyword(role, keyword); + } + + public override void WriteIdentifier(Identifier identifier) + { + WriteSpecialsUpToRole(identifier.Role ?? Roles.Identifier); + base.WriteIdentifier(identifier); + } + + public override void WriteToken(Role role, string token) + { + WriteSpecialsUpToRole(role); + base.WriteToken(role, token); + } + + public override void NewLine() + { + if (visitorWroteNewLine >= 0) + base.NewLine(); + visitorWroteNewLine++; + } + + #region WriteSpecials + /// + /// Writes all specials from start to end (exclusive). Does not touch the positionStack. + /// + void WriteSpecials(AstNode start, AstNode end) + { + for (AstNode pos = start; pos != end; pos = pos.NextSibling) { + if (pos.Role == Roles.Comment) { + var node = (Comment)pos; + base.WriteComment(node.CommentType, node.Content); + } + // see CSharpOutputVisitor.VisitNewLine() + // if (pos.Role == Roles.NewLine) { + // if (visitorWroteNewLine <= 0) + // base.NewLine(); + // visitorWroteNewLine--; + // } + if (pos.Role == Roles.PreProcessorDirective) { + var node = (PreProcessorDirective)pos; + base.WritePreProcessorDirective(node.Type, node.Argument); + } + } + } + + /// + /// Writes all specials between the current position (in the positionStack) and the next + /// node with the specified role. Advances the current position. + /// + void WriteSpecialsUpToRole(Role role) + { + WriteSpecialsUpToRole(role, null); + } + + void WriteSpecialsUpToRole(Role role, AstNode nextNode) + { + if (positionStack.Count == 0) { + return; + } + // Look for the role between the current position and the nextNode. + for (AstNode pos = positionStack.Peek(); pos != null && pos != nextNode; pos = pos.NextSibling) { + if (pos.Role == role) { + WriteSpecials(positionStack.Pop(), pos); + // Push the next sibling because the node matching the role is not a special, + // and should be considered to be already handled. + positionStack.Push(pos.NextSibling); + // This is necessary for OptionalComma() to work correctly. + break; + } + } + } + + /// + /// Writes all specials between the current position (in the positionStack) and the specified node. + /// Advances the current position. + /// + void WriteSpecialsUpToNode(AstNode node) + { + if (positionStack.Count == 0) { + return; + } + for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) { + if (pos == node) { + WriteSpecials(positionStack.Pop(), pos); + // Push the next sibling because the node itself is not a special, + // and should be considered to be already handled. + positionStack.Push(pos.NextSibling); + // This is necessary for OptionalComma() to work correctly. + break; + } + } + } + #endregion + } +} + + + + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs new file mode 100644 index 000000000..277b1c3e9 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs @@ -0,0 +1,419 @@ +// Copyright (c) 2010-2013 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.Globalization; +using System.IO; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Writes C# code into a TextWriter. + /// + public class TextWriterTokenWriter : TokenWriter, ILocatable + { + readonly TextWriter textWriter; + int indentation; + bool needsIndent = true; + bool isAtStartOfLine = true; + int line, column; + + public int Indentation { + get { return this.indentation; } + set { this.indentation = value; } + } + + public TextLocation Location { + get { return new TextLocation(line, column + (needsIndent ? indentation * IndentationString.Length : 0)); } + } + + public string IndentationString { get; set; } + + public TextWriterTokenWriter(TextWriter textWriter) + { + if (textWriter == null) + throw new ArgumentNullException("textWriter"); + this.textWriter = textWriter; + this.IndentationString = "\t"; + this.line = 1; + this.column = 1; + } + + public override void WriteIdentifier(Identifier identifier) + { + WriteIndentation(); + if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { + textWriter.Write('@'); + column++; + } + textWriter.Write(identifier.Name); + column += identifier.Name.Length; + isAtStartOfLine = false; + } + + public override void WriteKeyword(Role role, string keyword) + { + WriteIndentation(); + column += keyword.Length; + textWriter.Write(keyword); + isAtStartOfLine = false; + } + + public override void WriteToken(Role role, string token) + { + WriteIndentation(); + column += token.Length; + textWriter.Write(token); + isAtStartOfLine = false; + } + + public override void Space() + { + WriteIndentation(); + column++; + textWriter.Write(' '); + } + + protected void WriteIndentation() + { + if (needsIndent) { + needsIndent = false; + for (int i = 0; i < indentation; i++) { + textWriter.Write(this.IndentationString); + } + column += indentation * IndentationString.Length; + } + } + + public override void NewLine() + { + textWriter.WriteLine(); + column = 1; + line++; + needsIndent = true; + isAtStartOfLine = true; + } + + public override void Indent() + { + indentation++; + } + + public override void Unindent() + { + indentation--; + } + + public override void WriteComment(CommentType commentType, string content) + { + WriteIndentation(); + switch (commentType) { + case CommentType.SingleLine: + textWriter.Write("//"); + textWriter.WriteLine(content); + column += 2 + content.Length; + needsIndent = true; + isAtStartOfLine = true; + break; + case CommentType.MultiLine: + textWriter.Write("/*"); + textWriter.Write(content); + textWriter.Write("*/"); + column += 2; + UpdateEndLocation(content, ref line, ref column); + column += 2; + isAtStartOfLine = false; + break; + case CommentType.Documentation: + textWriter.Write("///"); + textWriter.WriteLine(content); + column += 3 + content.Length; + needsIndent = true; + isAtStartOfLine = true; + break; + case CommentType.MultiLineDocumentation: + textWriter.Write("/**"); + textWriter.Write(content); + textWriter.Write("*/"); + column += 3; + UpdateEndLocation(content, ref line, ref column); + column += 2; + isAtStartOfLine = false; + break; + default: + textWriter.Write(content); + column += content.Length; + break; + } + } + + static void UpdateEndLocation(string content, ref int line, ref int column) + { + if (string.IsNullOrEmpty(content)) + return; + for (int i = 0; i < content.Length; i++) { + char ch = content[i]; + switch (ch) { + case '\r': + if (i + 1 < content.Length && content[i + 1] == '\n') + i++; + goto case '\n'; + case '\n': + line++; + column = 0; + break; + } + column++; + } + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + // pre-processor directive must start on its own line + if (!isAtStartOfLine) + NewLine(); + WriteIndentation(); + textWriter.Write('#'); + string directive = type.ToString().ToLowerInvariant(); + textWriter.Write(directive); + column += 1 + directive.Length; + if (!string.IsNullOrEmpty(argument)) { + textWriter.Write(' '); + textWriter.Write(argument); + column += 1 + argument.Length; + } + NewLine(); + } + + public static string PrintPrimitiveValue(object value) + { + TextWriter writer = new StringWriter(); + TextWriterTokenWriter tokenWriter = new TextWriterTokenWriter(writer); + tokenWriter.WritePrimitiveValue(value); + return writer.ToString(); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + if (literalValue != null) { + textWriter.Write(literalValue); + column += literalValue.Length; + return; + } + + if (value == null) { + // usually NullReferenceExpression should be used for this, but we'll handle it anyways + textWriter.Write("null"); + column += 4; + return; + } + + if (value is bool) { + if ((bool)value) { + textWriter.Write("true"); + column += 4; + } else { + textWriter.Write("false"); + column += 5; + } + return; + } + + if (value is string) { + string tmp = "\"" + ConvertString(value.ToString()) + "\""; + column += tmp.Length; + textWriter.Write(tmp); + } else if (value is char) { + string tmp = "'" + ConvertCharLiteral((char)value) + "'"; + column += tmp.Length; + textWriter.Write(tmp); + } else if (value is decimal) { + string str = ((decimal)value).ToString(NumberFormatInfo.InvariantInfo) + "m"; + column += str.Length; + textWriter.Write(str); + } else if (value is float) { + float f = (float)value; + if (float.IsInfinity(f) || float.IsNaN(f)) { + // Strictly speaking, these aren't PrimitiveExpressions; + // but we still support writing these to make life easier for code generators. + textWriter.Write("float"); + column += 5; + WriteToken(Roles.Dot, "."); + if (float.IsPositiveInfinity(f)) { + textWriter.Write("PositiveInfinity"); + column += "PositiveInfinity".Length; + } else if (float.IsNegativeInfinity(f)) { + textWriter.Write("NegativeInfinity"); + column += "NegativeInfinity".Length; + } else { + textWriter.Write("NaN"); + column += 3; + } + return; + } + if (f == 0 && 1 / f == float.NegativeInfinity) { + // negative zero is a special case + // (again, not a primitive expression, but it's better to handle + // the special case here than to do it in all code generators) + textWriter.Write("-"); + column++; + } + var str = f.ToString("R", NumberFormatInfo.InvariantInfo) + "f"; + column += str.Length; + textWriter.Write(str); + } else if (value is double) { + double f = (double)value; + if (double.IsInfinity(f) || double.IsNaN(f)) { + // Strictly speaking, these aren't PrimitiveExpressions; + // but we still support writing these to make life easier for code generators. + textWriter.Write("double"); + column += 6; + WriteToken(Roles.Dot, "."); + if (double.IsPositiveInfinity(f)) { + textWriter.Write("PositiveInfinity"); + column += "PositiveInfinity".Length; + } else if (double.IsNegativeInfinity(f)) { + textWriter.Write("NegativeInfinity"); + column += "NegativeInfinity".Length; + } else { + textWriter.Write("NaN"); + column += 3; + } + return; + } + if (f == 0 && 1 / f == double.NegativeInfinity) { + // negative zero is a special case + // (again, not a primitive expression, but it's better to handle + // the special case here than to do it in all code generators) + textWriter.Write("-"); + } + string number = f.ToString("R", NumberFormatInfo.InvariantInfo); + if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) { + number += ".0"; + } + textWriter.Write(number); + } else if (value is IFormattable) { + StringBuilder b = new StringBuilder (); +// if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) { +// b.Append("0x"); +// b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo)); +// } else { + b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo)); +// } + if (value is uint || value is ulong) { + b.Append("u"); + } + if (value is long || value is ulong) { + b.Append("L"); + } + textWriter.Write(b.ToString()); + column += b.Length; + } else { + textWriter.Write(value.ToString()); + column += value.ToString().Length; + } + } + + /// + /// Gets the escape sequence for the specified character within a char literal. + /// Does not include the single quotes surrounding the char literal. + /// + public static string ConvertCharLiteral(char ch) + { + if (ch == '\'') { + return "\\'"; + } + return ConvertChar(ch); + } + + /// + /// Gets the escape sequence for the specified character. + /// + /// This method does not convert ' or ". + static string ConvertChar(char ch) + { + switch (ch) { + case '\\': + return "\\\\"; + case '\0': + return "\\0"; + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\f': + return "\\f"; + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\t': + return "\\t"; + case '\v': + return "\\v"; + default: + if (char.IsControl(ch) || char.IsSurrogate(ch) || + // print all uncommon white spaces as numbers + (char.IsWhiteSpace(ch) && ch != ' ')) { + return "\\u" + ((int)ch).ToString("x4"); + } else { + return ch.ToString(); + } + } + } + + /// + /// Converts special characters to escape sequences within the given string. + /// + public static string ConvertString(string str) + { + StringBuilder sb = new StringBuilder (); + foreach (char ch in str) { + if (ch == '"') { + sb.Append("\\\""); + } else { + sb.Append(ConvertChar(ch)); + } + } + return sb.ToString(); + } + + public override void WritePrimitiveType(string type) + { + textWriter.Write(type); + column += type.Length; + if (type == "new") { + textWriter.Write("()"); + column += 2; + } + } + + public override void StartNode(AstNode node) + { + // Write out the indentation, so that overrides of this method + // can rely use the current output length to identify the position of the node + // in the output. + WriteIndentation(); + } + + public override void EndNode(AstNode node) + { + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs new file mode 100644 index 000000000..158b86ce3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -0,0 +1,4096 @@ +// +// CSharpParser.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// 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.Linq; +using System.Collections.Generic; +using System.IO; +using ICSharpCode.NRefactory.Editor; +using Mono.CSharp; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class CSharpParser + { + CompilerSettings compilerSettings; + + class ConversionVisitor : StructuralVisitor + { + SyntaxTree unit = new SyntaxTree(); + internal bool convertTypeSystemMode; + + public SyntaxTree Unit { + get { + return unit; + } + set { + unit = value; + } + } + + public LocationsBag LocationsBag { + get; + private set; + } + + public ConversionVisitor(bool convertTypeSystemMode, LocationsBag locationsBag) + { + this.convertTypeSystemMode = convertTypeSystemMode; + this.LocationsBag = locationsBag; + } + + public static TextLocation Convert(Location loc) + { + return new TextLocation(loc.Row, loc.Column); + } + + public override void Visit(ModuleContainer mc) + { + bool first = true; + foreach (var container in mc.Containers) { + var nspace = container as NamespaceContainer; + if (nspace == null) { + container.Accept(this); + continue; + } + NamespaceDeclaration nDecl = null; + var loc = LocationsBag.GetLocations(nspace); + + if (nspace.NS != null && !string.IsNullOrEmpty(nspace.NS.Name)) { + nDecl = new NamespaceDeclaration(); + if (loc != null) { + nDecl.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.NamespaceKeyword), Roles.NamespaceKeyword); + } + nDecl.AddChild(ConvertNamespaceName(nspace.RealMemberName), NamespaceDeclaration.NamespaceNameRole); + if (loc != null && loc.Count > 1) { + nDecl.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.LBrace), Roles.LBrace); + } + AddToNamespace(nDecl); + namespaceStack.Push(nDecl); + } + + if (nspace.Usings != null) { + foreach (var us in nspace.Usings) { + us.Accept(this); + } + } + + if (first) { + first = false; + if (mc.OptAttributes != null) { + foreach (var attr in mc.OptAttributes.Sections) { + var section = ConvertAttributeSection(attr); + if (section != null) + unit.AddChild(section, SyntaxTree.MemberRole); + } + } + } + + if (nspace.Containers != null) { + foreach (var subContainer in nspace.Containers) { + subContainer.Accept(this); + } + } + if (nDecl != null) { + AddAttributeSection(nDecl, nspace.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + if (loc != null && loc.Count > 2) + nDecl.AddChild(new CSharpTokenNode(Convert(loc [2]), Roles.RBrace), Roles.RBrace); + if (loc != null && loc.Count > 3) + nDecl.AddChild(new CSharpTokenNode(Convert(loc [3]), Roles.Semicolon), Roles.Semicolon); + + namespaceStack.Pop(); + } else { + AddAttributeSection(unit, nspace.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + } + } + AddAttributeSection(unit, mc.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + } + + #region Global + + readonly Stack namespaceStack = new Stack(); + + void AddTypeArguments(ATypeNameExpression texpr, AstType result) + { + var unbound = texpr.TypeArguments as UnboundTypeArguments; + if (unbound != null) { + var loc2 = LocationsBag.GetLocations(texpr.TypeArguments); + if (loc2 == null) + return; + int j = 0; + if (j < loc2.Count) + result.AddChild(new CSharpTokenNode(Convert(loc2 [j++]), Roles.LChevron), Roles.LChevron); + while (j < loc2.Count - 1) { + result.AddChild (new SimpleType (), Roles.TypeArgument); + result.AddChild(new CSharpTokenNode(Convert(loc2 [j++]), Roles.LChevron), Roles.Comma); + } + if (j < loc2.Count) { + result.AddChild (new SimpleType (), Roles.TypeArgument); + result.AddChild(new CSharpTokenNode(Convert(loc2 [j++]), Roles.RChevron), Roles.RChevron); + } + return; + } + if (texpr.TypeArguments == null || texpr.TypeArguments.Args == null) + return; + var loc = LocationsBag.GetLocations(texpr.TypeArguments); + if (loc != null && loc.Count >= 2) + result.AddChild(new CSharpTokenNode(Convert(loc [loc.Count - 2]), Roles.LChevron), Roles.LChevron); + int i = 0; + foreach (var arg in texpr.TypeArguments.Args) { + result.AddChild(ConvertToType(arg), Roles.TypeArgument); + if (loc != null && i < loc.Count - 2) + result.AddChild(new CSharpTokenNode(Convert(loc [i++]), Roles.Comma), Roles.Comma); + } + if (loc != null && loc.Count >= 2) + result.AddChild(new CSharpTokenNode(Convert(loc [loc.Count - 1]), Roles.RChevron), Roles.RChevron); + } + + static AstType ConvertToType(TypeParameter spec) + { + AstType result; + result = new SimpleType { IdentifierToken = Identifier.Create(spec.Name, Convert(spec.Location)) }; + return result; + } + + AstType ConvertToType(MemberName memberName) + { + AstType result; + if (memberName.Left != null) { + result = new MemberType(); + result.AddChild(ConvertToType(memberName.Left), MemberType.TargetRole); + var loc = LocationsBag.GetLocations(memberName); + if (loc != null) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Dot), Roles.Dot); + result.AddChild(Identifier.Create(memberName.Name, Convert(memberName.Location)), Roles.Identifier); + } else { + result = new SimpleType { IdentifierToken = Identifier.Create(memberName.Name, Convert(memberName.Location)) }; + } + if (memberName.TypeParameters != null) { + var chevronLocs = LocationsBag.GetLocations(memberName.TypeParameters); + if (chevronLocs != null) + result.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 2]), Roles.LChevron), Roles.LChevron); + for (int i = 0; i < memberName.TypeParameters.Count; i++) { + var param = memberName.TypeParameters [i]; + result.AddChild(new SimpleType(Identifier.Create(param.Name, Convert(param.Location))), Roles.TypeArgument); + if (chevronLocs != null && i < chevronLocs.Count - 2) + result.AddChild(new CSharpTokenNode(Convert(chevronLocs [i]), Roles.Comma), Roles.Comma); + } + if (chevronLocs != null) + result.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 1]), Roles.RChevron), Roles.RChevron); + } + return result; + } + + AstType ConvertToType(Mono.CSharp.Expression typeName) + { + if (typeName == null) // may happen in typeof(Generic<,,,,>) + return new SimpleType(); + + var typeExpr = typeName as TypeExpression; + if (typeExpr != null) { + return new PrimitiveType(typeExpr.GetSignatureForError(), Convert(typeExpr.Location)); + } + + var qam = typeName as QualifiedAliasMember; + if (qam != null) { + var loc = LocationsBag.GetLocations(typeName); + var memberType = new MemberType(); + memberType.Target = new SimpleType(qam.alias, Convert(qam.Location)); + memberType.IsDoubleColon = true; + + if (loc != null && loc.Count > 0) + memberType.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.DoubleColon), Roles.DoubleColon); + + memberType.MemberNameToken = Identifier.Create(qam.Name, loc != null ? Convert(loc [1]) : TextLocation.Empty); + AddTypeArguments(qam, memberType); + return memberType; + } + + var ma = typeName as MemberAccess; + if (ma != null) { + var memberType = new MemberType(); + memberType.AddChild(ConvertToType(ma.LeftExpression), MemberType.TargetRole); + var loc = LocationsBag.GetLocations(ma); + if (loc != null) + memberType.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Dot), Roles.Dot); + + memberType.MemberNameToken = Identifier.Create(ma.Name, Convert(ma.Location)); + + AddTypeArguments(ma, memberType); + return memberType; + } + + var sn = typeName as SimpleName; + if (sn != null) { + var result = new SimpleType(sn.Name, Convert(sn.Location)); + AddTypeArguments(sn, result); + return result; + } + + var cc = typeName as ComposedCast; + if (cc != null) { + var baseType = ConvertToType(cc.Left); + var result = new ComposedType { BaseType = baseType }; + var ccSpec = cc.Spec; + while (ccSpec != null) { + if (ccSpec.IsNullable) { + result.AddChild(new CSharpTokenNode(Convert(ccSpec.Location), ComposedType.NullableRole), ComposedType.NullableRole); + } else if (ccSpec.IsPointer) { + result.AddChild(new CSharpTokenNode(Convert(ccSpec.Location), ComposedType.PointerRole), ComposedType.PointerRole); + } else { + var location = LocationsBag.GetLocations(ccSpec); + var spec = new ArraySpecifier { Dimensions = ccSpec.Dimension }; + spec.AddChild(new CSharpTokenNode(Convert(ccSpec.Location), Roles.LBracket), Roles.LBracket); + if (location != null) + spec.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.RBracket), Roles.RBracket); + + result.ArraySpecifiers.Add(spec); + } + ccSpec = ccSpec.Next; + } + return result; + } + + var sce = typeName as SpecialContraintExpr; + if (sce != null) { + switch (sce.Constraint) { + case SpecialConstraint.Class: + return new PrimitiveType("class", Convert(sce.Location)); + case SpecialConstraint.Struct: + return new PrimitiveType("struct", Convert(sce.Location)); + case SpecialConstraint.Constructor: + return new PrimitiveType("new", Convert(sce.Location)); + } + } + return new SimpleType("unknown"); + } + + IEnumerable GetAttributes(IEnumerable optAttributes) + { + if (optAttributes == null) + yield break; + foreach (var attr in optAttributes) { + var result = new Attribute(); + result.Type = ConvertToType(attr.TypeNameExpression); + var loc = LocationsBag.GetLocations(attr); + result.HasArgumentList = loc != null; + int pos = 0; + if (loc != null) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.LPar), Roles.LPar); + + if (attr.PositionalArguments != null) { + foreach (var arg in attr.PositionalArguments) { + if (arg == null) + continue; + var na = arg as NamedArgument; + if (na != null) { + var newArg = new NamedArgumentExpression(); + newArg.AddChild(Identifier.Create(na.Name, Convert(na.Location)), Roles.Identifier); + + var argLoc = LocationsBag.GetLocations(na); + if (argLoc != null) + newArg.AddChild(new CSharpTokenNode(Convert(argLoc [0]), Roles.Colon), Roles.Colon); + if (na.Expr != null) + newArg.AddChild((Expression)na.Expr.Accept(this), Roles.Expression); + result.AddChild(newArg, Roles.Argument); + } else { + if (arg.Expr != null) + result.AddChild((Expression)arg.Expr.Accept(this), Roles.Argument); + } + if (loc != null && pos + 1 < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Comma), Roles.Comma); + } + } + if (attr.NamedArguments != null) { + foreach (var arg in attr.NamedArguments) { + var na = (NamedArgument)arg; + var newArg = new NamedExpression(); + newArg.AddChild(Identifier.Create(na.Name, Convert(na.Location)), Roles.Identifier); + + var argLoc = LocationsBag.GetLocations(na); + if (argLoc != null) + newArg.AddChild(new CSharpTokenNode(Convert(argLoc [0]), Roles.Assign), Roles.Assign); + if (na.Expr != null) + newArg.AddChild((Expression)na.Expr.Accept(this), Roles.Expression); + result.AddChild(newArg, Roles.Argument); + if (loc != null && pos + 1 < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Comma), Roles.Comma); + } + } + if (loc != null && pos < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.RPar), Roles.RPar); + + yield return result; + } + } + + AttributeSection ConvertAttributeSection(IEnumerable optAttributes) + { + if (optAttributes == null) + return null; + var result = new AttributeSection(); + var loc = LocationsBag.GetLocations(optAttributes); + int pos = 0; + if (loc != null) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.LBracket), Roles.LBracket); + var first = optAttributes.FirstOrDefault(); + string target = first != null ? first.ExplicitTarget : null; + + if (!string.IsNullOrEmpty(target)) { + if (loc != null && pos < loc.Count - 1) { + result.AddChild(Identifier.Create(target, Convert(loc [pos++])), Roles.Identifier); + } else { + result.AddChild(Identifier.Create(target), Roles.Identifier); + } + if (loc != null && pos < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Colon), Roles.Colon); + } + + int attributeCount = 0; + foreach (var attr in GetAttributes (optAttributes)) { + result.AddChild(attr, Roles.Attribute); + if (loc != null && pos + 1 < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Comma), Roles.Comma); + + attributeCount++; + } + if (attributeCount == 0) + return null; + // Left and right bracket + commas between the attributes + int locCount = 2 + attributeCount - 1; + // optional comma + if (loc != null && pos < loc.Count - 1 && loc.Count == locCount + 1) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Comma), Roles.Comma); + if (loc != null && pos < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.RBracket), Roles.RBracket); + return result; + } + + public override void Visit(NamespaceContainer ns) + { + NamespaceDeclaration nDecl = null; + var loc = LocationsBag.GetLocations(ns); + // is caused by the parser - see Bug 12383 - [AST] Non existing namespaces generated + if (ns.NS != null && !string.IsNullOrEmpty(ns.NS.Name) && !ns.NS.Name.EndsWith("", StringComparison.Ordinal)) { + nDecl = new NamespaceDeclaration(); + if (loc != null) { + nDecl.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.NamespaceKeyword), Roles.NamespaceKeyword); + } + nDecl.AddChild(ConvertNamespaceName(ns.RealMemberName), NamespaceDeclaration.NamespaceNameRole); + if (loc != null && loc.Count > 1) { + nDecl.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.LBrace), Roles.LBrace); + } + + AddToNamespace(nDecl); + namespaceStack.Push(nDecl); + } + + if (ns.Usings != null) { + foreach (var us in ns.Usings) { + us.Accept(this); + } + } + + if (ns.Containers != null) { + foreach (var container in ns.Containers) { + container.Accept(this); + } + } + + if (nDecl != null) { + AddAttributeSection(nDecl, ns.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + if (loc != null && loc.Count > 2) + nDecl.AddChild(new CSharpTokenNode(Convert(loc [2]), Roles.RBrace), Roles.RBrace); + if (loc != null && loc.Count > 3) + nDecl.AddChild(new CSharpTokenNode(Convert(loc [3]), Roles.Semicolon), Roles.Semicolon); + + namespaceStack.Pop(); + } + } + // public override void Visit (UsingsBag.Namespace nspace) + // { + // + // + // VisitNamespaceUsings (nspace); + // VisitNamespaceBody (nspace); + // + // } + // + AstType ConvertNamespaceName(MemberName memberName) + { + // HACK for a parser 'bug' - sometimes it generates "" identifiers in namespace names (on certain bugs in the input file) + if (memberName.Name == "") + return AstType.Null; + return ConvertToType(memberName); + } + + public override void Visit(UsingNamespace un) + { + var ud = new UsingDeclaration(); + var loc = LocationsBag.GetLocations(un); + ud.AddChild(new CSharpTokenNode(Convert(un.Location), UsingDeclaration.UsingKeywordRole), UsingDeclaration.UsingKeywordRole); + if (un.NamespaceExpression != null) + ud.AddChild(ConvertToType(un.NamespaceExpression), UsingDeclaration.ImportRole); + if (loc != null) + ud.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Semicolon), Roles.Semicolon); + AddToNamespace(ud); + } + + public override void Visit(UsingAliasNamespace uan) + { + var ud = new UsingAliasDeclaration(); + var loc = LocationsBag.GetLocations(uan); + + ud.AddChild(new CSharpTokenNode(Convert(uan.Location), UsingAliasDeclaration.UsingKeywordRole), UsingAliasDeclaration.UsingKeywordRole); + ud.AddChild(Identifier.Create(uan.Alias.Value, Convert(uan.Alias.Location)), UsingAliasDeclaration.AliasRole); + if (loc != null) + ud.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Assign), Roles.Assign); + if (uan.NamespaceExpression != null) + ud.AddChild(ConvertToType(uan.NamespaceExpression), UsingAliasDeclaration.ImportRole); + if (loc != null && loc.Count > 1) + ud.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Semicolon), Roles.Semicolon); + AddToNamespace(ud); + } + + public override void Visit(UsingExternAlias uea) + { + var ud = new ExternAliasDeclaration(); + var loc = LocationsBag.GetLocations(uea); + ud.AddChild(new CSharpTokenNode(Convert(uea.Location), Roles.ExternKeyword), Roles.ExternKeyword); + if (loc != null) + ud.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.AliasKeyword), Roles.AliasKeyword); + ud.AddChild(Identifier.Create(uea.Alias.Value, Convert(uea.Alias.Location)), Roles.Identifier); + if (loc != null && loc.Count > 1) + ud.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Semicolon), Roles.Semicolon); + AddToNamespace(ud); + } + + AstType ConvertImport(MemberName memberName) + { + if (memberName.Left != null) { + // left.name + var t = new MemberType(); +// t.IsDoubleColon = memberName.IsDoubleColon; + t.AddChild(ConvertImport(memberName.Left), MemberType.TargetRole); + var loc = LocationsBag.GetLocations(memberName); + if (loc != null) + t.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Dot), Roles.Dot); + + t.AddChild(Identifier.Create(memberName.Name, Convert(memberName.Location)), Roles.Identifier); + AddTypeArguments(t, memberName); + return t; + } else { + var t = new SimpleType(); + t.AddChild(Identifier.Create(memberName.Name, Convert(memberName.Location)), Roles.Identifier); + AddTypeArguments(t, memberName); + return t; + } + } + + public override void Visit(MemberCore member) + { + Console.WriteLine("Unknown member:"); + Console.WriteLine(member.GetType() + "-> Member {0}", member.GetSignatureForError()); + } + + readonly Stack typeStack = new Stack(); + + public override void Visit(Class c) + { + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Class; + AddAttributeSection(newType, c); + var location = LocationsBag.GetMemberLocation(c); + AddModifiers(newType, location); + int curLoc = 0; + if (location != null && location.Count > 0) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.ClassKeyword), Roles.ClassKeyword); + + newType.AddChild(Identifier.Create(c.MemberName.Name, Convert(c.MemberName.Location)), Roles.Identifier); + AddTypeParameters(newType, c.MemberName); + + if (c.TypeBaseExpressions != null) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Colon), Roles.Colon); + + var commaLocations = LocationsBag.GetLocations(c.TypeBaseExpressions); + int i = 0; + foreach (var baseTypes in c.TypeBaseExpressions) { + newType.AddChild(ConvertToType(baseTypes), Roles.BaseType); + if (commaLocations != null && i < commaLocations.Count) { + newType.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + i++; + } + } + } + + AddConstraints(newType, c.CurrentTypeParameters); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.LBrace), Roles.LBrace); + typeStack.Push(newType); + base.Visit(c); + AddAttributeSection(newType, c.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + + if (location != null && curLoc < location.Count) { + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.RBrace), Roles.RBrace); + + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Semicolon), Roles.Semicolon); + + } else { + // parser error, set end node to max value. + newType.AddChild(new ErrorNode(), Roles.Error); + } + typeStack.Pop(); + AddType(newType); + } + + public override void Visit(Struct s) + { + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Struct; + AddAttributeSection(newType, s); + var location = LocationsBag.GetMemberLocation(s); + AddModifiers(newType, location); + int curLoc = 0; + if (location != null && location.Count > 0) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.StructKeyword), Roles.StructKeyword); + newType.AddChild(Identifier.Create(s.MemberName.Name, Convert(s.MemberName.Location)), Roles.Identifier); + AddTypeParameters(newType, s.MemberName); + + if (s.TypeBaseExpressions != null) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Colon), Roles.Colon); + var commaLocations = LocationsBag.GetLocations(s.TypeBaseExpressions); + int i = 0; + foreach (var baseTypes in s.TypeBaseExpressions) { + newType.AddChild(ConvertToType(baseTypes), Roles.BaseType); + if (commaLocations != null && i < commaLocations.Count) { + newType.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + i++; + } + } + } + + AddConstraints(newType, s.CurrentTypeParameters); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.LBrace), Roles.LBrace); + typeStack.Push(newType); + base.Visit(s); + if (location != null && location.Count > 2) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.RBrace), Roles.RBrace); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Semicolon), Roles.Semicolon); + } else { + // parser error, set end node to max value. + newType.AddChild(new ErrorNode(), Roles.Error); + } + typeStack.Pop(); + AddType(newType); + } + + public override void Visit(Interface i) + { + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Interface; + AddAttributeSection(newType, i); + var location = LocationsBag.GetMemberLocation(i); + AddModifiers(newType, location); + int curLoc = 0; + if (location != null && location.Count > 0) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.InterfaceKeyword), Roles.InterfaceKeyword); + newType.AddChild(Identifier.Create(i.MemberName.Name, Convert(i.MemberName.Location)), Roles.Identifier); + AddTypeParameters(newType, i.MemberName); + + if (i.TypeBaseExpressions != null) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Colon), Roles.Colon); + var commaLocations = LocationsBag.GetLocations(i.TypeBaseExpressions); + int j = 0; + foreach (var baseTypes in i.TypeBaseExpressions) { + newType.AddChild(ConvertToType(baseTypes), Roles.BaseType); + if (commaLocations != null && j < commaLocations.Count) { + newType.AddChild(new CSharpTokenNode(Convert(commaLocations [j]), Roles.Comma), Roles.Comma); + j++; + } + } + } + + AddConstraints(newType, i.CurrentTypeParameters); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.LBrace), Roles.LBrace); + typeStack.Push(newType); + base.Visit(i); + if (location != null && location.Count > 2) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.RBrace), Roles.RBrace); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Semicolon), Roles.Semicolon); + } else { + // parser error, set end node to max value. + newType.AddChild(new ErrorNode(), Roles.Error); + } + typeStack.Pop(); + AddType(newType); + } + + public override void Visit(Mono.CSharp.Delegate d) + { + var newDelegate = new DelegateDeclaration(); + var location = LocationsBag.GetMemberLocation(d); + AddAttributeSection(newDelegate, d); + AddModifiers(newDelegate, location); + if (location != null && location.Count > 0) { + newDelegate.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.DelegateKeyword), Roles.DelegateKeyword); + } + if (d.ReturnType != null) + newDelegate.AddChild(ConvertToType(d.ReturnType), Roles.Type); + newDelegate.AddChild(Identifier.Create(d.MemberName.Name, Convert(d.MemberName.Location)), Roles.Identifier); + AddTypeParameters(newDelegate, d.MemberName); + + if (location != null && location.Count > 1) + newDelegate.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LPar), Roles.LPar); + AddParameter(newDelegate, d.Parameters); + + if (location != null && location.Count > 2) { + newDelegate.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RPar), Roles.RPar); + } + AddConstraints(newDelegate, d.CurrentTypeParameters); + if (location != null && location.Count > 3) { + newDelegate.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.Semicolon), Roles.Semicolon); + } + AddType(newDelegate); + } + + void AddType(EntityDeclaration child) + { + if (typeStack.Count > 0) { + typeStack.Peek().AddChild(child, Roles.TypeMemberRole); + } else { + AddToNamespace(child); + } + } + + void AddToNamespace(AstNode child) + { + if (namespaceStack.Count > 0) { + namespaceStack.Peek().AddChild(child, NamespaceDeclaration.MemberRole); + } else { + unit.AddChild(child, SyntaxTree.MemberRole); + } + } + + public override void Visit(Mono.CSharp.Enum e) + { + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Enum; + AddAttributeSection(newType, e); + var location = LocationsBag.GetMemberLocation(e); + + AddModifiers(newType, location); + int curLoc = 0; + if (location != null && location.Count > 0) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.EnumKeyword), Roles.EnumKeyword); + newType.AddChild(Identifier.Create(e.MemberName.Name, Convert(e.MemberName.Location)), Roles.Identifier); + + if (e.BaseTypeExpression != null) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Colon), Roles.Colon); + newType.AddChild(ConvertToType(e.BaseTypeExpression), Roles.BaseType); + } + + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.LBrace), Roles.LBrace); + typeStack.Push(newType); + + foreach (var m in e.Members) { + var member = m as EnumMember; + if (member == null) { + Console.WriteLine("WARNING - ENUM MEMBER: " + m); + continue; + } + Visit(member); + if (location != null && curLoc < location.Count - 1) //last one is closing brace + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Comma), Roles.Comma); + } + + if (location != null && location.Count > 2) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.RBrace), Roles.RBrace); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Semicolon), Roles.Semicolon); + } else { + // parser error, set end node to max value. + newType.AddChild(new ErrorNode(), Roles.Error); + } + + AddAttributeSection(newType, e.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + typeStack.Pop(); + AddType(newType); + } + + public override void Visit(EnumMember em) + { + var newField = new EnumMemberDeclaration(); + AddAttributeSection(newField, em); + newField.AddChild(Identifier.Create(em.Name, Convert(em.Location)), Roles.Identifier); + if (em.Initializer != null) { + newField.AddChild(new CSharpTokenNode(Convert(em.Initializer.Location), Roles.Assign), Roles.Assign); + newField.AddChild((Expression)em.Initializer.Accept(this), EnumMemberDeclaration.InitializerRole); + } + //Console.WriteLine (newField.StartLocation +"-" + newField.EndLocation); + + typeStack.Peek().AddChild(newField, Roles.TypeMemberRole); + } + + #endregion + + #region Type members + + public override void Visit(FixedField f) + { + var location = LocationsBag.GetMemberLocation(f); + int locationIdx = 0; + + var newField = new FixedFieldDeclaration(); + AddAttributeSection(newField, f); + AddModifiers(newField, location); + if (location != null && location.Count > 0) + newField.AddChild(new CSharpTokenNode(Convert(location [locationIdx++]), FixedFieldDeclaration.FixedKeywordRole), FixedFieldDeclaration.FixedKeywordRole); + + if (f.TypeExpression != null) + newField.AddChild(ConvertToType(f.TypeExpression), Roles.Type); + + var variable = new FixedVariableInitializer(); + variable.AddChild(Identifier.Create(f.MemberName.Name, Convert(f.MemberName.Location)), Roles.Identifier); + if (f.Initializer != null && !f.Initializer.IsNull) { + variable.AddChild(new CSharpTokenNode(Convert(f.Initializer.Location), Roles.LBracket), Roles.LBracket); + + variable.AddChild((Expression)f.Initializer.Accept(this), Roles.Expression); + var bracketLocations = LocationsBag.GetLocations(f.Initializer); + if (bracketLocations != null) + variable.AddChild(new CSharpTokenNode(Convert(bracketLocations [0]), Roles.RBracket), Roles.RBracket); + } + newField.AddChild(variable, FixedFieldDeclaration.VariableRole); + + if (f.Declarators != null) { + foreach (var decl in f.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + if (declLoc != null) + newField.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + + variable = new FixedVariableInitializer(); + variable.AddChild(Identifier.Create(decl.Name.Value, Convert(decl.Name.Location)), Roles.Identifier); + variable.AddChild(new CSharpTokenNode(Convert(decl.Initializer.Location), Roles.LBracket), Roles.LBracket); + variable.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + var bracketLocations = LocationsBag.GetLocations(decl.Initializer); + if (bracketLocations != null) + variable.AddChild(new CSharpTokenNode(Convert(bracketLocations [0]), Roles.RBracket), Roles.RBracket); + + newField.AddChild(variable, FixedFieldDeclaration.VariableRole); + } + } + if (location != null && location.Count > locationIdx) + newField.AddChild(new CSharpTokenNode(Convert(location [locationIdx]), Roles.Semicolon), Roles.Semicolon); + typeStack.Peek().AddChild(newField, Roles.TypeMemberRole); + + } + + public override void Visit(Field f) + { + var location = LocationsBag.GetMemberLocation(f); + + var newField = new FieldDeclaration(); + AddAttributeSection(newField, f); + AddModifiers(newField, location); + newField.AddChild(ConvertToType(f.TypeExpression), Roles.Type); + + var variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(f.MemberName.Name, Convert(f.MemberName.Location)), Roles.Identifier); + int locationIdx = 0; + if (f.Initializer != null) { + if (location != null) + variable.AddChild(new CSharpTokenNode(Convert(location [locationIdx++]), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)f.Initializer.Accept(this), Roles.Expression); + } + newField.AddChild(variable, Roles.Variable); + if (f.Declarators != null) { + foreach (var decl in f.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + if (declLoc != null) + newField.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + + variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(decl.Name.Value, Convert(decl.Name.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (declLoc != null) + variable.AddChild(new CSharpTokenNode(Convert(declLoc [1]), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + newField.AddChild(variable, Roles.Variable); + } + } + if (location != null && location.Count > locationIdx) + newField.AddChild(new CSharpTokenNode(Convert(location [locationIdx++]), Roles.Semicolon), Roles.Semicolon); + + typeStack.Peek().AddChild(newField, Roles.TypeMemberRole); + } + + public override void Visit(Const c) + { + var location = LocationsBag.GetMemberLocation(c); + + var newField = new FieldDeclaration(); + AddAttributeSection(newField, c); + AddModifiers(newField, location); + if (location != null) + newField.AddChild(new CSharpModifierToken(Convert(location [0]), Modifiers.Const), EntityDeclaration.ModifierRole); + newField.AddChild(ConvertToType(c.TypeExpression), Roles.Type); + + var variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(c.MemberName.Name, Convert(c.MemberName.Location)), Roles.Identifier); + + if (c.Initializer != null) { + variable.AddChild(new CSharpTokenNode(Convert(c.Initializer.Location), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)c.Initializer.Accept(this), Roles.Expression); + } + newField.AddChild(variable, Roles.Variable); + if (c.Declarators != null) { + foreach (var decl in c.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + if (declLoc != null) + newField.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + + variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(decl.Name.Value, Convert(decl.Name.Location)), Roles.Identifier); + if (decl.Initializer != null) { + variable.AddChild(new CSharpTokenNode(Convert(decl.Initializer.Location), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + newField.AddChild(variable, Roles.Variable); + } + } + if (location != null) + newField.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + + typeStack.Peek().AddChild(newField, Roles.TypeMemberRole); + + + } + + public override void Visit(Operator o) + { + var newOperator = new OperatorDeclaration(); + newOperator.OperatorType = (OperatorType)o.OperatorType; + + var location = LocationsBag.GetMemberLocation(o); + AddAttributeSection(newOperator, o); + AddModifiers(newOperator, location); + + + if (o.OperatorType == Operator.OpType.Implicit) { + if (location != null && location.Count > 0) { + newOperator.AddChild(new CSharpTokenNode(Convert(location [0]), OperatorDeclaration.ImplicitRole), OperatorDeclaration.ImplicitRole); + if (location.Count > 1) + newOperator.AddChild(new CSharpTokenNode(Convert(location [1]), OperatorDeclaration.OperatorKeywordRole), OperatorDeclaration.OperatorKeywordRole); + } + newOperator.AddChild(ConvertToType(o.TypeExpression), Roles.Type); + } else if (o.OperatorType == Operator.OpType.Explicit) { + if (location != null && location.Count > 0) { + newOperator.AddChild(new CSharpTokenNode(Convert(location [0]), OperatorDeclaration.ExplicitRole), OperatorDeclaration.ExplicitRole); + if (location.Count > 1) + newOperator.AddChild(new CSharpTokenNode(Convert(location [1]), OperatorDeclaration.OperatorKeywordRole), OperatorDeclaration.OperatorKeywordRole); + } + newOperator.AddChild(ConvertToType(o.TypeExpression), Roles.Type); + } else { + newOperator.AddChild(ConvertToType(o.TypeExpression), Roles.Type); + + if (location != null && location.Count > 0) + newOperator.AddChild(new CSharpTokenNode(Convert(location [0]), OperatorDeclaration.OperatorKeywordRole), OperatorDeclaration.OperatorKeywordRole); + + if (location != null && location.Count > 1) { + var r = OperatorDeclaration.GetRole(newOperator.OperatorType); + newOperator.AddChild(new CSharpTokenNode(Convert(location [1]), r), r); + } + } + if (location != null && location.Count > 2) + newOperator.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.LPar), Roles.LPar); + AddParameter(newOperator, o.ParameterInfo); + if (location != null && location.Count > 3) + newOperator.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.RPar), Roles.RPar); + + if (o.Block != null) { + newOperator.AddChild((BlockStatement)o.Block.Accept(this), Roles.Body); + } else { + if (location != null && location.Count >= 5) + newOperator.AddChild(new CSharpTokenNode(Convert(location [4]), Roles.Semicolon), Roles.Semicolon); + } + typeStack.Peek().AddChild(newOperator, Roles.TypeMemberRole); + } + + public void AddAttributeSection(AstNode parent, Attributable a) + { + if (a == null || a.OptAttributes == null) + return; + AddAttributeSection(parent, a.OptAttributes); + } + + public void AddAttributeSection(AstNode parent, Attributes attrs, Role role) + { + if (attrs == null) + return; + foreach (var attr in attrs.Sections) { + var section = ConvertAttributeSection(attr); + if (section == null) + continue; + parent.AddChild(section, role); + } + } + + public void AddAttributeSection(AstNode parent, Attributes attrs) + { + AddAttributeSection(parent, attrs, EntityDeclaration.AttributeRole); + } + + public override void Visit(Indexer i) + { + var newIndexer = new IndexerDeclaration(); + AddAttributeSection(newIndexer, i); + var location = LocationsBag.GetMemberLocation(i); + AddModifiers(newIndexer, location); + newIndexer.AddChild(ConvertToType(i.TypeExpression), Roles.Type); + AddExplicitInterface(newIndexer, i.MemberName); + var name = i.MemberName; + newIndexer.AddChild(new CSharpTokenNode(Convert(name.Location), IndexerDeclaration.ThisKeywordRole), IndexerDeclaration.ThisKeywordRole); + + if (location != null && location.Count > 0) + newIndexer.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LBracket), Roles.LBracket); + AddParameter(newIndexer, i.ParameterInfo); + if (location != null && location.Count > 1) + newIndexer.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RBracket), Roles.RBracket); + + if (location != null && location.Count > 2) + newIndexer.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.LBrace), Roles.LBrace); + if (i.Get != null) { + var getAccessor = new Accessor(); + var getLocation = LocationsBag.GetMemberLocation(i.Get); + AddAttributeSection(getAccessor, i.Get); + AddModifiers(getAccessor, getLocation); + if (getLocation != null) + getAccessor.AddChild(new CSharpTokenNode(Convert(i.Get.Location), PropertyDeclaration.GetKeywordRole), PropertyDeclaration.GetKeywordRole); + if (i.Get.Block != null) { + getAccessor.AddChild((BlockStatement)i.Get.Block.Accept(this), Roles.Body); + } else { + if (getLocation != null && getLocation.Count > 0) + newIndexer.AddChild(new CSharpTokenNode(Convert(getLocation [0]), Roles.Semicolon), Roles.Semicolon); + } + newIndexer.AddChild(getAccessor, PropertyDeclaration.GetterRole); + } + + if (i.Set != null) { + var setAccessor = new Accessor(); + var setLocation = LocationsBag.GetMemberLocation(i.Set); + AddAttributeSection(setAccessor, i.Set); + AddModifiers(setAccessor, setLocation); + if (setLocation != null) + setAccessor.AddChild(new CSharpTokenNode(Convert(i.Set.Location), PropertyDeclaration.SetKeywordRole), PropertyDeclaration.SetKeywordRole); + + if (i.Set.Block != null) { + setAccessor.AddChild((BlockStatement)i.Set.Block.Accept(this), Roles.Body); + } else { + if (setLocation != null && setLocation.Count > 0) + newIndexer.AddChild(new CSharpTokenNode(Convert(setLocation [0]), Roles.Semicolon), Roles.Semicolon); + } + newIndexer.AddChild(setAccessor, PropertyDeclaration.SetterRole); + } + + if (location != null) { + if (location.Count > 3) + newIndexer.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.RBrace), Roles.RBrace); + } else { + // parser error, set end node to max value. + newIndexer.AddChild(new ErrorNode(), Roles.Error); + } + typeStack.Peek().AddChild(newIndexer, Roles.TypeMemberRole); + } + + public override void Visit(Method m) + { + var newMethod = new MethodDeclaration(); + AddAttributeSection(newMethod, m); + var location = LocationsBag.GetMemberLocation(m); + AddModifiers(newMethod, location); + newMethod.AddChild(ConvertToType(m.TypeExpression), Roles.Type); + AddExplicitInterface(newMethod, m.MethodName); + newMethod.AddChild(Identifier.Create(m.MethodName.Name, Convert(m.Location)), Roles.Identifier); + + AddTypeParameters(newMethod, m.MemberName); + + if (location != null && location.Count > 0) + newMethod.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + AddParameter(newMethod, m.ParameterInfo); + + if (location != null && location.Count > 1) + newMethod.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + AddConstraints(newMethod, m.CurrentTypeParameters); + + if (m.Block != null) { + var bodyBlock = (BlockStatement)m.Block.Accept(this); +// if (m.Block is ToplevelBlock) { +// newMethod.AddChild (bodyBlock.FirstChild.NextSibling, Roles.Body); +// } else { + newMethod.AddChild(bodyBlock, Roles.Body); +// } + } else if (location != null) { + if (location.Count < 3) { + // parser error, set end node to max value. + newMethod.AddChild(new ErrorNode(), Roles.Error); + } else { + newMethod.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.Semicolon), Roles.Semicolon); + } + } + typeStack.Peek().AddChild(newMethod, Roles.TypeMemberRole); + } + + static readonly Dictionary modifierTable = new Dictionary(); + static readonly string[] keywordTable; + + static ConversionVisitor() + { + modifierTable [Mono.CSharp.Modifiers.NEW] = Modifiers.New; + modifierTable [Mono.CSharp.Modifiers.PUBLIC] = Modifiers.Public; + modifierTable [Mono.CSharp.Modifiers.PROTECTED] = Modifiers.Protected; + modifierTable [Mono.CSharp.Modifiers.PRIVATE] = Modifiers.Private; + modifierTable [Mono.CSharp.Modifiers.INTERNAL] = Modifiers.Internal; + modifierTable [Mono.CSharp.Modifiers.ABSTRACT] = Modifiers.Abstract; + modifierTable [Mono.CSharp.Modifiers.VIRTUAL] = Modifiers.Virtual; + modifierTable [Mono.CSharp.Modifiers.SEALED] = Modifiers.Sealed; + modifierTable [Mono.CSharp.Modifiers.STATIC] = Modifiers.Static; + modifierTable [Mono.CSharp.Modifiers.OVERRIDE] = Modifiers.Override; + modifierTable [Mono.CSharp.Modifiers.READONLY] = Modifiers.Readonly; + modifierTable [Mono.CSharp.Modifiers.PARTIAL] = Modifiers.Partial; + modifierTable [Mono.CSharp.Modifiers.EXTERN] = Modifiers.Extern; + modifierTable [Mono.CSharp.Modifiers.VOLATILE] = Modifiers.Volatile; + modifierTable [Mono.CSharp.Modifiers.UNSAFE] = Modifiers.Unsafe; + modifierTable [Mono.CSharp.Modifiers.ASYNC] = Modifiers.Async; + + keywordTable = new string[255]; + for (int i = 0; i< keywordTable.Length; i++) + keywordTable [i] = "unknown"; + + keywordTable [(int)BuiltinTypeSpec.Type.Other] = "void"; + keywordTable [(int)BuiltinTypeSpec.Type.String] = "string"; + keywordTable [(int)BuiltinTypeSpec.Type.Int] = "int"; + keywordTable [(int)BuiltinTypeSpec.Type.Object] = "object"; + keywordTable [(int)BuiltinTypeSpec.Type.Float] = "float"; + keywordTable [(int)BuiltinTypeSpec.Type.Double] = "double"; + keywordTable [(int)BuiltinTypeSpec.Type.Long] = "long"; + keywordTable [(int)BuiltinTypeSpec.Type.Byte] = "byte"; + keywordTable [(int)BuiltinTypeSpec.Type.UInt] = "uint"; + keywordTable [(int)BuiltinTypeSpec.Type.ULong] = "ulong"; + keywordTable [(int)BuiltinTypeSpec.Type.Short] = "short"; + keywordTable [(int)BuiltinTypeSpec.Type.UShort] = "ushort"; + keywordTable [(int)BuiltinTypeSpec.Type.SByte] = "sbyte"; + keywordTable [(int)BuiltinTypeSpec.Type.Decimal] = "decimal"; + keywordTable [(int)BuiltinTypeSpec.Type.Char] = "char"; + keywordTable [(int)BuiltinTypeSpec.Type.Bool] = "bool"; + } + + static void AddModifiers(EntityDeclaration parent, LocationsBag.MemberLocations location) + { + if (location == null || location.Modifiers == null) + return; + foreach (var modifier in location.Modifiers) { + Modifiers mod; + if (!modifierTable.TryGetValue(modifier.Item1, out mod)) { + Console.WriteLine("modifier " + modifier.Item1 + " can't be converted,"); + } + + parent.AddChild(new CSharpModifierToken(Convert(modifier.Item2), mod), EntityDeclaration.ModifierRole); + } + } + + public override void Visit(Property p) + { + var newProperty = new PropertyDeclaration(); + AddAttributeSection(newProperty, p); + var location = LocationsBag.GetMemberLocation(p); + AddModifiers(newProperty, location); + newProperty.AddChild(ConvertToType(p.TypeExpression), Roles.Type); + AddExplicitInterface(newProperty, p.MemberName); + newProperty.AddChild(Identifier.Create(p.MemberName.Name, Convert(p.Location)), Roles.Identifier); + + if (location != null && location.Count > 0) + newProperty.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LBrace), Roles.LBrace); + + Accessor getAccessor = null; + if (p.Get != null) { + getAccessor = new Accessor(); + AddAttributeSection(getAccessor, p.Get); + var getLocation = LocationsBag.GetMemberLocation(p.Get); + AddModifiers(getAccessor, getLocation); + getAccessor.AddChild(new CSharpTokenNode(Convert(p.Get.Location), PropertyDeclaration.GetKeywordRole), PropertyDeclaration.GetKeywordRole); + + if (p.Get.Block != null) { + getAccessor.AddChild((BlockStatement)p.Get.Block.Accept(this), Roles.Body); + } else { + if (getLocation != null && getLocation.Count > 0) + getAccessor.AddChild(new CSharpTokenNode(Convert(getLocation [0]), Roles.Semicolon), Roles.Semicolon); + } + } + + Accessor setAccessor = null; + if (p.Set != null) { + setAccessor = new Accessor(); + AddAttributeSection(setAccessor, p.Set); + var setLocation = LocationsBag.GetMemberLocation(p.Set); + AddModifiers(setAccessor, setLocation); + setAccessor.AddChild(new CSharpTokenNode(Convert(p.Set.Location), PropertyDeclaration.SetKeywordRole), PropertyDeclaration.SetKeywordRole); + + if (p.Set.Block != null) { + setAccessor.AddChild((BlockStatement)p.Set.Block.Accept(this), Roles.Body); + } else { + if (setLocation != null && setLocation.Count > 0) + setAccessor.AddChild(new CSharpTokenNode(Convert(setLocation [0]), Roles.Semicolon), Roles.Semicolon); + } + } + if (getAccessor != null && setAccessor != null) { + if (getAccessor.StartLocation < setAccessor.StartLocation) { + newProperty.AddChild(getAccessor, PropertyDeclaration.GetterRole); + newProperty.AddChild(setAccessor, PropertyDeclaration.SetterRole); + } else { + newProperty.AddChild(setAccessor, PropertyDeclaration.SetterRole); + newProperty.AddChild(getAccessor, PropertyDeclaration.GetterRole); + } + } else { + if (getAccessor != null) + newProperty.AddChild(getAccessor, PropertyDeclaration.GetterRole); + if (setAccessor != null) + newProperty.AddChild(setAccessor, PropertyDeclaration.SetterRole); + } + + if (location != null && location.Count > 1) { + newProperty.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RBrace), Roles.RBrace); + } else { + // parser error, set end node to max value. + newProperty.AddChild(new ErrorNode(), Roles.Error); + } + + typeStack.Peek().AddChild(newProperty, Roles.TypeMemberRole); + } + + public override void Visit(Constructor c) + { + var newConstructor = new ConstructorDeclaration(); + AddAttributeSection(newConstructor, c); + var location = LocationsBag.GetMemberLocation(c); + AddModifiers(newConstructor, location); + newConstructor.AddChild(Identifier.Create(c.MemberName.Name, Convert(c.MemberName.Location)), Roles.Identifier); + if (location != null && location.Count > 0) + newConstructor.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + AddParameter(newConstructor, c.ParameterInfo); + if (location != null && location.Count > 1) + newConstructor.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + if (c.Initializer != null) { + var initializer = new ConstructorInitializer(); + initializer.ConstructorInitializerType = c.Initializer is ConstructorBaseInitializer ? ConstructorInitializerType.Base : ConstructorInitializerType.This; + var initializerLocation = LocationsBag.GetLocations(c.Initializer); + + if (initializerLocation != null) + newConstructor.AddChild(new CSharpTokenNode(Convert(initializerLocation [0]), Roles.Colon), Roles.Colon); + + if (initializerLocation != null && initializerLocation.Count > 1) { + // this and base has the same length + var r = initializer.ConstructorInitializerType == ConstructorInitializerType.This ? ConstructorInitializer.ThisKeywordRole : ConstructorInitializer.BaseKeywordRole; + initializer.AddChild(new CSharpTokenNode(Convert(c.Initializer.Location), r), r); + initializer.AddChild(new CSharpTokenNode(Convert(initializerLocation [1]), Roles.LPar), Roles.LPar); + AddArguments(initializer, c.Initializer.Arguments); + initializer.AddChild(new CSharpTokenNode(Convert(initializerLocation [2]), Roles.RPar), Roles.RPar); + newConstructor.AddChild(initializer, ConstructorDeclaration.InitializerRole); + } + } + + if (c.Block != null) + newConstructor.AddChild((BlockStatement)c.Block.Accept(this), Roles.Body); + typeStack.Peek().AddChild(newConstructor, Roles.TypeMemberRole); + } + + public override void Visit(Destructor d) + { + var newDestructor = new DestructorDeclaration(); + AddAttributeSection(newDestructor, d); + var location = LocationsBag.GetMemberLocation(d); + AddModifiers(newDestructor, location); + if (location != null && location.Count > 0) + newDestructor.AddChild(new CSharpTokenNode(Convert(location [0]), DestructorDeclaration.TildeRole), DestructorDeclaration.TildeRole); + newDestructor.AddChild(Identifier.Create(d.Identifier, Convert(d.MemberName.Location)), Roles.Identifier); + + if (location != null && location.Count > 1) { + newDestructor.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LPar), Roles.LPar); + + if (location.Count > 2) + newDestructor.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RPar), Roles.RPar); + } + + if (d.Block != null) + newDestructor.AddChild((BlockStatement)d.Block.Accept(this), Roles.Body); + + typeStack.Peek().AddChild(newDestructor, Roles.TypeMemberRole); + } + + public override void Visit(EventField e) + { + var newEvent = new EventDeclaration(); + AddAttributeSection(newEvent, e); + var location = LocationsBag.GetMemberLocation(e); + int l = 0; + AddModifiers(newEvent, location); + + if (location != null && location.Count > 0) + newEvent.AddChild(new CSharpTokenNode(Convert(location [l++]), EventDeclaration.EventKeywordRole), EventDeclaration.EventKeywordRole); + newEvent.AddChild(ConvertToType(e.TypeExpression), Roles.Type); + + var variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(e.MemberName.Name, Convert(e.MemberName.Location)), Roles.Identifier); + + if (e.Initializer != null) { + if (location != null && location.Count > l) + variable.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)e.Initializer.Accept(this), Roles.Expression); + } + newEvent.AddChild(variable, Roles.Variable); + if (e.Declarators != null) { + foreach (var decl in e.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + if (declLoc != null) + newEvent.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + + variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(decl.Name.Value, Convert(decl.Name.Location)), Roles.Identifier); + + if (decl.Initializer != null) { + if (declLoc != null) + variable.AddChild(new CSharpTokenNode(Convert(declLoc [1]), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + newEvent.AddChild(variable, Roles.Variable); + } + } + + if (location != null && location.Count > l) + newEvent.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.Semicolon), Roles.Semicolon); + + typeStack.Peek().AddChild(newEvent, Roles.TypeMemberRole); + } + + void AddExplicitInterface(AstNode parent, MemberName memberName) + { + if (memberName == null || memberName.ExplicitInterface == null) + return; + + parent.AddChild(ConvertToType(memberName.ExplicitInterface), EntityDeclaration.PrivateImplementationTypeRole); + var privateImplTypeLoc = LocationsBag.GetLocations(memberName.ExplicitInterface); + if (privateImplTypeLoc != null) + parent.AddChild(new CSharpTokenNode(Convert(privateImplTypeLoc [0]), Roles.Dot), Roles.Dot); + } + + public override void Visit(EventProperty ep) + { + var newEvent = new CustomEventDeclaration(); + AddAttributeSection(newEvent, ep); + var location = LocationsBag.GetMemberLocation(ep); + AddModifiers(newEvent, location); + + if (location != null && location.Count > 0) + newEvent.AddChild(new CSharpTokenNode(Convert(location [0]), CustomEventDeclaration.EventKeywordRole), CustomEventDeclaration.EventKeywordRole); + newEvent.AddChild(ConvertToType(ep.TypeExpression), Roles.Type); + + AddExplicitInterface(newEvent, ep.MemberName); + + newEvent.AddChild(Identifier.Create(ep.MemberName.Name, Convert(ep.Location)), Roles.Identifier); + + if (location != null && location.Count >= 2) + newEvent.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LBrace), Roles.LBrace); + + if (ep.Add != null) { + var addAccessor = new Accessor(); + AddAttributeSection(addAccessor, ep.Add); + var addLocation = LocationsBag.GetMemberLocation(ep.Add); + AddModifiers(addAccessor, addLocation); + addAccessor.AddChild(new CSharpTokenNode(Convert(ep.Add.Location), CustomEventDeclaration.AddKeywordRole), CustomEventDeclaration.AddKeywordRole); + if (ep.Add.Block != null) + addAccessor.AddChild((BlockStatement)ep.Add.Block.Accept(this), Roles.Body); + newEvent.AddChild(addAccessor, CustomEventDeclaration.AddAccessorRole); + } + + if (ep.Remove != null) { + var removeAccessor = new Accessor(); + AddAttributeSection(removeAccessor, ep.Remove); + var removeLocation = LocationsBag.GetMemberLocation(ep.Remove); + AddModifiers(removeAccessor, removeLocation); + removeAccessor.AddChild(new CSharpTokenNode(Convert(ep.Remove.Location), CustomEventDeclaration.RemoveKeywordRole), CustomEventDeclaration.RemoveKeywordRole); + + if (ep.Remove.Block != null) + removeAccessor.AddChild((BlockStatement)ep.Remove.Block.Accept(this), Roles.Body); + newEvent.AddChild(removeAccessor, CustomEventDeclaration.RemoveAccessorRole); + } + if (location != null && location.Count >= 3) { + newEvent.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RBrace), Roles.RBrace); + } else { + // parser error, set end node to max value. + newEvent.AddChild(new ErrorNode(), Roles.Error); + } + + typeStack.Peek().AddChild(newEvent, Roles.TypeMemberRole); + } + + #endregion + + #region Statements + + public override object Visit(Mono.CSharp.Statement stmt) + { + Console.WriteLine("unknown statement:" + stmt); + return null; + } + + public override object Visit(BlockVariable blockVariableDeclaration) + { + var result = new VariableDeclarationStatement(); + result.AddChild(ConvertToType(blockVariableDeclaration.TypeExpression), Roles.Type); + + var varInit = new VariableInitializer(); + var location = LocationsBag.GetLocations(blockVariableDeclaration); + varInit.AddChild(Identifier.Create(blockVariableDeclaration.Variable.Name, Convert(blockVariableDeclaration.Variable.Location)), Roles.Identifier); + if (blockVariableDeclaration.Initializer != null) { + if (location != null && location.Count > 0) + varInit.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Assign), Roles.Assign); + varInit.AddChild((Expression)blockVariableDeclaration.Initializer.Accept(this), Roles.Expression); + } + + result.AddChild(varInit, Roles.Variable); + + if (blockVariableDeclaration.Declarators != null) { + foreach (var decl in blockVariableDeclaration.Declarators) { + var loc = LocationsBag.GetLocations(decl); + var init = new VariableInitializer(); + if (loc != null && loc.Count > 0) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Comma), Roles.Comma); + init.AddChild(Identifier.Create(decl.Variable.Name, Convert(decl.Variable.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (loc != null && loc.Count > 1) + init.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Assign), Roles.Assign); + init.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + result.AddChild(init, Roles.Variable); + } + } + if (location != null && (blockVariableDeclaration.Initializer == null || location.Count > 1)) + result.AddChild(new CSharpTokenNode(Convert(location [location.Count - 1]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(BlockConstant blockConstantDeclaration) + { + var result = new VariableDeclarationStatement(); + + var location = LocationsBag.GetLocations(blockConstantDeclaration); + if (location != null && location.Count > 0) + result.AddChild(new CSharpModifierToken(Convert(location [0]), Modifiers.Const), VariableDeclarationStatement.ModifierRole); + + result.AddChild(ConvertToType(blockConstantDeclaration.TypeExpression), Roles.Type); + + var varInit = new VariableInitializer(); + varInit.AddChild(Identifier.Create(blockConstantDeclaration.Variable.Name, Convert(blockConstantDeclaration.Variable.Location)), Roles.Identifier); + if (blockConstantDeclaration.Initializer != null) { + if (location != null && location.Count > 1) + varInit.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Assign), Roles.Assign); + varInit.AddChild((Expression)blockConstantDeclaration.Initializer.Accept(this), Roles.Expression); + } + + result.AddChild(varInit, Roles.Variable); + + if (blockConstantDeclaration.Declarators != null) { + foreach (var decl in blockConstantDeclaration.Declarators) { + var loc = LocationsBag.GetLocations(decl); + var init = new VariableInitializer(); + init.AddChild(Identifier.Create(decl.Variable.Name, Convert(decl.Variable.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (loc != null) + init.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Assign), Roles.Assign); + init.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + if (loc != null && loc.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Comma), Roles.Comma); + } else { + if (loc != null && loc.Count > 0) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Comma), Roles.Comma); + } + result.AddChild(init, Roles.Variable); + } + } + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [location.Count - 1]), Roles.Semicolon), Roles.Semicolon); + } else { + // parser error, set end node to max value. + result.AddChild(new ErrorNode(), Roles.Error); + } + return result; + } + + public override object Visit(Mono.CSharp.EmptyStatement emptyStatement) + { + var result = new EmptyStatement(); + result.Location = Convert(emptyStatement.loc); + return result; + } + + public override object Visit(Mono.CSharp.ErrorExpression errorExpression) + { + return new ErrorExpression(Convert(errorExpression.Location)); + } + + public override object Visit(EmptyExpressionStatement emptyExpressionStatement) + { + // Should never happen. + throw new NotSupportedException(); + } + + public override object Visit(If ifStatement) + { + var result = new IfElseStatement(); + + var location = LocationsBag.GetLocations(ifStatement); + + result.AddChild(new CSharpTokenNode(Convert(ifStatement.loc), IfElseStatement.IfKeywordRole), IfElseStatement.IfKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (ifStatement.Expr != null) + result.AddChild((Expression)ifStatement.Expr.Accept(this), Roles.Condition); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + if (ifStatement.TrueStatement != null) + result.AddChild((Statement)ifStatement.TrueStatement.Accept(this), IfElseStatement.TrueRole); + + if (ifStatement.FalseStatement != null) { + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), IfElseStatement.ElseKeywordRole), IfElseStatement.ElseKeywordRole); + result.AddChild((Statement)ifStatement.FalseStatement.Accept(this), IfElseStatement.FalseRole); + } + + return result; + } + + public override object Visit(Do doStatement) + { + var result = new DoWhileStatement(); + var location = LocationsBag.GetLocations(doStatement); + result.AddChild(new CSharpTokenNode(Convert(doStatement.loc), DoWhileStatement.DoKeywordRole), DoWhileStatement.DoKeywordRole); + if (doStatement.Statement != null) + result.AddChild((Statement)doStatement.Statement.Accept(this), Roles.EmbeddedStatement); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), DoWhileStatement.WhileKeywordRole), DoWhileStatement.WhileKeywordRole); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LPar), Roles.LPar); + if (doStatement.expr != null) + result.AddChild((Expression)doStatement.expr.Accept(this), Roles.Condition); + if (location != null && location.Count > 2) { + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RPar), Roles.RPar); + if (location.Count > 3) + result.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.Semicolon), Roles.Semicolon); + } + + return result; + } + + public override object Visit(While whileStatement) + { + var result = new WhileStatement(); + var location = LocationsBag.GetLocations(whileStatement); + result.AddChild(new CSharpTokenNode(Convert(whileStatement.loc), WhileStatement.WhileKeywordRole), WhileStatement.WhileKeywordRole); + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (whileStatement.expr != null) + result.AddChild((Expression)whileStatement.expr.Accept(this), Roles.Condition); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + if (whileStatement.Statement != null) + result.AddChild((Statement)whileStatement.Statement.Accept(this), Roles.EmbeddedStatement); + return result; + } + + void AddStatementOrList(ForStatement forStatement, Mono.CSharp.Statement init, Role role) + { + if (init == null) + return; + var stmtList = init as StatementList; + if (stmtList != null) { + foreach (var stmt in stmtList.Statements) { + forStatement.AddChild((Statement)stmt.Accept(this), role); + } + } else if (init is Mono.CSharp.EmptyStatement) { + + } else { + forStatement.AddChild((Statement)init.Accept(this), role); + } + } + + public override object Visit(For forStatement) + { + var result = new ForStatement(); + + var location = LocationsBag.GetLocations(forStatement); + + result.AddChild(new CSharpTokenNode(Convert(forStatement.loc), ForStatement.ForKeywordRole), ForStatement.ForKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + AddStatementOrList(result, forStatement.Initializer, ForStatement.InitializerRole); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + if (forStatement.Condition != null) + result.AddChild((Expression)forStatement.Condition.Accept(this), Roles.Condition); + if (location != null && location.Count >= 3) + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.Semicolon), Roles.Semicolon); + + AddStatementOrList(result, forStatement.Iterator, ForStatement.IteratorRole); + + if (location != null && location.Count >= 4) + result.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.RPar), Roles.RPar); + + if (forStatement.Statement != null) + result.AddChild((Statement)forStatement.Statement.Accept(this), Roles.EmbeddedStatement); + + return result; + } + + public override object Visit(StatementExpression statementExpression) + { + var result = new ExpressionStatement(); + var expr = statementExpression.Expr.Accept(this) as Expression; + if (expr != null) + result.AddChild(expr, Roles.Expression); + var location = LocationsBag.GetLocations(statementExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(StatementErrorExpression errorStatement) + { + var result = new ExpressionStatement(); + var expr = errorStatement.Expr.Accept(this) as Expression; + if (expr != null) + result.AddChild(expr, Roles.Expression); + var location = LocationsBag.GetLocations(errorStatement); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(InvalidStatementExpression invalidStatementExpression) + { + var result = new ExpressionStatement(); + if (invalidStatementExpression.Expression == null) + return result; + var expr = invalidStatementExpression.Expression.Accept(this) as Expression; + if (expr != null) + result.AddChild(expr, Roles.Expression); + var location = LocationsBag.GetLocations(invalidStatementExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(Return returnStatement) + { + var result = new ReturnStatement(); + + result.AddChild(new CSharpTokenNode(Convert(returnStatement.loc), ReturnStatement.ReturnKeywordRole), ReturnStatement.ReturnKeywordRole); + if (returnStatement.Expr != null) + result.AddChild((Expression)returnStatement.Expr.Accept(this), Roles.Expression); + + var location = LocationsBag.GetLocations(returnStatement); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + + return result; + } + + public override object Visit(Goto gotoStatement) + { + var result = new GotoStatement(); + var location = LocationsBag.GetLocations(gotoStatement); + result.AddChild(new CSharpTokenNode(Convert(gotoStatement.loc), GotoStatement.GotoKeywordRole), GotoStatement.GotoKeywordRole); + var loc = location != null ? Convert(location [0]) : TextLocation.Empty; + result.AddChild(Identifier.Create(gotoStatement.Target, loc), Roles.Identifier); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + + return result; + } + + public override object Visit(LabeledStatement labeledStatement) + { + var result = new LabelStatement(); + result.AddChild(Identifier.Create(labeledStatement.Name, Convert(labeledStatement.loc)), Roles.Identifier); + var location = LocationsBag.GetLocations(labeledStatement); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Colon), Roles.Colon); + return result; + } + + public override object Visit(GotoDefault gotoDefault) + { + var result = new GotoDefaultStatement(); + result.AddChild(new CSharpTokenNode(Convert(gotoDefault.loc), GotoDefaultStatement.GotoKeywordRole), GotoDefaultStatement.GotoKeywordRole); + var location = LocationsBag.GetLocations(gotoDefault); + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [0]), GotoDefaultStatement.DefaultKeywordRole), GotoDefaultStatement.DefaultKeywordRole); + if (location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + } + + return result; + } + + public override object Visit(GotoCase gotoCase) + { + var result = new GotoCaseStatement(); + result.AddChild(new CSharpTokenNode(Convert(gotoCase.loc), GotoCaseStatement.GotoKeywordRole), GotoCaseStatement.GotoKeywordRole); + + var location = LocationsBag.GetLocations(gotoCase); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), GotoCaseStatement.CaseKeywordRole), GotoCaseStatement.CaseKeywordRole); + if (gotoCase.Expr != null) + result.AddChild((Expression)gotoCase.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(Throw throwStatement) + { + var result = new ThrowStatement(); + var location = LocationsBag.GetLocations(throwStatement); + + result.AddChild(new CSharpTokenNode(Convert(throwStatement.loc), ThrowStatement.ThrowKeywordRole), ThrowStatement.ThrowKeywordRole); + if (throwStatement.Expr != null) + result.AddChild((Expression)throwStatement.Expr.Accept(this), Roles.Expression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(Break breakStatement) + { + var result = new BreakStatement(); + var location = LocationsBag.GetLocations(breakStatement); + + result.AddChild(new CSharpTokenNode(Convert(breakStatement.loc), BreakStatement.BreakKeywordRole), BreakStatement.BreakKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(Continue continueStatement) + { + var result = new ContinueStatement(); + var location = LocationsBag.GetLocations(continueStatement); + result.AddChild(new CSharpTokenNode(Convert(continueStatement.loc), ContinueStatement.ContinueKeywordRole), ContinueStatement.ContinueKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public static bool IsLower(Location left, Location right) + { + return left.Row < right.Row || left.Row == right.Row && left.Column < right.Column; + } + + public UsingStatement CreateUsingStatement(Block blockStatement) + { + var usingResult = new UsingStatement(); + Mono.CSharp.Statement cur = blockStatement.Statements [0]; + var u = cur as Using; + if (u != null) { + usingResult.AddChild(new CSharpTokenNode(Convert(u.loc), UsingStatement.UsingKeywordRole), UsingStatement.UsingKeywordRole); + usingResult.AddChild(new CSharpTokenNode(Convert(blockStatement.StartLocation), Roles.LPar), Roles.LPar); + if (u.Variables != null) { + var initializer = new VariableInitializer { + NameToken = Identifier.Create(u.Variables.Variable.Name, Convert(u.Variables.Variable.Location)), + }; + + var loc = LocationsBag.GetLocations(u.Variables); + if (loc != null) + initializer.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Assign), Roles.Assign); + if (u.Variables.Initializer != null) + initializer.Initializer = u.Variables.Initializer.Accept(this) as Expression; + + + var varDec = new VariableDeclarationStatement { + Type = ConvertToType(u.Variables.TypeExpression), + Variables = { initializer } + }; + + if (u.Variables.Declarators != null) { + foreach (var decl in u.Variables.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + var init = new VariableInitializer(); + if (declLoc != null && declLoc.Count > 0) + varDec.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + init.AddChild(Identifier.Create(decl.Variable.Name, Convert(decl.Variable.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (declLoc != null && declLoc.Count > 1) + init.AddChild(new CSharpTokenNode(Convert(declLoc [1]), Roles.Assign), Roles.Assign); + init.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + varDec.AddChild(init, Roles.Variable); + } + } + usingResult.AddChild(varDec, UsingStatement.ResourceAcquisitionRole); + } + cur = u.Statement; + usingResult.AddChild(new CSharpTokenNode(Convert(blockStatement.EndLocation), Roles.RPar), Roles.RPar); + if (cur != null) + usingResult.AddChild((Statement)cur.Accept(this), Roles.EmbeddedStatement); + } + return usingResult; + } + + void AddBlockChildren(BlockStatement result, Block blockStatement, ref int curLocal) + { + if (convertTypeSystemMode) { + return; + } + foreach (Mono.CSharp.Statement stmt in blockStatement.Statements) { + if (stmt == null) + continue; + /* if (curLocal < localVariables.Count && IsLower (localVariables[curLocal].Location, stmt.loc)) { + result.AddChild (CreateVariableDeclaration (localVariables[curLocal]), Roles.Statement); + curLocal++; + }*/ + if (stmt is Block && !(stmt is ToplevelBlock || stmt is ExplicitBlock)) { + AddBlockChildren(result, (Block)stmt, ref curLocal); + } else { + result.AddChild((Statement)stmt.Accept(this), BlockStatement.StatementRole); + } + } + } + + public override object Visit(Block blockStatement) + { + if (blockStatement.IsCompilerGenerated && blockStatement.Statements.Any()) { + if (blockStatement.Statements.First() is Using) + return CreateUsingStatement(blockStatement); + return blockStatement.Statements.Last().Accept(this); + } + var result = new BlockStatement(); + result.AddChild(new CSharpTokenNode(Convert(blockStatement.StartLocation), Roles.LBrace), Roles.LBrace); + int curLocal = 0; + AddBlockChildren(result, blockStatement, ref curLocal); + + result.AddChild(new CSharpTokenNode(Convert(blockStatement.EndLocation), Roles.RBrace), Roles.RBrace); + return result; + } + + public override object Visit(Switch switchStatement) + { + var result = new SwitchStatement(); + + var location = LocationsBag.GetLocations(switchStatement); + result.AddChild(new CSharpTokenNode(Convert(switchStatement.loc), SwitchStatement.SwitchKeywordRole), SwitchStatement.SwitchKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (switchStatement.Expr != null) + result.AddChild((Expression)switchStatement.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.LBrace), Roles.LBrace); + SwitchSection newSection = null; + bool lastWasCase = false, added = true; + if (switchStatement.Block != null) { + foreach (var child in switchStatement.Block.Statements) { + var statement = child.Accept(this); + var caseLabel = statement as CaseLabel; + if (caseLabel != null) { + if (!lastWasCase) { + newSection = new SwitchSection(); + added = false; + } + newSection.AddChild(caseLabel, SwitchSection.CaseLabelRole); + lastWasCase = true; + } else { + if (lastWasCase) { + result.AddChild(newSection, SwitchStatement.SwitchSectionRole); + lastWasCase = false; + added = true; + } + newSection.AddChild((Statement)statement, Roles.EmbeddedStatement); + } + } + } + if (!added) + result.AddChild(newSection, SwitchStatement.SwitchSectionRole); + + if (location != null && location.Count > 3) { + result.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.RBrace), Roles.RBrace); + } else { + // parser error, set end node to max value. + result.AddChild(new ErrorNode(), Roles.Error); + } + + return result; + } + + public override object Visit(SwitchLabel switchLabel) + { + var newLabel = new CaseLabel(); + if (!switchLabel.IsDefault) { + newLabel.AddChild(new CSharpTokenNode(Convert(switchLabel.Location), CaseLabel.CaseKeywordRole), CaseLabel.CaseKeywordRole); + if (switchLabel.Label != null) + newLabel.AddChild((Expression)switchLabel.Label.Accept(this), Roles.Expression); + var colonLocation = LocationsBag.GetLocations(switchLabel); + if (colonLocation != null) + newLabel.AddChild(new CSharpTokenNode(Convert(colonLocation [0]), Roles.Colon), Roles.Colon); + } else { + newLabel.AddChild(new CSharpTokenNode(Convert(switchLabel.Location), CaseLabel.DefaultKeywordRole), CaseLabel.DefaultKeywordRole); + newLabel.AddChild(new CSharpTokenNode(new TextLocation(switchLabel.Location.Row, switchLabel.Location.Column + "default".Length), Roles.Colon), Roles.Colon); + } + return newLabel; + } + + public override object Visit(Lock lockStatement) + { + var result = new LockStatement(); + var location = LocationsBag.GetLocations(lockStatement); + result.AddChild(new CSharpTokenNode(Convert(lockStatement.loc), LockStatement.LockKeywordRole), LockStatement.LockKeywordRole); + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (lockStatement.Expr != null) + result.AddChild((Expression)lockStatement.Expr.Accept(this), Roles.Expression); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + if (lockStatement.Statement != null) + result.AddChild((Statement)lockStatement.Statement.Accept(this), Roles.EmbeddedStatement); + + return result; + } + + public override object Visit(Unchecked uncheckedStatement) + { + var result = new UncheckedStatement(); + result.AddChild(new CSharpTokenNode(Convert(uncheckedStatement.loc), UncheckedStatement.UncheckedKeywordRole), UncheckedStatement.UncheckedKeywordRole); + if (uncheckedStatement.Block != null) + result.AddChild((BlockStatement)uncheckedStatement.Block.Accept(this), Roles.Body); + return result; + } + + public override object Visit(Checked checkedStatement) + { + var result = new CheckedStatement(); + result.AddChild(new CSharpTokenNode(Convert(checkedStatement.loc), CheckedStatement.CheckedKeywordRole), CheckedStatement.CheckedKeywordRole); + if (checkedStatement.Block != null) + result.AddChild((BlockStatement)checkedStatement.Block.Accept(this), Roles.Body); + return result; + } + + public override object Visit(Unsafe unsafeStatement) + { + var result = new UnsafeStatement(); + result.AddChild(new CSharpTokenNode(Convert(unsafeStatement.loc), UnsafeStatement.UnsafeKeywordRole), UnsafeStatement.UnsafeKeywordRole); + if (unsafeStatement.Block != null) + result.AddChild((BlockStatement)unsafeStatement.Block.Accept(this), Roles.Body); + return result; + } + + public override object Visit(Fixed fixedStatement) + { + var result = new FixedStatement(); + var location = LocationsBag.GetLocations(fixedStatement); + + result.AddChild(new CSharpTokenNode(Convert(fixedStatement.loc), FixedStatement.FixedKeywordRole), FixedStatement.FixedKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + if (fixedStatement.Variables != null) { + var blockVariableDeclaration = fixedStatement.Variables; + result.AddChild(ConvertToType(blockVariableDeclaration.TypeExpression), Roles.Type); + var varInit = new VariableInitializer(); + var initLocation = LocationsBag.GetLocations(blockVariableDeclaration); + varInit.AddChild(Identifier.Create(blockVariableDeclaration.Variable.Name, Convert(blockVariableDeclaration.Variable.Location)), Roles.Identifier); + if (blockVariableDeclaration.Initializer != null) { + if (initLocation != null) + varInit.AddChild(new CSharpTokenNode(Convert(initLocation [0]), Roles.Assign), Roles.Assign); + varInit.AddChild((Expression)blockVariableDeclaration.Initializer.Accept(this), Roles.Expression); + } + + result.AddChild(varInit, Roles.Variable); + + if (blockVariableDeclaration.Declarators != null) { + foreach (var decl in blockVariableDeclaration.Declarators) { + var loc = LocationsBag.GetLocations(decl); + var init = new VariableInitializer(); + if (loc != null && loc.Count > 0) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Comma), Roles.Comma); + init.AddChild(Identifier.Create(decl.Variable.Name, Convert(decl.Variable.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (loc != null && loc.Count > 1) + init.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Assign), Roles.Assign); + init.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + result.AddChild(init, Roles.Variable); + } + } + } + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + if (fixedStatement.Statement != null) + result.AddChild((Statement)fixedStatement.Statement.Accept(this), Roles.EmbeddedStatement); + return result; + } + + public override object Visit(TryFinally tryFinallyStatement) + { + TryCatchStatement result; + var location = LocationsBag.GetLocations(tryFinallyStatement); + + if (tryFinallyStatement.Stmt is TryCatch) { + result = (TryCatchStatement)tryFinallyStatement.Stmt.Accept(this); + } else { + result = new TryCatchStatement(); + result.AddChild(new CSharpTokenNode(Convert(tryFinallyStatement.loc), TryCatchStatement.TryKeywordRole), TryCatchStatement.TryKeywordRole); + if (tryFinallyStatement.Stmt != null) + result.AddChild((BlockStatement)tryFinallyStatement.Stmt.Accept(this), TryCatchStatement.TryBlockRole); + } + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), TryCatchStatement.FinallyKeywordRole), TryCatchStatement.FinallyKeywordRole); + if (tryFinallyStatement.Fini != null) + result.AddChild((BlockStatement)tryFinallyStatement.Fini.Accept(this), TryCatchStatement.FinallyBlockRole); + + return result; + } + + CatchClause ConvertCatch(Catch ctch) + { + var result = new CatchClause(); + var location = LocationsBag.GetLocations(ctch); + result.AddChild(new CSharpTokenNode(Convert(ctch.loc), CatchClause.CatchKeywordRole), CatchClause.CatchKeywordRole); + if (ctch.TypeExpression != null) { + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + if (ctch.TypeExpression != null) + result.AddChild(ConvertToType(ctch.TypeExpression), Roles.Type); + if (ctch.Variable != null && !string.IsNullOrEmpty(ctch.Variable.Name)) + result.AddChild(Identifier.Create(ctch.Variable.Name, Convert(ctch.Variable.Location)), Roles.Identifier); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + } + + if (ctch.Block != null) + result.AddChild((BlockStatement)ctch.Block.Accept(this), Roles.Body); + + return result; + } + + public override object Visit(TryCatch tryCatchStatement) + { + var result = new TryCatchStatement(); + result.AddChild(new CSharpTokenNode(Convert(tryCatchStatement.loc), TryCatchStatement.TryKeywordRole), TryCatchStatement.TryKeywordRole); + if (tryCatchStatement.Block != null) + result.AddChild((BlockStatement)tryCatchStatement.Block.Accept(this), TryCatchStatement.TryBlockRole); + if (tryCatchStatement.Clauses != null) { + foreach (var ctch in tryCatchStatement.Clauses) { + result.AddChild(ConvertCatch(ctch), TryCatchStatement.CatchClauseRole); + } + } +// if (tryCatchStatement.General != null) +// result.AddChild (ConvertCatch (tryCatchStatement.General), TryCatchStatement.CatchClauseRole); + + return result; + } + + public override object Visit(Using usingStatement) + { + var result = new UsingStatement(); + var location = LocationsBag.GetLocations(usingStatement); + + result.AddChild(new CSharpTokenNode(Convert(usingStatement.loc), UsingStatement.UsingKeywordRole), UsingStatement.UsingKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (usingStatement.Expr != null) + result.AddChild((AstNode)usingStatement.Expr.Accept(this), UsingStatement.ResourceAcquisitionRole); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + if (usingStatement.Statement != null) + result.AddChild((Statement)usingStatement.Statement.Accept(this), Roles.EmbeddedStatement); + return result; + } + + public override object Visit(Foreach foreachStatement) + { + var result = new ForeachStatement(); + + var location = LocationsBag.GetLocations(foreachStatement); + + result.AddChild(new CSharpTokenNode(Convert(foreachStatement.loc), ForeachStatement.ForeachKeywordRole), ForeachStatement.ForeachKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + if (foreachStatement.TypeExpression != null) + result.AddChild(ConvertToType(foreachStatement.TypeExpression), Roles.Type); + + if (foreachStatement.Variable != null) + result.AddChild(Identifier.Create(foreachStatement.Variable.Name, Convert(foreachStatement.Variable.Location)), Roles.Identifier); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), ForeachStatement.InKeywordRole), ForeachStatement.InKeywordRole); + + if (foreachStatement.Expr != null) + result.AddChild((Expression)foreachStatement.Expr.Accept(this), Roles.Expression); + + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RPar), Roles.RPar); + + if (foreachStatement.Statement != null) + result.AddChild((Statement)foreachStatement.Statement.Accept(this), Roles.EmbeddedStatement); + + return result; + } + + public override object Visit(Yield yieldStatement) + { + var result = new YieldReturnStatement(); + var location = LocationsBag.GetLocations(yieldStatement); + + result.AddChild(new CSharpTokenNode(Convert(yieldStatement.loc), YieldReturnStatement.YieldKeywordRole), YieldReturnStatement.YieldKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), YieldReturnStatement.ReturnKeywordRole), YieldReturnStatement.ReturnKeywordRole); + if (yieldStatement.Expr != null) + result.AddChild((Expression)yieldStatement.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + + return result; + } + + public override object Visit(YieldBreak yieldBreakStatement) + { + var result = new YieldBreakStatement(); + var location = LocationsBag.GetLocations(yieldBreakStatement); + result.AddChild(new CSharpTokenNode(Convert(yieldBreakStatement.loc), YieldBreakStatement.YieldKeywordRole), YieldBreakStatement.YieldKeywordRole); + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [0]), YieldBreakStatement.BreakKeywordRole), YieldBreakStatement.BreakKeywordRole); + if (location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + } + return result; + } + + #endregion + + #region Expression + + public override object Visit(Mono.CSharp.Expression expression) + { + Console.WriteLine("Visit unknown expression:" + expression); + Console.WriteLine(Environment.StackTrace); + return null; + } + + public override object Visit(DefaultParameterValueExpression defaultParameterValueExpression) + { + return defaultParameterValueExpression.Child.Accept(this); + } + + public override object Visit(TypeExpression typeExpression) + { + return new TypeReferenceExpression(new PrimitiveType(keywordTable [(int)typeExpression.Type.BuiltinType], Convert(typeExpression.Location))); + } + + public override object Visit(LocalVariableReference localVariableReference) + { + return Identifier.Create(localVariableReference.Name, Convert(localVariableReference.Location)); + } + + public override object Visit(MemberAccess memberAccess) + { + Expression result; + var ind = memberAccess.LeftExpression as Indirection; + if (ind != null) { + result = new PointerReferenceExpression(); + result.AddChild((Expression)ind.Expr.Accept(this), Roles.TargetExpression); + result.AddChild(new CSharpTokenNode(Convert(ind.Location), PointerReferenceExpression.ArrowRole), PointerReferenceExpression.ArrowRole); + } else { + result = new MemberReferenceExpression(); + if (memberAccess.LeftExpression != null) { + var leftExpr = memberAccess.LeftExpression.Accept(this); + result.AddChild((Expression)leftExpr, Roles.TargetExpression); + } + var loc = LocationsBag.GetLocations(memberAccess); + + if (loc != null) { + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Dot), Roles.Dot); + } + } + + result.AddChild(Identifier.Create(memberAccess.Name, Convert(memberAccess.Location)), Roles.Identifier); + + AddTypeArguments(result, memberAccess); + return result; + } + + public override object Visit(QualifiedAliasMember qualifiedAliasMember) + { + var result = new MemberType(); + result.Target = new SimpleType(qualifiedAliasMember.alias, Convert(qualifiedAliasMember.Location)); + result.IsDoubleColon = true; + var location = LocationsBag.GetLocations(qualifiedAliasMember); + if (location != null && location.Count > 0) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.DoubleColon), Roles.DoubleColon); + + AddTypeArguments(result, qualifiedAliasMember); + result.AddChild(Identifier.Create(qualifiedAliasMember.Name, location != null && location.Count > 1 ? Convert(location [1]) : TextLocation.Empty), Roles.Identifier); + return new TypeReferenceExpression { Type = result }; + } + + public override object Visit(Constant constant) + { + if (constant.GetValue() == null) + return new NullReferenceExpression(Convert(constant.Location)); + string literalValue; + var literalConstant = constant as ILiteralConstant; + literalValue = literalConstant != null ? new string(literalConstant.ParsedValue) : constant.GetValueAsLiteral(); + object val = constant.GetValue(); + if (val is bool) + literalValue = (bool)val ? "true" : "false"; + var result = new PrimitiveExpression(val, Convert(constant.Location), literalValue); + return result; + } + + public override object Visit(SimpleName simpleName) + { + var result = new IdentifierExpression(); + result.AddChild(Identifier.Create(simpleName.Name, Convert(simpleName.Location)), Roles.Identifier); + AddTypeArguments(result, simpleName); + return result; + } + + public override object Visit(BooleanExpression booleanExpression) + { + return booleanExpression.Expr.Accept(this); + } + + public override object Visit(Mono.CSharp.ParenthesizedExpression parenthesizedExpression) + { + var result = new ParenthesizedExpression(); + var location = LocationsBag.GetLocations(parenthesizedExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (parenthesizedExpression.Expr != null) + result.AddChild((Expression)parenthesizedExpression.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(Unary unaryExpression) + { + var result = new UnaryOperatorExpression(); + switch (unaryExpression.Oper) { + case Unary.Operator.UnaryPlus: + result.Operator = UnaryOperatorType.Plus; + break; + case Unary.Operator.UnaryNegation: + result.Operator = UnaryOperatorType.Minus; + break; + case Unary.Operator.LogicalNot: + result.Operator = UnaryOperatorType.Not; + break; + case Unary.Operator.OnesComplement: + result.Operator = UnaryOperatorType.BitNot; + break; + case Unary.Operator.AddressOf: + result.Operator = UnaryOperatorType.AddressOf; + break; + } + var r = UnaryOperatorExpression.GetOperatorRole(result.Operator); + result.AddChild(new CSharpTokenNode(Convert(unaryExpression.Location), r), r); + if (unaryExpression.Expr != null) + result.AddChild((Expression)unaryExpression.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(UnaryMutator unaryMutatorExpression) + { + var result = new UnaryOperatorExpression(); + if (unaryMutatorExpression.Expr == null) + return result; + var expression = (Expression)unaryMutatorExpression.Expr.Accept(this); + switch (unaryMutatorExpression.UnaryMutatorMode) { + case UnaryMutator.Mode.PostDecrement: + result.Operator = UnaryOperatorType.PostDecrement; + result.AddChild(expression, Roles.Expression); + result.AddChild(new CSharpTokenNode(Convert(unaryMutatorExpression.Location), UnaryOperatorExpression.DecrementRole), UnaryOperatorExpression.DecrementRole); + break; + case UnaryMutator.Mode.PostIncrement: + result.Operator = UnaryOperatorType.PostIncrement; + result.AddChild(expression, Roles.Expression); + result.AddChild(new CSharpTokenNode(Convert(unaryMutatorExpression.Location), UnaryOperatorExpression.IncrementRole), UnaryOperatorExpression.IncrementRole); + break; + + case UnaryMutator.Mode.PreIncrement: + result.Operator = UnaryOperatorType.Increment; + result.AddChild(new CSharpTokenNode(Convert(unaryMutatorExpression.Location), UnaryOperatorExpression.IncrementRole), UnaryOperatorExpression.IncrementRole); + result.AddChild(expression, Roles.Expression); + break; + case UnaryMutator.Mode.PreDecrement: + result.Operator = UnaryOperatorType.Decrement; + result.AddChild(new CSharpTokenNode(Convert(unaryMutatorExpression.Location), UnaryOperatorExpression.DecrementRole), UnaryOperatorExpression.DecrementRole); + result.AddChild(expression, Roles.Expression); + break; + } + + return result; + } + + public override object Visit(Indirection indirectionExpression) + { + var result = new UnaryOperatorExpression(); + result.Operator = UnaryOperatorType.Dereference; + result.AddChild(new CSharpTokenNode(Convert(indirectionExpression.Location), UnaryOperatorExpression.DereferenceRole), UnaryOperatorExpression.DereferenceRole); + if (indirectionExpression.Expr != null) + result.AddChild((Expression)indirectionExpression.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(Is isExpression) + { + var result = new IsExpression(); + if (isExpression.Expr != null) + result.AddChild((Expression)isExpression.Expr.Accept(this), Roles.Expression); + result.AddChild(new CSharpTokenNode(Convert(isExpression.Location), IsExpression.IsKeywordRole), IsExpression.IsKeywordRole); + + if (isExpression.ProbeType != null) + result.AddChild(ConvertToType(isExpression.ProbeType), Roles.Type); + return result; + } + + public override object Visit(As asExpression) + { + var result = new AsExpression(); + if (asExpression.Expr != null) + result.AddChild((Expression)asExpression.Expr.Accept(this), Roles.Expression); + result.AddChild(new CSharpTokenNode(Convert(asExpression.Location), AsExpression.AsKeywordRole), AsExpression.AsKeywordRole); + if (asExpression.ProbeType != null) + result.AddChild(ConvertToType(asExpression.ProbeType), Roles.Type); + return result; + } + + public override object Visit(Cast castExpression) + { + var result = new CastExpression(); + var location = LocationsBag.GetLocations(castExpression); + + result.AddChild(new CSharpTokenNode(Convert(castExpression.Location), Roles.LPar), Roles.LPar); + if (castExpression.TargetType != null) + result.AddChild(ConvertToType(castExpression.TargetType), Roles.Type); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.RPar), Roles.RPar); + if (castExpression.Expr != null) + result.AddChild((Expression)castExpression.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(ComposedCast composedCast) + { + var result = new ComposedType(); + result.AddChild(ConvertToType(composedCast.Left), Roles.Type); + + var spec = composedCast.Spec; + while (spec != null) { + if (spec.IsNullable) { + result.AddChild(new CSharpTokenNode(Convert(spec.Location), ComposedType.NullableRole), ComposedType.NullableRole); + } else if (spec.IsPointer) { + result.AddChild(new CSharpTokenNode(Convert(spec.Location), ComposedType.PointerRole), ComposedType.PointerRole); + } else { + var aSpec = new ArraySpecifier(); + aSpec.AddChild(new CSharpTokenNode(Convert(spec.Location), Roles.LBracket), Roles.LBracket); + var location = LocationsBag.GetLocations(spec); + if (location != null) + aSpec.AddChild(new CSharpTokenNode(Convert(spec.Location), Roles.RBracket), Roles.RBracket); + result.AddChild(aSpec, ComposedType.ArraySpecifierRole); + } + spec = spec.Next; + } + + return result; + } + + public override object Visit(Mono.CSharp.DefaultValueExpression defaultValueExpression) + { + var result = new DefaultValueExpression(); + result.AddChild(new CSharpTokenNode(Convert(defaultValueExpression.Location), DefaultValueExpression.DefaultKeywordRole), DefaultValueExpression.DefaultKeywordRole); + var location = LocationsBag.GetLocations(defaultValueExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + result.AddChild(ConvertToType(defaultValueExpression.Expr), Roles.Type); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(Binary binaryExpression) + { + var result = new BinaryOperatorExpression(); + switch (binaryExpression.Oper) { + case Binary.Operator.Multiply: + result.Operator = BinaryOperatorType.Multiply; + break; + case Binary.Operator.Division: + result.Operator = BinaryOperatorType.Divide; + break; + case Binary.Operator.Modulus: + result.Operator = BinaryOperatorType.Modulus; + break; + case Binary.Operator.Addition: + result.Operator = BinaryOperatorType.Add; + break; + case Binary.Operator.Subtraction: + result.Operator = BinaryOperatorType.Subtract; + break; + case Binary.Operator.LeftShift: + result.Operator = BinaryOperatorType.ShiftLeft; + break; + case Binary.Operator.RightShift: + result.Operator = BinaryOperatorType.ShiftRight; + break; + case Binary.Operator.LessThan: + result.Operator = BinaryOperatorType.LessThan; + break; + case Binary.Operator.GreaterThan: + result.Operator = BinaryOperatorType.GreaterThan; + break; + case Binary.Operator.LessThanOrEqual: + result.Operator = BinaryOperatorType.LessThanOrEqual; + break; + case Binary.Operator.GreaterThanOrEqual: + result.Operator = BinaryOperatorType.GreaterThanOrEqual; + break; + case Binary.Operator.Equality: + result.Operator = BinaryOperatorType.Equality; + break; + case Binary.Operator.Inequality: + result.Operator = BinaryOperatorType.InEquality; + break; + case Binary.Operator.BitwiseAnd: + result.Operator = BinaryOperatorType.BitwiseAnd; + break; + case Binary.Operator.ExclusiveOr: + result.Operator = BinaryOperatorType.ExclusiveOr; + break; + case Binary.Operator.BitwiseOr: + result.Operator = BinaryOperatorType.BitwiseOr; + break; + case Binary.Operator.LogicalAnd: + result.Operator = BinaryOperatorType.ConditionalAnd; + break; + case Binary.Operator.LogicalOr: + result.Operator = BinaryOperatorType.ConditionalOr; + break; + } + + if (binaryExpression.Left != null) + result.AddChild((Expression)binaryExpression.Left.Accept(this), BinaryOperatorExpression.LeftRole); + var location = LocationsBag.GetLocations(binaryExpression); + if (location != null) { + var r = BinaryOperatorExpression.GetOperatorRole(result.Operator); + result.AddChild(new CSharpTokenNode(Convert(location [0]), r), r); + } + if (binaryExpression.Right != null) + result.AddChild((Expression)binaryExpression.Right.Accept(this), BinaryOperatorExpression.RightRole); + return result; + } + + public override object Visit(Mono.CSharp.Nullable.NullCoalescingOperator nullCoalescingOperator) + { + var result = new BinaryOperatorExpression(); + result.Operator = BinaryOperatorType.NullCoalescing; + if (nullCoalescingOperator.LeftExpression != null) + result.AddChild((Expression)nullCoalescingOperator.LeftExpression.Accept(this), BinaryOperatorExpression.LeftRole); + var location = LocationsBag.GetLocations(nullCoalescingOperator); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), BinaryOperatorExpression.NullCoalescingRole), BinaryOperatorExpression.NullCoalescingRole); + if (nullCoalescingOperator.RightExpression != null) + result.AddChild((Expression)nullCoalescingOperator.RightExpression.Accept(this), BinaryOperatorExpression.RightRole); + return result; + } + + public override object Visit(Conditional conditionalExpression) + { + var result = new ConditionalExpression(); + + if (conditionalExpression.Expr != null) + result.AddChild((Expression)conditionalExpression.Expr.Accept(this), Roles.Condition); + var location = LocationsBag.GetLocations(conditionalExpression); + + result.AddChild(new CSharpTokenNode(Convert(conditionalExpression.Location), ConditionalExpression.QuestionMarkRole), ConditionalExpression.QuestionMarkRole); + if (conditionalExpression.TrueExpr != null) + result.AddChild((Expression)conditionalExpression.TrueExpr.Accept(this), ConditionalExpression.TrueRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), ConditionalExpression.ColonRole), ConditionalExpression.ColonRole); + if (conditionalExpression.FalseExpr != null) + result.AddChild((Expression)conditionalExpression.FalseExpr.Accept(this), ConditionalExpression.FalseRole); + return result; + } + + void AddParameter(AstNode parent, AParametersCollection parameters) + { + if (parameters == null) + return; + var paramLocation = LocationsBag.GetLocations(parameters); + + for (int i = 0; i < parameters.Count; i++) { + var p = (Parameter)parameters.FixedParameters [i]; + if (p == null) + continue; + var location = LocationsBag.GetLocations(p); + var parameterDeclarationExpression = new ParameterDeclaration(); + AddAttributeSection(parameterDeclarationExpression, p); + switch (p.ModFlags) { + case Parameter.Modifier.OUT: + parameterDeclarationExpression.ParameterModifier = ParameterModifier.Out; + if (location != null) + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [0]), ParameterDeclaration.OutModifierRole), ParameterDeclaration.OutModifierRole); + break; + case Parameter.Modifier.REF: + parameterDeclarationExpression.ParameterModifier = ParameterModifier.Ref; + if (location != null) + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [0]), ParameterDeclaration.RefModifierRole), ParameterDeclaration.RefModifierRole); + break; + case Parameter.Modifier.PARAMS: + parameterDeclarationExpression.ParameterModifier = ParameterModifier.Params; + if (location != null) + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [0]), ParameterDeclaration.ParamsModifierRole), ParameterDeclaration.ParamsModifierRole); + break; + default: + if (p.HasExtensionMethodModifier) { + parameterDeclarationExpression.ParameterModifier = ParameterModifier.This; + if (location != null) { + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [0]), ParameterDeclaration.ThisModifierRole), ParameterDeclaration.ThisModifierRole); + } + } + break; + } + if (p.TypeExpression != null) // lambdas may have no types (a, b) => ... + parameterDeclarationExpression.AddChild(ConvertToType(p.TypeExpression), Roles.Type); + if (p.Name != null) + parameterDeclarationExpression.AddChild(Identifier.Create(p.Name, Convert(p.Location)), Roles.Identifier); + if (p.HasDefaultValue) { + if (location != null && location.Count > 1) + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Assign), Roles.Assign); + parameterDeclarationExpression.AddChild((Expression)p.DefaultValue.Accept(this), Roles.Expression); + } + parent.AddChild(parameterDeclarationExpression, Roles.Parameter); + if (paramLocation != null && i < paramLocation.Count) { + parent.AddChild(new CSharpTokenNode(Convert(paramLocation [i]), Roles.Comma), Roles.Comma); + } + } + } + + void AddTypeParameters(AstNode parent, MemberName memberName) + { + if (memberName == null || memberName.TypeParameters == null) + return; + var chevronLocs = LocationsBag.GetLocations(memberName.TypeParameters); + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 2]), Roles.LChevron), Roles.LChevron); + for (int i = 0; i < memberName.TypeParameters.Count; i++) { + if (chevronLocs != null && i > 0 && i - 1 < chevronLocs.Count) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [i - 1]), Roles.Comma), Roles.Comma); + var arg = memberName.TypeParameters [i]; + if (arg == null) + continue; + var tp = new TypeParameterDeclaration(); + + List varianceLocation; + switch (arg.Variance) { + case Variance.Contravariant: + tp.Variance = VarianceModifier.Contravariant; + varianceLocation = LocationsBag.GetLocations(arg); + if (varianceLocation != null) + tp.AddChild(new CSharpTokenNode(Convert(varianceLocation [0]), TypeParameterDeclaration.InVarianceKeywordRole), TypeParameterDeclaration.InVarianceKeywordRole); + break; + case Variance.Covariant: + tp.Variance = VarianceModifier.Covariant; + varianceLocation = LocationsBag.GetLocations(arg); + if (varianceLocation != null) + tp.AddChild(new CSharpTokenNode(Convert(varianceLocation [0]), TypeParameterDeclaration.OutVarianceKeywordRole), TypeParameterDeclaration.OutVarianceKeywordRole); + break; + default: + tp.Variance = VarianceModifier.Invariant; + break; + + } + + AddAttributeSection(tp, arg.OptAttributes); + + switch (arg.Variance) { + case Variance.Covariant: + tp.Variance = VarianceModifier.Covariant; + break; + case Variance.Contravariant: + tp.Variance = VarianceModifier.Contravariant; + break; + } + tp.AddChild(Identifier.Create(arg.Name, Convert(arg.Location)), Roles.Identifier); + parent.AddChild(tp, Roles.TypeParameter); + } + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 1]), Roles.RChevron), Roles.RChevron); + } + + void AddTypeArguments(AstNode parent, MemberName memberName) + { + if (memberName == null || memberName.TypeParameters == null) + return; + var chevronLocs = LocationsBag.GetLocations(memberName.TypeParameters); + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 2]), Roles.LChevron), Roles.LChevron); + + for (int i = 0; i < memberName.TypeParameters.Count; i++) { + var arg = memberName.TypeParameters [i]; + if (arg == null) + continue; + parent.AddChild(ConvertToType(arg), Roles.TypeArgument); + if (chevronLocs != null && i < chevronLocs.Count - 2) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [i]), Roles.Comma), Roles.Comma); + } + + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 1]), Roles.RChevron), Roles.RChevron); + } + + void AddTypeArguments(AstNode parent, ATypeNameExpression memberName) + { + if (memberName == null || !memberName.HasTypeArguments) + return; + var chevronLocs = LocationsBag.GetLocations(memberName.TypeArguments); + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 2]), Roles.LChevron), Roles.LChevron); + + for (int i = 0; i < memberName.TypeArguments.Count; i++) { + var arg = memberName.TypeArguments.Args [i]; + if (arg == null) + continue; + parent.AddChild(ConvertToType(arg), Roles.TypeArgument); + if (chevronLocs != null && i < chevronLocs.Count - 2) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [i]), Roles.Comma), Roles.Comma); + } + + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 1]), Roles.RChevron), Roles.RChevron); + } + + void AddConstraints(AstNode parent, TypeParameters d) + { + if (d == null) + return; + for (int i = 0; i < d.Count; i++) { + var typeParameter = d [i]; + if (typeParameter == null) + continue; + var c = typeParameter.Constraints; + if (c == null) + continue; + var location = LocationsBag.GetLocations(c); + var constraint = new Constraint(); + constraint.AddChild(new CSharpTokenNode(Convert(c.Location), Roles.WhereKeyword), Roles.WhereKeyword); + constraint.AddChild(new SimpleType(Identifier.Create(c.TypeParameter.Value, Convert(c.TypeParameter.Location))), Roles.ConstraintTypeParameter); + if (location != null) + constraint.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Colon), Roles.Colon); + var commaLocs = LocationsBag.GetLocations(c.ConstraintExpressions); + int curComma = 0; + if (c.ConstraintExpressions != null) { + foreach (var expr in c.ConstraintExpressions) { + constraint.AddChild(ConvertToType(expr), Roles.BaseType); + var sce = expr as SpecialContraintExpr; + if (sce != null) { + switch (sce.Constraint) { + case SpecialConstraint.Class: + break; + case SpecialConstraint.Struct: + break; + case SpecialConstraint.Constructor: + var bl = LocationsBag.GetLocations(expr); + if (bl != null) { + constraint.AddChild(new CSharpTokenNode(Convert(bl [0]), Roles.LPar), Roles.LPar); + constraint.AddChild(new CSharpTokenNode(Convert(bl [1]), Roles.RPar), Roles.RPar); + } + break; + } + } + + if (commaLocs != null && curComma < commaLocs.Count) + constraint.AddChild(new CSharpTokenNode(Convert(commaLocs [curComma++]), Roles.Comma), Roles.Comma); + } + } + + // We need to sort the constraints by position; as they might be in a different order than the type parameters + AstNode prevSibling = parent.LastChild; + while (prevSibling.StartLocation > constraint.StartLocation && prevSibling.PrevSibling != null) + prevSibling = prevSibling.PrevSibling; + parent.InsertChildAfter(prevSibling, constraint, Roles.Constraint); + } + } + + Expression ConvertArgument(Argument arg) + { + var na = arg as NamedArgument; + if (na != null) { + var newArg = new NamedArgumentExpression(); + newArg.AddChild(Identifier.Create(na.Name, Convert(na.Location)), Roles.Identifier); + + var loc = LocationsBag.GetLocations(na); + if (loc != null) + newArg.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Colon), Roles.Colon); + + if (arg.ArgType == Argument.AType.Out || arg.ArgType == Argument.AType.Ref) { + var direction = new DirectionExpression(); + direction.FieldDirection = arg.ArgType == Argument.AType.Out ? FieldDirection.Out : FieldDirection.Ref; + var argLocation = LocationsBag.GetLocations(arg); + if (argLocation != null) { + var r = arg.ArgType == Argument.AType.Out ? DirectionExpression.OutKeywordRole : DirectionExpression.RefKeywordRole; + direction.AddChild(new CSharpTokenNode(Convert(argLocation [0]), r), r); + } + direction.AddChild((Expression)arg.Expr.Accept(this), Roles.Expression); + newArg.AddChild(direction, Roles.Expression); + } else { + newArg.AddChild(na.Expr != null ? (Expression)na.Expr.Accept(this) : new ErrorExpression("Named argument expression parse error"), Roles.Expression); + } + return newArg; + } + + if (arg.ArgType == Argument.AType.Out || arg.ArgType == Argument.AType.Ref) { + var direction = new DirectionExpression(); + direction.FieldDirection = arg.ArgType == Argument.AType.Out ? FieldDirection.Out : FieldDirection.Ref; + var argLocation = LocationsBag.GetLocations(arg); + if (argLocation != null) { + var r = arg.ArgType == Argument.AType.Out ? DirectionExpression.OutKeywordRole : DirectionExpression.RefKeywordRole; + direction.AddChild(new CSharpTokenNode(Convert(argLocation [0]), r), r); + } + direction.AddChild((Expression)arg.Expr.Accept(this), Roles.Expression); + return direction; + } + + return (Expression)arg.Expr.Accept(this); + } + + void AddArguments(AstNode parent, Arguments args) + { + if (args == null) + return; + + var commaLocations = LocationsBag.GetLocations(args); + for (int i = 0; i < args.Count; i++) { + parent.AddChild(ConvertArgument(args [i]), Roles.Argument); + if (commaLocations != null && i < commaLocations.Count) { + parent.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + } + } + if (commaLocations != null && commaLocations.Count > args.Count) + parent.AddChild(new CSharpTokenNode(Convert(commaLocations [args.Count]), Roles.Comma), Roles.Comma); + } + + public override object Visit(Invocation invocationExpression) + { + var result = new InvocationExpression(); + var location = LocationsBag.GetLocations(invocationExpression); + if (invocationExpression.Exp != null) + result.AddChild((Expression)invocationExpression.Exp.Accept(this), Roles.TargetExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + AddArguments(result, invocationExpression.Arguments); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(New newExpression) + { + var result = new ObjectCreateExpression(); + var location = LocationsBag.GetLocations(newExpression); + result.AddChild(new CSharpTokenNode(Convert(newExpression.Location), ObjectCreateExpression.NewKeywordRole), ObjectCreateExpression.NewKeywordRole); + + if (newExpression.TypeRequested != null) + result.AddChild(ConvertToType(newExpression.TypeRequested), Roles.Type); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + AddArguments(result, newExpression.Arguments); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + return result; + } + + public override object Visit(NewAnonymousType newAnonymousType) + { + var result = new AnonymousTypeCreateExpression(); + var location = LocationsBag.GetLocations(newAnonymousType); + result.AddChild(new CSharpTokenNode(Convert(newAnonymousType.Location), ObjectCreateExpression.NewKeywordRole), ObjectCreateExpression.NewKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LBrace), Roles.LBrace); + if (newAnonymousType.Parameters != null) { + foreach (var par in newAnonymousType.Parameters) { + if (par == null) + continue; + var parLocation = LocationsBag.GetLocations(par); + + if (parLocation == null) { + if (par.Expr != null) + result.AddChild((Expression)par.Expr.Accept(this), Roles.Expression); + } else { + var namedExpression = new NamedExpression(); + namedExpression.AddChild(Identifier.Create(par.Name, Convert(par.Location)), Roles.Identifier); + namedExpression.AddChild(new CSharpTokenNode(Convert(parLocation [0]), Roles.Assign), Roles.Assign); + if (par.Expr != null) + namedExpression.AddChild((Expression)par.Expr.Accept(this), Roles.Expression); + result.AddChild(namedExpression, Roles.Expression); + } + } + } + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RBrace), Roles.RBrace); + return result; + } + + ArrayInitializerExpression ConvertCollectionOrObjectInitializers(CollectionOrObjectInitializers minit) + { + if (minit == null) + return null; + var init = new ArrayInitializerExpression(); + AddConvertCollectionOrObjectInitializers(init, minit); + return init; + } + + void AddConvertCollectionOrObjectInitializers(Expression init, CollectionOrObjectInitializers minit) + { + var initLoc = LocationsBag.GetLocations(minit); + var commaLoc = LocationsBag.GetLocations(minit.Initializers); + int curComma = 0; + init.AddChild(new CSharpTokenNode(Convert(minit.Location), Roles.LBrace), Roles.LBrace); + foreach (var expr in minit.Initializers) { + var collectionInit = expr as CollectionElementInitializer; + if (collectionInit != null) { + AstNode parent; + // For ease of use purposes in the resolver the ast representation + // of { a, b, c } is { {a}, {b}, {c} } - but the generated ArrayInitializerExpression + // can be identified by expr.IsSingleElement. + if (!collectionInit.IsSingle) { + parent = new ArrayInitializerExpression(); + parent.AddChild(new CSharpTokenNode(Convert(collectionInit.Location), Roles.LBrace), Roles.LBrace); + } else { + parent = ArrayInitializerExpression.CreateSingleElementInitializer(); + } + + if (collectionInit.Arguments != null) { + for (int i = 0; i < collectionInit.Arguments.Count; i++) { + var arg = collectionInit.Arguments [i] as CollectionElementInitializer.ElementInitializerArgument; + if (arg == null || arg.Expr == null) + continue; + parent.AddChild( + (Expression)arg.Expr.Accept(this), + Roles.Expression + ); + } + } + + if (!collectionInit.IsSingle) { + var braceLocs = LocationsBag.GetLocations(expr); + if (braceLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(braceLocs [0]), Roles.RBrace), Roles.RBrace); + } + init.AddChild((ArrayInitializerExpression)parent, Roles.Expression); + } else { + var eleInit = expr as ElementInitializer; + if (eleInit != null) { + var nexpr = new NamedExpression(); + nexpr.AddChild( + Identifier.Create(eleInit.Name, Convert(eleInit.Location)), + Roles.Identifier + ); + var assignLoc = LocationsBag.GetLocations(eleInit); + if (assignLoc != null) + nexpr.AddChild(new CSharpTokenNode(Convert(assignLoc [0]), Roles.Assign), Roles.Assign); + if (eleInit.Source != null) { + var colInit = eleInit.Source as CollectionOrObjectInitializers; + if (colInit != null) { + var arrInit = new ArrayInitializerExpression(); + AddConvertCollectionOrObjectInitializers( + arrInit, + colInit + ); + nexpr.AddChild(arrInit, Roles.Expression); + } else { + nexpr.AddChild((Expression)eleInit.Source.Accept(this), Roles.Expression); + } + } + + init.AddChild(nexpr, Roles.Expression); + } + } + if (commaLoc != null && curComma < commaLoc.Count) + init.AddChild(new CSharpTokenNode(Convert(commaLoc [curComma++]), Roles.Comma), Roles.Comma); + } + + if (initLoc != null) { + if (initLoc.Count == 2) // optional comma + init.AddChild(new CSharpTokenNode(Convert(initLoc [0]), Roles.Comma), Roles.Comma); + init.AddChild(new CSharpTokenNode(Convert(initLoc [initLoc.Count - 1]), Roles.RBrace), Roles.RBrace); + } + } + + public override object Visit(NewInitialize newInitializeExpression) + { + var result = new ObjectCreateExpression(); + result.AddChild(new CSharpTokenNode(Convert(newInitializeExpression.Location), ObjectCreateExpression.NewKeywordRole), ObjectCreateExpression.NewKeywordRole); + + if (newInitializeExpression.TypeRequested != null) + result.AddChild(ConvertToType(newInitializeExpression.TypeRequested), Roles.Type); + + var location = LocationsBag.GetLocations(newInitializeExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + AddArguments(result, newInitializeExpression.Arguments); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + var init = ConvertCollectionOrObjectInitializers(newInitializeExpression.Initializers); + if (init != null) + result.AddChild(init, ObjectCreateExpression.InitializerRole); + + return result; + } + + public override object Visit(ArrayCreation arrayCreationExpression) + { + var result = new ArrayCreateExpression(); + + var location = LocationsBag.GetLocations(arrayCreationExpression); + result.AddChild(new CSharpTokenNode(Convert(arrayCreationExpression.Location), ArrayCreateExpression.NewKeywordRole), ArrayCreateExpression.NewKeywordRole); + if (arrayCreationExpression.TypeExpression != null) + result.AddChild(ConvertToType(arrayCreationExpression.TypeExpression), Roles.Type); + + var next = arrayCreationExpression.Rank; + if (arrayCreationExpression.Arguments != null) { + // skip first array rank. + next = next.Next; + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LBracket), Roles.LBracket); + + var commaLocations = LocationsBag.GetLocations(arrayCreationExpression.Arguments); + for (int i = 0; i < arrayCreationExpression.Arguments.Count; i++) { + var arg = arrayCreationExpression.Arguments [i]; + if (arg != null) + result.AddChild((Expression)arg.Accept(this), Roles.Argument); + if (commaLocations != null && i < commaLocations.Count) + result.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + } + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RBracket), Roles.RBracket); + + } + + while (next != null) { + var spec = new ArraySpecifier(next.Dimension); + var loc = LocationsBag.GetLocations(next); + spec.AddChild(new CSharpTokenNode(Convert(next.Location), Roles.LBracket), Roles.LBracket); + result.AddChild(spec, ArrayCreateExpression.AdditionalArraySpecifierRole); + if (loc != null) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.RBracket), Roles.RBracket); + next = next.Next; + } + + if (arrayCreationExpression.Initializers != null) { + var initLocation = LocationsBag.GetLocations(arrayCreationExpression.Initializers); + var initializer = new ArrayInitializerExpression(); + + initializer.AddChild(new CSharpTokenNode(Convert(arrayCreationExpression.Initializers.Location), Roles.LBrace), Roles.LBrace); + var commaLocations = LocationsBag.GetLocations(arrayCreationExpression.Initializers.Elements); + for (int i = 0; i < arrayCreationExpression.Initializers.Count; i++) { + var init = arrayCreationExpression.Initializers [i]; + if (init == null) + continue; + initializer.AddChild((Expression)init.Accept(this), Roles.Expression); + if (commaLocations != null && i < commaLocations.Count) { + initializer.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + } + } + if (initLocation != null) { + if (initLocation.Count == 2) // optional comma + initializer.AddChild(new CSharpTokenNode(Convert(initLocation [0]), Roles.Comma), Roles.Comma); + initializer.AddChild(new CSharpTokenNode(Convert(initLocation [initLocation.Count - 1]), Roles.RBrace), Roles.RBrace); + } + result.AddChild(initializer, ArrayCreateExpression.InitializerRole); + } + + return result; + } + + public override object Visit(This thisExpression) + { + var result = new ThisReferenceExpression(); + result.Location = Convert(thisExpression.Location); + return result; + } + + public override object Visit(ArglistAccess argListAccessExpression) + { + var result = new UndocumentedExpression { + UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess + }; + result.AddChild(new CSharpTokenNode(Convert(argListAccessExpression.Location), UndocumentedExpression.ArglistKeywordRole), UndocumentedExpression.ArglistKeywordRole); + return result; + } + + #region Undocumented expressions + + public override object Visit(Arglist argListExpression) + { + var result = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgList }; + result.AddChild(new CSharpTokenNode(Convert(argListExpression.Location), UndocumentedExpression.ArglistKeywordRole), UndocumentedExpression.ArglistKeywordRole); + var location = LocationsBag.GetLocations(argListExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + AddArguments(result, argListExpression.Arguments); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(MakeRefExpr makeRefExpr) + { + var result = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.MakeRef }; + result.AddChild(new CSharpTokenNode(Convert(makeRefExpr.Location), UndocumentedExpression.MakerefKeywordRole), UndocumentedExpression.MakerefKeywordRole); + var location = LocationsBag.GetLocations(makeRefExpr); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (makeRefExpr.Expr != null) + result.AddChild((Expression)makeRefExpr.Expr.Accept(this), Roles.Argument); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(RefTypeExpr refTypeExpr) + { + var result = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefType }; + result.AddChild(new CSharpTokenNode(Convert(refTypeExpr.Location), UndocumentedExpression.ReftypeKeywordRole), UndocumentedExpression.ReftypeKeywordRole); + var location = LocationsBag.GetLocations(refTypeExpr); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + if (refTypeExpr.Expr != null) + result.AddChild((Expression)refTypeExpr.Expr.Accept(this), Roles.Argument); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(RefValueExpr refValueExpr) + { + var result = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefValue }; + result.AddChild(new CSharpTokenNode(Convert(refValueExpr.Location), UndocumentedExpression.RefvalueKeywordRole), UndocumentedExpression.RefvalueKeywordRole); + var location = LocationsBag.GetLocations(refValueExpr); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + + if (refValueExpr.Expr != null) + result.AddChild((Expression)refValueExpr.Expr.Accept(this), Roles.Argument); + + if (refValueExpr.FullNamedExpression != null) + result.AddChild((Expression)refValueExpr.FullNamedExpression.Accept(this), Roles.Argument); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + #endregion + + public override object Visit(TypeOf typeOfExpression) + { + var result = new TypeOfExpression(); + var location = LocationsBag.GetLocations(typeOfExpression); + result.AddChild(new CSharpTokenNode(Convert(typeOfExpression.Location), TypeOfExpression.TypeofKeywordRole), TypeOfExpression.TypeofKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (typeOfExpression.TypeExpression != null) + result.AddChild(ConvertToType(typeOfExpression.TypeExpression), Roles.Type); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(SizeOf sizeOfExpression) + { + var result = new SizeOfExpression(); + var location = LocationsBag.GetLocations(sizeOfExpression); + result.AddChild(new CSharpTokenNode(Convert(sizeOfExpression.Location), SizeOfExpression.SizeofKeywordRole), SizeOfExpression.SizeofKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (sizeOfExpression.TypeExpression != null) + result.AddChild(ConvertToType(sizeOfExpression.TypeExpression), Roles.Type); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(CheckedExpr checkedExpression) + { + var result = new CheckedExpression(); + var location = LocationsBag.GetLocations(checkedExpression); + result.AddChild(new CSharpTokenNode(Convert(checkedExpression.Location), CheckedExpression.CheckedKeywordRole), CheckedExpression.CheckedKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (checkedExpression.Expr != null) + result.AddChild((Expression)checkedExpression.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(UnCheckedExpr uncheckedExpression) + { + var result = new UncheckedExpression(); + var location = LocationsBag.GetLocations(uncheckedExpression); + result.AddChild(new CSharpTokenNode(Convert(uncheckedExpression.Location), UncheckedExpression.UncheckedKeywordRole), UncheckedExpression.UncheckedKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (uncheckedExpression.Expr != null) + result.AddChild((Expression)uncheckedExpression.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(ElementAccess elementAccessExpression) + { + var result = new IndexerExpression(); + var location = LocationsBag.GetLocations(elementAccessExpression); + + if (elementAccessExpression.Expr != null) + result.AddChild((Expression)elementAccessExpression.Expr.Accept(this), Roles.TargetExpression); + result.AddChild(new CSharpTokenNode(Convert(elementAccessExpression.Location), Roles.LBracket), Roles.LBracket); + AddArguments(result, elementAccessExpression.Arguments); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.RBracket), Roles.RBracket); + return result; + } + + public override object Visit(BaseThis baseAccessExpression) + { + var result = new BaseReferenceExpression(); + result.Location = Convert(baseAccessExpression.Location); + return result; + } + + public override object Visit(StackAlloc stackAllocExpression) + { + var result = new StackAllocExpression(); + + var location = LocationsBag.GetLocations(stackAllocExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), StackAllocExpression.StackallocKeywordRole), StackAllocExpression.StackallocKeywordRole); + if (stackAllocExpression.TypeExpression != null) + result.AddChild(ConvertToType(stackAllocExpression.TypeExpression), Roles.Type); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LBracket), Roles.LBracket); + if (stackAllocExpression.CountExpression != null) + result.AddChild((Expression)stackAllocExpression.CountExpression.Accept(this), Roles.Expression); + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RBracket), Roles.RBracket); + return result; + } + + public override object Visit(SimpleAssign simpleAssign) + { + var result = new AssignmentExpression(); + + result.Operator = AssignmentOperatorType.Assign; + if (simpleAssign.Target != null) + result.AddChild((Expression)simpleAssign.Target.Accept(this), AssignmentExpression.LeftRole); + var location = LocationsBag.GetLocations(simpleAssign); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), AssignmentExpression.AssignRole), AssignmentExpression.AssignRole); + if (simpleAssign.Source != null) { + result.AddChild((Expression)simpleAssign.Source.Accept(this), AssignmentExpression.RightRole); + } + return result; + } + + public override object Visit(CompoundAssign compoundAssign) + { + var result = new AssignmentExpression(); + switch (compoundAssign.Op) { + case Binary.Operator.Multiply: + result.Operator = AssignmentOperatorType.Multiply; + break; + case Binary.Operator.Division: + result.Operator = AssignmentOperatorType.Divide; + break; + case Binary.Operator.Modulus: + result.Operator = AssignmentOperatorType.Modulus; + break; + case Binary.Operator.Addition: + result.Operator = AssignmentOperatorType.Add; + break; + case Binary.Operator.Subtraction: + result.Operator = AssignmentOperatorType.Subtract; + break; + case Binary.Operator.LeftShift: + result.Operator = AssignmentOperatorType.ShiftLeft; + break; + case Binary.Operator.RightShift: + result.Operator = AssignmentOperatorType.ShiftRight; + break; + case Binary.Operator.BitwiseAnd: + result.Operator = AssignmentOperatorType.BitwiseAnd; + break; + case Binary.Operator.BitwiseOr: + result.Operator = AssignmentOperatorType.BitwiseOr; + break; + case Binary.Operator.ExclusiveOr: + result.Operator = AssignmentOperatorType.ExclusiveOr; + break; + } + + if (compoundAssign.Target != null) + result.AddChild((Expression)compoundAssign.Target.Accept(this), AssignmentExpression.LeftRole); + var location = LocationsBag.GetLocations(compoundAssign); + if (location != null) { + var r = AssignmentExpression.GetOperatorRole(result.Operator); + result.AddChild(new CSharpTokenNode(Convert(location [0]), r), r); + } + if (compoundAssign.Source != null) + result.AddChild((Expression)compoundAssign.Source.Accept(this), AssignmentExpression.RightRole); + return result; + } + + public override object Visit(Mono.CSharp.AnonymousMethodExpression anonymousMethodExpression) + { + var result = new AnonymousMethodExpression(); + var location = LocationsBag.GetLocations(anonymousMethodExpression); + int l = 0; + if (anonymousMethodExpression.IsAsync) { + result.IsAsync = true; + result.AddChild(new CSharpTokenNode(Convert(location [l++]), AnonymousMethodExpression.AsyncModifierRole), AnonymousMethodExpression.AsyncModifierRole); + } + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [l++]), AnonymousMethodExpression.DelegateKeywordRole), AnonymousMethodExpression.DelegateKeywordRole); + + if (location.Count > l) { + result.HasParameterList = true; + result.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.LPar), Roles.LPar); + AddParameter(result, anonymousMethodExpression.Parameters); + result.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.RPar), Roles.RPar); + } + } + if (anonymousMethodExpression.Block != null) + result.AddChild((BlockStatement)anonymousMethodExpression.Block.Accept(this), Roles.Body); + return result; + } + + public override object Visit(Mono.CSharp.LambdaExpression lambdaExpression) + { + var result = new LambdaExpression(); + var location = LocationsBag.GetLocations(lambdaExpression); + int l = 0; + if (lambdaExpression.IsAsync) { + result.IsAsync = true; + result.AddChild(new CSharpTokenNode(Convert(location [l++]), LambdaExpression.AsyncModifierRole), LambdaExpression.AsyncModifierRole); + } + if (location == null || location.Count == l + 1) { + if (lambdaExpression.Block != null) + AddParameter(result, lambdaExpression.Parameters); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [l++]), LambdaExpression.ArrowRole), LambdaExpression.ArrowRole); + } else { + result.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.LPar), Roles.LPar); + if (lambdaExpression.Block != null) + AddParameter(result, lambdaExpression.Parameters); + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.RPar), Roles.RPar); + result.AddChild(new CSharpTokenNode(Convert(location [l++]), LambdaExpression.ArrowRole), LambdaExpression.ArrowRole); + } + } + if (lambdaExpression.Block != null) { + if (lambdaExpression.Block.IsCompilerGenerated) { + var generatedReturn = (ContextualReturn)lambdaExpression.Block.Statements [0]; + result.AddChild((AstNode)generatedReturn.Expr.Accept(this), LambdaExpression.BodyRole); + } else { + result.AddChild((AstNode)lambdaExpression.Block.Accept(this), LambdaExpression.BodyRole); + } + } + return result; + } + + public override object Visit(ConstInitializer constInitializer) + { + return constInitializer.Expr.Accept(this); + } + + public override object Visit(ArrayInitializer arrayInitializer) + { + var result = new ArrayInitializerExpression(); + var location = LocationsBag.GetLocations(arrayInitializer); + result.AddChild(new CSharpTokenNode(Convert(arrayInitializer.Location), Roles.LBrace), Roles.LBrace); + var commaLocations = LocationsBag.GetLocations(arrayInitializer.Elements); + for (int i = 0; i < arrayInitializer.Count; i++) { + var init = arrayInitializer [i]; + if (init == null) + continue; + result.AddChild((Expression)init.Accept(this), Roles.Expression); + if (commaLocations != null && i < commaLocations.Count) + result.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + } + + if (location != null) { + if (location.Count == 2) // optional comma + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Comma), Roles.Comma); + result.AddChild(new CSharpTokenNode(Convert(location [location.Count - 1]), Roles.RBrace), Roles.RBrace); + } + return result; + } + + #endregion + + #region LINQ expressions + + QueryOrderClause currentQueryOrderClause; + + public override object Visit(Mono.CSharp.Linq.QueryExpression queryExpression) + { + var oldQueryOrderClause = currentQueryOrderClause; + try { + currentQueryOrderClause = null; + var result = new QueryExpression(); + + var currentClause = queryExpression.next; + + while (currentClause != null) { + var clause = (QueryClause)currentClause.Accept(this); + if (clause is QueryContinuationClause) { + // insert preceding query at beginning of QueryContinuationClause + clause.InsertChildAfter(null, result, QueryContinuationClause.PrecedingQueryRole); + // create a new QueryExpression for the remaining query + result = new QueryExpression(); + } + if (clause != null) { + result.AddChild(clause, QueryExpression.ClauseRole); + } + currentClause = currentClause.next; + } + + return result; + } finally { + currentQueryOrderClause = oldQueryOrderClause; + } + } + + public override object Visit(Mono.CSharp.Linq.QueryStartClause queryExpression) + { + if (queryExpression.Expr == null) { + var intoClause = new QueryContinuationClause(); + intoClause.AddChild(new CSharpTokenNode(Convert(queryExpression.Location), QueryContinuationClause.IntoKeywordRole), QueryContinuationClause.IntoKeywordRole); + intoClause.AddChild(Identifier.Create(queryExpression.IntoVariable.Name, Convert(queryExpression.IntoVariable.Location)), Roles.Identifier); + return intoClause; + } + + var fromClause = new QueryFromClause(); + + fromClause.AddChild(new CSharpTokenNode(Convert(queryExpression.Location), QueryFromClause.FromKeywordRole), QueryFromClause.FromKeywordRole); + + if (queryExpression.IdentifierType != null) + fromClause.AddChild(ConvertToType(queryExpression.IdentifierType), Roles.Type); + + fromClause.AddChild(Identifier.Create(queryExpression.IntoVariable.Name, Convert(queryExpression.IntoVariable.Location)), Roles.Identifier); + + var location = LocationsBag.GetLocations(queryExpression); + if (location != null) + fromClause.AddChild(new CSharpTokenNode(Convert(location [0]), QueryFromClause.InKeywordRole), QueryFromClause.InKeywordRole); + + if (queryExpression.Expr != null) + fromClause.AddChild((Expression)queryExpression.Expr.Accept(this), Roles.Expression); + return fromClause; + } + + public override object Visit(Mono.CSharp.Linq.SelectMany selectMany) + { + var fromClause = new QueryFromClause(); + + fromClause.AddChild(new CSharpTokenNode(Convert(selectMany.Location), QueryFromClause.FromKeywordRole), QueryFromClause.FromKeywordRole); + + if (selectMany.IdentifierType != null) + fromClause.AddChild(ConvertToType(selectMany.IdentifierType), Roles.Type); + + fromClause.AddChild(Identifier.Create(selectMany.IntoVariable.Name, Convert(selectMany.IntoVariable.Location)), Roles.Identifier); + + var location = LocationsBag.GetLocations(selectMany); + if (location != null) + fromClause.AddChild(new CSharpTokenNode(Convert(location [0]), QueryFromClause.InKeywordRole), QueryFromClause.InKeywordRole); + + if (selectMany.Expr != null) + fromClause.AddChild((Expression)selectMany.Expr.Accept(this), Roles.Expression); + return fromClause; + } + + public override object Visit(Mono.CSharp.Linq.Select select) + { + var result = new QuerySelectClause(); + result.AddChild(new CSharpTokenNode(Convert(select.Location), QuerySelectClause.SelectKeywordRole), QuerySelectClause.SelectKeywordRole); + if (select.Expr != null) + result.AddChild((Expression)select.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(Mono.CSharp.Linq.GroupBy groupBy) + { + var result = new QueryGroupClause(); + var location = LocationsBag.GetLocations(groupBy); + result.AddChild(new CSharpTokenNode(Convert(groupBy.Location), QueryGroupClause.GroupKeywordRole), QueryGroupClause.GroupKeywordRole); + if (groupBy.ElementSelector != null) + result.AddChild((Expression)groupBy.ElementSelector.Accept(this), QueryGroupClause.ProjectionRole); + if (location != null) { + var byLoc = Convert(location[0]); + if (byLoc.Line > 1 || byLoc.Column > 1) + result.AddChild(new CSharpTokenNode(byLoc, QueryGroupClause.ByKeywordRole), QueryGroupClause.ByKeywordRole); + } + if (groupBy.Expr != null) + result.AddChild((Expression)groupBy.Expr.Accept(this), QueryGroupClause.KeyRole); + return result; + } + + public override object Visit(Mono.CSharp.Linq.Let let) + { + var result = new QueryLetClause(); + var location = LocationsBag.GetLocations(let); + + result.AddChild(new CSharpTokenNode(Convert(let.Location), QueryLetClause.LetKeywordRole), QueryLetClause.LetKeywordRole); + result.AddChild(Identifier.Create(let.IntoVariable.Name, Convert(let.IntoVariable.Location)), Roles.Identifier); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Assign), Roles.Assign); + if (let.Expr != null) + result.AddChild((Expression)let.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(Mono.CSharp.Linq.Where where) + { + var result = new QueryWhereClause(); + result.AddChild(new CSharpTokenNode(Convert(where.Location), QueryWhereClause.WhereKeywordRole), QueryWhereClause.WhereKeywordRole); + if (where.Expr != null) + result.AddChild((Expression)where.Expr.Accept(this), Roles.Condition); + return result; + } + + public override object Visit(Mono.CSharp.Linq.Join join) + { + var result = new QueryJoinClause(); + var location = LocationsBag.GetLocations(join); + result.AddChild(new CSharpTokenNode(Convert(join.Location), QueryJoinClause.JoinKeywordRole), QueryJoinClause.JoinKeywordRole); + + if (join.IdentifierType != null) + result.AddChild(ConvertToType(join.IdentifierType), QueryJoinClause.TypeRole); + + result.AddChild(Identifier.Create(join.JoinVariable.Name, Convert(join.JoinVariable.Location)), QueryJoinClause.JoinIdentifierRole); + + if (join.IdentifierType != null) + result.AddChild(ConvertToType(join.IdentifierType), QueryJoinClause.TypeRole); + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), QueryJoinClause.InKeywordRole), QueryJoinClause.InKeywordRole); + + if (join.Expr != null) + result.AddChild((Expression)join.Expr.Accept(this), QueryJoinClause.InExpressionRole); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), QueryJoinClause.OnKeywordRole), QueryJoinClause.OnKeywordRole); + + var outer = join.OuterSelector.Statements.FirstOrDefault() as ContextualReturn; + if (outer != null) + result.AddChild((Expression)outer.Expr.Accept(this), QueryJoinClause.OnExpressionRole); + + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), QueryJoinClause.EqualsKeywordRole), QueryJoinClause.EqualsKeywordRole); + + var inner = join.InnerSelector.Statements.FirstOrDefault() as ContextualReturn; + if (inner != null) + result.AddChild((Expression)inner.Expr.Accept(this), QueryJoinClause.EqualsExpressionRole); + + return result; + } + + public override object Visit(Mono.CSharp.Linq.GroupJoin groupJoin) + { + var result = new QueryJoinClause(); + var location = LocationsBag.GetLocations(groupJoin); + result.AddChild(new CSharpTokenNode(Convert(groupJoin.Location), QueryJoinClause.JoinKeywordRole), QueryJoinClause.JoinKeywordRole); + + // mcs seems to have swapped IntoVariable with JoinVariable, so we'll swap it back here + result.AddChild(Identifier.Create(groupJoin.IntoVariable.Name, Convert(groupJoin.IntoVariable.Location)), QueryJoinClause.JoinIdentifierRole); + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), QueryJoinClause.InKeywordRole), QueryJoinClause.InKeywordRole); + + if (groupJoin.Expr != null) + result.AddChild((Expression)groupJoin.Expr.Accept(this), QueryJoinClause.InExpressionRole); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), QueryJoinClause.OnKeywordRole), QueryJoinClause.OnKeywordRole); + + var outer = groupJoin.OuterSelector.Statements.FirstOrDefault() as ContextualReturn; + if (outer != null) + result.AddChild((Expression)outer.Expr.Accept(this), QueryJoinClause.OnExpressionRole); + + + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), QueryJoinClause.EqualsKeywordRole), QueryJoinClause.EqualsKeywordRole); + var inner = groupJoin.InnerSelector.Statements.FirstOrDefault() as ContextualReturn; + if (inner != null) + result.AddChild((Expression)inner.Expr.Accept(this), QueryJoinClause.EqualsExpressionRole); + + if (location != null && location.Count > 3) + result.AddChild(new CSharpTokenNode(Convert(location [3]), QueryJoinClause.IntoKeywordRole), QueryJoinClause.IntoKeywordRole); + + result.AddChild(Identifier.Create(groupJoin.JoinVariable.Name, Convert(groupJoin.JoinVariable.Location)), QueryJoinClause.IntoIdentifierRole); + return result; + } + + public override object Visit(Mono.CSharp.Linq.OrderByAscending orderByAscending) + { + currentQueryOrderClause = new QueryOrderClause(); + var location2 = LocationsBag.GetLocations(orderByAscending.block); + if (location2 != null) + currentQueryOrderClause.AddChild(new CSharpTokenNode(Convert(location2 [0]), QueryOrderClause.OrderbyKeywordRole), QueryOrderClause.OrderbyKeywordRole); + var ordering = new QueryOrdering(); + if (orderByAscending.Expr != null) + ordering.AddChild((Expression)orderByAscending.Expr.Accept(this), Roles.Expression); + var location = LocationsBag.GetLocations(orderByAscending); + if (location != null) { + ordering.Direction = QueryOrderingDirection.Ascending; + ordering.AddChild(new CSharpTokenNode(Convert(location [0]), QueryOrdering.AscendingKeywordRole), QueryOrdering.AscendingKeywordRole); + } + currentQueryOrderClause.AddChild(ordering, QueryOrderClause.OrderingRole); + return currentQueryOrderClause; + } + + public override object Visit(Mono.CSharp.Linq.OrderByDescending orderByDescending) + { + currentQueryOrderClause = new QueryOrderClause(); + + var ordering = new QueryOrdering(); + if (orderByDescending.Expr != null) + ordering.AddChild((Expression)orderByDescending.Expr.Accept(this), Roles.Expression); + var location = LocationsBag.GetLocations(orderByDescending); + if (location != null) { + ordering.Direction = QueryOrderingDirection.Descending; + ordering.AddChild(new CSharpTokenNode(Convert(location [0]), QueryOrdering.DescendingKeywordRole), QueryOrdering.DescendingKeywordRole); + } + currentQueryOrderClause.AddChild(ordering, QueryOrderClause.OrderingRole); + return currentQueryOrderClause; + } + + public override object Visit(Mono.CSharp.Linq.ThenByAscending thenByAscending) + { + var ordering = new QueryOrdering(); + if (thenByAscending.Expr != null) + ordering.AddChild((Expression)thenByAscending.Expr.Accept(this), Roles.Expression); + var location = LocationsBag.GetLocations(thenByAscending); + if (location != null) { + ordering.Direction = QueryOrderingDirection.Ascending; + ordering.AddChild(new CSharpTokenNode(Convert(location [0]), QueryOrdering.AscendingKeywordRole), QueryOrdering.AscendingKeywordRole); + } + currentQueryOrderClause.AddChild(ordering, QueryOrderClause.OrderingRole); + return null; + } + + public override object Visit(Mono.CSharp.Linq.ThenByDescending thenByDescending) + { + var ordering = new QueryOrdering(); + if (thenByDescending.Expr != null) + ordering.AddChild((Expression)thenByDescending.Expr.Accept(this), Roles.Expression); + var location = LocationsBag.GetLocations(thenByDescending); + if (location != null) { + ordering.Direction = QueryOrderingDirection.Descending; + ordering.AddChild(new CSharpTokenNode(Convert(location [0]), QueryOrdering.DescendingKeywordRole), QueryOrdering.DescendingKeywordRole); + } + currentQueryOrderClause.AddChild(ordering, QueryOrderClause.OrderingRole); + return null; + } + + public override object Visit(Await awaitExpr) + { + var result = new UnaryOperatorExpression(); + result.Operator = UnaryOperatorType.Await; + result.AddChild(new CSharpTokenNode(Convert(awaitExpr.Location), UnaryOperatorExpression.AwaitRole), UnaryOperatorExpression.AwaitRole); + if (awaitExpr.Expression != null) + result.AddChild((Expression)awaitExpr.Expression.Accept(this), Roles.Expression); + return result; + } + + #endregion + + #region XmlDoc + + public DocumentationReference ConvertXmlDoc(DocumentationBuilder doc) + { + var result = new DocumentationReference(); + if (doc.ParsedName != null) { + if (doc.ParsedName.Name == "") { + result.SymbolKind = SymbolKind.Indexer; + } else { + result.MemberName = doc.ParsedName.Name; + } + if (doc.ParsedName.Left != null) { + result.DeclaringType = ConvertToType(doc.ParsedName.Left); + } else if (doc.ParsedBuiltinType != null) { + result.DeclaringType = ConvertToType(doc.ParsedBuiltinType); + } + if (doc.ParsedName.TypeParameters != null) { + for (int i = 0; i < doc.ParsedName.TypeParameters.Count; i++) { + result.TypeArguments.Add(ConvertToType(doc.ParsedName.TypeParameters [i])); + } + } + } else if (doc.ParsedBuiltinType != null) { + result.SymbolKind = SymbolKind.TypeDefinition; + result.DeclaringType = ConvertToType(doc.ParsedBuiltinType); + } + if (doc.ParsedParameters != null) { + result.HasParameterList = true; + result.Parameters.AddRange(doc.ParsedParameters.Select(ConvertXmlDocParameter)); + } + if (doc.ParsedOperator != null) { + result.SymbolKind = SymbolKind.Operator; + result.OperatorType = (OperatorType)doc.ParsedOperator; + if (result.OperatorType == OperatorType.Implicit || result.OperatorType == OperatorType.Explicit) { + var returnTypeParam = result.Parameters.LastOrNullObject(); + returnTypeParam.Remove(); // detach from parameter list + var returnType = returnTypeParam.Type; + returnType.Remove(); + result.ConversionOperatorReturnType = returnType; + } + if (result.Parameters.Count == 0) { + // reset HasParameterList if necessary + result.HasParameterList = false; + } + } + return result; + } + + ParameterDeclaration ConvertXmlDocParameter(DocumentationParameter p) + { + var result = new ParameterDeclaration(); + switch (p.Modifier) { + case Parameter.Modifier.OUT: + result.ParameterModifier = ParameterModifier.Out; + break; + case Parameter.Modifier.REF: + result.ParameterModifier = ParameterModifier.Ref; + break; + case Parameter.Modifier.PARAMS: + result.ParameterModifier = ParameterModifier.Params; + break; + } + if (p.Type != null) { + result.Type = ConvertToType(p.Type); + } + return result; + } + + #endregion + + } + + public CSharpParser() + { + compilerSettings = new CompilerSettings(); + } + + public CSharpParser(CompilerSettings args) + { + compilerSettings = args ?? new CompilerSettings(); + } + + void InsertComments(CompilerCompilationUnit top, ConversionVisitor conversionVisitor) + { + AstNode insertionPoint = conversionVisitor.Unit.FirstChild; + foreach (var special in top.SpecialsBag.Specials) { + AstNode newLeaf = null; + Role role = null; + bool isDocumentationComment = false; + var comment = special as SpecialsBag.Comment; + if (comment != null) { + // HACK: multiline documentation comment detection; better move this logic into the mcs tokenizer + bool isMultilineDocumentationComment = (comment.CommentType == SpecialsBag.CommentType.Multi && comment.Content.StartsWith("*", StringComparison.Ordinal) && !comment.Content.StartsWith("**", StringComparison.Ordinal)); + isDocumentationComment = comment.CommentType == SpecialsBag.CommentType.Documentation || isMultilineDocumentationComment; + if (conversionVisitor.convertTypeSystemMode && !isDocumentationComment) + continue; + var type = isMultilineDocumentationComment ? CommentType.MultiLineDocumentation : (CommentType)comment.CommentType; + var start = new TextLocation(comment.Line, comment.Col); + var end = new TextLocation(comment.EndLine, comment.EndCol); + newLeaf = new Comment(type, start, end) { + StartsLine = comment.StartsLine, + Content = isMultilineDocumentationComment ? comment.Content.Substring(1) : comment.Content + }; + role = Roles.Comment; + } else if (!GenerateTypeSystemMode) { + var pragmaDirective = special as SpecialsBag.PragmaPreProcessorDirective; + if (pragmaDirective != null) { + var pragma = new PragmaWarningPreprocessorDirective(new TextLocation(pragmaDirective.Line, pragmaDirective.Col), new TextLocation(pragmaDirective.EndLine, pragmaDirective.EndCol)); + pragma.AddChild(new CSharpTokenNode(new TextLocation(pragmaDirective.Line, pragmaDirective.Col), PragmaWarningPreprocessorDirective.PragmaKeywordRole), PragmaWarningPreprocessorDirective.PragmaKeywordRole); + pragma.AddChild(new CSharpTokenNode(new TextLocation(pragmaDirective.Line, pragmaDirective.WarningColumn), PragmaWarningPreprocessorDirective.WarningKeywordRole), PragmaWarningPreprocessorDirective.WarningKeywordRole); + var pragmaRole = pragmaDirective.Disalbe ? PragmaWarningPreprocessorDirective.DisableKeywordRole : PragmaWarningPreprocessorDirective.RestoreKeywordRole; + pragma.AddChild(new CSharpTokenNode(new TextLocation(pragmaDirective.Line, pragmaDirective.DisableRestoreColumn), pragmaRole), pragmaRole); + foreach (var code in pragmaDirective.Codes) { + pragma.AddChild((PrimitiveExpression)conversionVisitor.Visit(code), PragmaWarningPreprocessorDirective.WarningRole); + } + newLeaf = pragma; + role = Roles.PreProcessorDirective; + goto end; + } + var lineDirective = special as SpecialsBag.LineProcessorDirective; + if (lineDirective != null) { + var pragma = new LinePreprocessorDirective(new TextLocation(lineDirective.Line, lineDirective.Col), new TextLocation(lineDirective.EndLine, lineDirective.EndCol)); + pragma.LineNumber = lineDirective.LineNumber; + pragma.FileName = lineDirective.FileName; + newLeaf = pragma; + role = Roles.PreProcessorDirective; + goto end; + } + var directive = special as SpecialsBag.PreProcessorDirective; + if (directive != null) { + newLeaf = new PreProcessorDirective((PreProcessorDirectiveType)((int)directive.Cmd & 0xF), new TextLocation(directive.Line, directive.Col), new TextLocation(directive.EndLine, directive.EndCol)) { + Argument = directive.Arg, + Take = directive.Take + }; + role = Roles.PreProcessorDirective; + } + end: + ; + } + if (newLeaf != null) { + InsertComment(ref insertionPoint, newLeaf, role, isDocumentationComment, conversionVisitor.Unit); + } + } + if (!GenerateTypeSystemMode) { + // We cannot insert newlines in the same loop as comments/preprocessor directives + // because they are not correctly ordered in the specials bag + insertionPoint = conversionVisitor.Unit.FirstChild; + for (int i = 0; i < top.SpecialsBag.Specials.Count; i++) { + var newLine = top.SpecialsBag.Specials [i] as SpecialsBag.NewLineToken; + if (newLine != null) { + var newLeaf = new NewLineNode(new TextLocation(newLine.Line, newLine.Col + 1)); + newLeaf.NewLineType = newLine.NewLine == SpecialsBag.NewLine.Unix ? UnicodeNewline.LF : UnicodeNewline.CRLF; + InsertComment(ref insertionPoint, newLeaf, Roles.NewLine, false, conversionVisitor.Unit); + } + } + } + } + + static void InsertComment(ref AstNode insertionPoint, AstNode newNode, Role role, bool isDocumentationComment, AstNode rootNode) + { + TextLocation insertAt = newNode.StartLocation; + // Advance insertionPoint to the first node that has a start location >= insertAt + while (insertionPoint != null && insertionPoint.StartLocation < insertAt) { + // Enter the current node if insertAt is within + while (insertAt < insertionPoint.EndLocation && insertionPoint.FirstChild != null) { + insertionPoint = insertionPoint.FirstChild; + } + // Go to next node (insertionPoint.NextSibling if it exists; otherwise the next sibling of the parent node etc.) + insertionPoint = insertionPoint.GetNextNode(); + } + // As a special case, XmlDoc gets inserted at the beginning of the entity declaration + if (isDocumentationComment && insertionPoint is EntityDeclaration && insertionPoint.FirstChild != null) { + insertionPoint = insertionPoint.FirstChild; + } + if (insertionPoint == null) { + // we're at the end of the compilation unit + rootNode.AddChildUnsafe(newNode, role); + } else { + insertionPoint.Parent.InsertChildBeforeUnsafe(insertionPoint, newNode, role); + } + } + + public class ErrorReportPrinter : ReportPrinter + { + readonly string fileName; + public readonly List Errors = new List(); + + public ErrorReportPrinter(string fileName) + { + this.fileName = fileName; + } + + public override void Print(AbstractMessage msg, bool showFullPath) + { + base.Print(msg, showFullPath); + var newError = new Error(msg.IsWarning ? ErrorType.Warning : ErrorType.Error, msg.Text, new DomRegion(fileName, msg.Location.Row, msg.Location.Column)); + Errors.Add(newError); + } + } + + ErrorReportPrinter errorReportPrinter = new ErrorReportPrinter(null); + + [Obsolete("Use the Errors/Warnings/ErrorsAndWarnings properties instead")] + public ErrorReportPrinter ErrorPrinter { + get { + return errorReportPrinter; + } + } + + public bool HasErrors { + get { + return errorReportPrinter.ErrorsCount > 0; + } + } + + public bool HasWarnings { + get { + return errorReportPrinter.WarningsCount > 0; + } + } + + public IEnumerable Errors { + get { + return errorReportPrinter.Errors.Where(e => e.ErrorType == ErrorType.Error); + } + } + + public IEnumerable Warnings { + get { + return errorReportPrinter.Errors.Where(e => e.ErrorType == ErrorType.Warning); + } + } + + public IEnumerable ErrorsAndWarnings { + get { return errorReportPrinter.Errors; } + } + + /// + /// Parses a C# code file. + /// + /// The source code to parse. + /// The file name. Used to identify the file (e.g. when building a type system). + /// This can be an arbitrary identifier, NRefactory never tries to access the file on disk. + /// Returns the syntax tree. + public SyntaxTree Parse(string program, string fileName = "") + { + return Parse(new StringTextSource(program), fileName); + } + + /// + /// Parses a C# code file. + /// + /// The text reader containing the source code to parse. + /// The file name. Used to identify the file (e.g. when building a type system). + /// This can be an arbitrary identifier, NRefactory never tries to access the file on disk. + /// Returns the syntax tree. + public SyntaxTree Parse(TextReader reader, string fileName = "") + { + return Parse(new StringTextSource(reader.ReadToEnd()), fileName); + } + + /// + /// Converts a Mono.CSharp syntax tree into an NRefactory syntax tree. + /// + public SyntaxTree Parse(CompilerCompilationUnit top, string fileName) + { + if (top == null) { + return null; + } + CSharpParser.ConversionVisitor conversionVisitor = new ConversionVisitor(GenerateTypeSystemMode, top.LocationsBag); + top.ModuleCompiled.Accept(conversionVisitor); + InsertComments(top, conversionVisitor); + if (CompilationUnitCallback != null) { + CompilationUnitCallback(top); + } + var expr = top.LastYYValue as Mono.CSharp.Expression; + if (expr != null) + conversionVisitor.Unit.TopExpression = expr.Accept(conversionVisitor) as AstNode; + + conversionVisitor.Unit.FileName = fileName; + var conditionals = new List(); + foreach (var settings in compilerSettings.ConditionalSymbols) { + if (top.Conditionals.ContainsKey(settings) && !top.Conditionals [settings]) + continue; + conditionals.Add(settings); + } + foreach (var kv in top.Conditionals) { + if (!kv.Value || compilerSettings.ConditionalSymbols.Contains(kv.Key)) + continue; + conditionals.Add(kv.Key); + } + conversionVisitor.Unit.ConditionalSymbols = conditionals; + return conversionVisitor.Unit; + } + + public CompilerSettings CompilerSettings { + get { return compilerSettings; } + set { + if (value == null) + throw new ArgumentNullException(); + compilerSettings = value; + } + } + + /// + /// Callback that gets called with the Mono.CSharp syntax tree whenever some code is parsed. + /// + public Action CompilationUnitCallback { + get; + set; + } + + /// + /// Specifies whether to run the parser in a special mode for generating the type system. + /// If this property is true, the syntax tree will only contain nodes relevant for the + /// call and might be missing other nodes (e.g. method bodies). + /// The default is false. + /// + public bool GenerateTypeSystemMode { + get; + set; + } + + TextLocation initialLocation = new TextLocation(1, 1); + + /// + /// Specifies the text location where parsing starts. + /// This property can be used when parsing a part of a file to make the locations of the AstNodes + /// refer to the position in the whole file. + /// The default is (1,1). + /// + public TextLocation InitialLocation { + get { return initialLocation; } + set { initialLocation = value; } + } + + internal static object parseLock = new object(); + + /// + /// Parses a C# code file. + /// + /// The stream containing the source code to parse. + /// The file name. Used to identify the file (e.g. when building a type system). + /// This can be an arbitrary identifier, NRefactory never tries to access the file on disk. + /// Returns the syntax tree. + public SyntaxTree Parse(Stream stream, string fileName = "") + { + return Parse(new StreamReader(stream), fileName); + } + + /// + /// Parses a C# code file. + /// + /// The source code to parse. + /// The file name. Used to identify the file (e.g. when building a type system). + /// This can be an arbitrary identifier, NRefactory never tries to access the file on disk. + /// Returns the syntax tree. + public SyntaxTree Parse(ITextSource program, string fileName = "") + { + return Parse(program, fileName, initialLocation.Line, initialLocation.Column); + } + + SyntaxTree Parse(ITextSource program, string fileName, int initialLine, int initialColumn) + { + lock (parseLock) { + errorReportPrinter = new ErrorReportPrinter(""); + var ctx = new CompilerContext(compilerSettings.ToMono(), errorReportPrinter); + ctx.Settings.TabSize = 1; + var reader = new SeekableStreamReader(program); + var file = new SourceFile(fileName, fileName, 0); + Location.Initialize(new List(new [] { file })); + var module = new ModuleContainer(ctx); + var session = new ParserSession(); + session.LocationsBag = new LocationsBag(); + var report = new Report(ctx, errorReportPrinter); + var parser = Driver.Parse(reader, file, module, session, report, initialLine - 1, initialColumn - 1); + var top = new CompilerCompilationUnit { + ModuleCompiled = module, + LocationsBag = session.LocationsBag, + SpecialsBag = parser.Lexer.sbag, + Conditionals = parser.Lexer.SourceFile.Conditionals + }; + var unit = Parse(top, fileName); + unit.Errors.AddRange(errorReportPrinter.Errors); + CompilerCallableEntryPoint.Reset(); + return unit; + } + } + + public IEnumerable ParseTypeMembers(string code) + { + return ParseTypeMembers(code, initialLocation.Line, initialLocation.Column); + } + + IEnumerable ParseTypeMembers(string code, int initialLine, int initialColumn) + { + const string prefix = "unsafe partial class MyClass { "; + var syntaxTree = Parse(new StringTextSource(prefix + code + "}"), "parsed.cs", initialLine, initialColumn - prefix.Length); + if (syntaxTree == null) + return Enumerable.Empty(); + var td = syntaxTree.FirstChild as TypeDeclaration; + if (td != null) { + var members = td.Members.ToArray(); + // detach members from parent + foreach (var m in members) + m.Remove(); + return members; + } + return Enumerable.Empty(); + } + + public IEnumerable ParseStatements(string code) + { + return ParseStatements(code, initialLocation.Line, initialLocation.Column); + } + + IEnumerable ParseStatements(string code, int initialLine, int initialColumn) + { + // the dummy method is async so that 'await' expressions are parsed as expected + const string prefix = "async void M() { "; + var members = ParseTypeMembers(prefix + code + "}", initialLine, initialColumn - prefix.Length); + var method = members.FirstOrDefault() as MethodDeclaration; + if (method != null && method.Body != null) { + var statements = method.Body.Statements.ToArray(); + // detach statements from parent + foreach (var st in statements) + st.Remove(); + return statements; + } + return Enumerable.Empty(); + } + + public AstType ParseTypeReference(string code) + { + var members = ParseTypeMembers(code + " a;"); + var field = members.FirstOrDefault() as FieldDeclaration; + if (field != null) { + AstType type = field.ReturnType; + type.Remove(); + return type; + } + return AstType.Null; + } + + public Expression ParseExpression(string code) + { + const string prefix = "tmp = "; + var statements = ParseStatements(prefix + code + ";", initialLocation.Line, initialLocation.Column - prefix.Length); + var es = statements.FirstOrDefault() as ExpressionStatement; + if (es != null) { + var ae = es.Expression as AssignmentExpression; + if (ae != null) { + Expression expr = ae.Right; + expr.Remove(); + return expr; + } + } + return Expression.Null; + } + /* + /// + /// Parses a file snippet; guessing what the code snippet represents (whole file, type members, block, type reference, expression). + /// + public AstNode ParseSnippet (string code) + { + // TODO: add support for parsing a part of a file + throw new NotImplementedException (); + } + */ + public DocumentationReference ParseDocumentationReference(string cref) + { + // see Mono.CSharp.DocumentationBuilder.HandleXrefCommon + if (cref == null) + throw new ArgumentNullException("cref"); + + // Additional symbols for < and > are allowed for easier XML typing + cref = cref.Replace('{', '<').Replace('}', '>'); + + lock (parseLock) { + errorReportPrinter = new ErrorReportPrinter(""); + var ctx = new CompilerContext(compilerSettings.ToMono(), errorReportPrinter); + ctx.Settings.TabSize = 1; + var reader = new SeekableStreamReader(new StringTextSource(cref)); + var file = new SourceFile("", "", 0); + Location.Initialize(new List(new [] { file })); + var module = new ModuleContainer(ctx); + module.DocumentationBuilder = new DocumentationBuilder(module); + var source_file = new CompilationSourceFile(module); + var report = new Report(ctx, errorReportPrinter); + var session = new ParserSession(); + session.LocationsBag = new LocationsBag(); + var parser = new Mono.CSharp.CSharpParser(reader, source_file, report, session); + parser.Lexer.Line += initialLocation.Line - 1; + parser.Lexer.Column += initialLocation.Column - 1; + parser.Lexer.putback_char = Tokenizer.DocumentationXref; + parser.Lexer.parsing_generic_declaration_doc = true; + parser.parse(); + if (report.Errors > 0) { +// Report.Warning (1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'", +// mc.GetSignatureForError (), cref); + } + + var conversionVisitor = new ConversionVisitor(false, session.LocationsBag); + var docRef = conversionVisitor.ConvertXmlDoc(module.DocumentationBuilder); + CompilerCallableEntryPoint.Reset(); + return docRef; + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CompilerSettings.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CompilerSettings.cs new file mode 100644 index 000000000..1146ff85e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CompilerSettings.cs @@ -0,0 +1,151 @@ +// Copyright (c) 2010-2013 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 ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// C# compiler settings. + /// + [Serializable] + public class CompilerSettings : AbstractFreezable + { + protected override void FreezeInternal() + { + conditionalSymbols = FreezableHelper.FreezeList(conditionalSymbols); + specificWarningsAsErrors = FreezableHelper.FreezeList(specificWarningsAsErrors); + disabledWarnings = FreezableHelper.FreezeList(disabledWarnings); + base.FreezeInternal(); + } + + /// + /// Creates a new CompilerSettings instance. + /// + public CompilerSettings() + { + } + + bool allowUnsafeBlocks = true; + + /// + /// Gets/Sets whether unsafe code is allowed. + /// The default is true. If set to false, parsing unsafe code will result in parser errors. + /// + public bool AllowUnsafeBlocks { + get { return allowUnsafeBlocks; } + set { + FreezableHelper.ThrowIfFrozen(this); + allowUnsafeBlocks = value; + } + } + + bool checkForOverflow; + + /// + /// Gets/Sets whether overflow checking is enabled. + /// The default is false. This setting effects semantic analysis. + /// + public bool CheckForOverflow { + get { return checkForOverflow; } + set { checkForOverflow = value; } + } + + Version languageVersion = new Version((int)Mono.CSharp.LanguageVersion.Default, 0); + + /// + /// Gets/Sets the language version used by the parser. + /// Using language constructs newer than the supplied version will result in parser errors. + /// + public Version LanguageVersion { + get { return languageVersion; } + set { + FreezableHelper.ThrowIfFrozen(this); + if (value == null) + throw new ArgumentNullException(); + languageVersion = value; + } + } + + IList conditionalSymbols = new List(); + + /// + /// Gets/Sets the list of conditional symbols that are defined project-wide. + /// + public IList ConditionalSymbols { + get { return conditionalSymbols; } + } + + bool treatWarningsAsErrors; + + public bool TreatWarningsAsErrors { + get { return treatWarningsAsErrors; } + set { + FreezableHelper.ThrowIfFrozen(this); + treatWarningsAsErrors = value; + } + } + + IList specificWarningsAsErrors = new List(); + + /// + /// Allows treating specific warnings as errors without setting to true. + /// + public IList SpecificWarningsAsErrors { + get { return specificWarningsAsErrors; } + } + + int warningLevel = 4; + + public int WarningLevel { + get { return warningLevel; } + set { + FreezableHelper.ThrowIfFrozen(this); + warningLevel = value; + } + } + + IList disabledWarnings = new List(); + + /// + /// Disables the specified warnings. + /// + public IList DisabledWarnings { + get { return disabledWarnings; } + } + + internal Mono.CSharp.CompilerSettings ToMono() + { + var s = new Mono.CSharp.CompilerSettings(); + s.Unsafe = allowUnsafeBlocks; + s.Checked = checkForOverflow; + s.Version = (Mono.CSharp.LanguageVersion)languageVersion.Major; + s.WarningsAreErrors = treatWarningsAsErrors; + s.WarningLevel = warningLevel; + foreach (int code in disabledWarnings) + s.SetIgnoreWarning(code); + foreach (int code in specificWarningsAsErrors) + s.AddWarningAsError(code); + foreach (string sym in conditionalSymbols) + s.AddConditionalSymbol(sym); + return s; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/SeekableStreamReader.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/SeekableStreamReader.cs new file mode 100644 index 000000000..5a853c54e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/SeekableStreamReader.cs @@ -0,0 +1,103 @@ +// +// SeekableStreamReader.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.Editor; +using System.IO; +using System.Text; + +namespace Mono.CSharp +{ + public class SeekableStreamReader : IDisposable + { + public const int DefaultReadAheadSize = 2048; + + readonly ITextSource textSource; + + int pos; + + static string GetAllText(Stream stream, Encoding encoding) { + using (var rdr = new StreamReader(stream, encoding)) { + return rdr.ReadToEnd(); + } + } + + public SeekableStreamReader (Stream stream, Encoding encoding, char[] sharedBuffer = null) : this(new StringTextSource(GetAllText(stream, encoding))) + { + } + + public SeekableStreamReader (ITextSource source) + { + this.textSource = source; + } + + + public void Dispose () + { + } + + /// + /// This value corresponds to the current position in a stream of characters. + /// The StreamReader hides its manipulation of the underlying byte stream and all + /// character set/decoding issues. Thus, we cannot use this position to guess at + /// the corresponding position in the underlying byte stream even though there is + /// a correlation between them. + /// + public int Position { + get { + return pos; + } + + set { + pos = value; + } + } + + public char GetChar (int position) + { + return textSource.GetCharAt (position); + } + + public char[] ReadChars (int fromPosition, int toPosition) + { + return textSource.GetText (fromPosition, toPosition - fromPosition).ToCharArray (); + } + + public int Peek () + { + if (pos >= textSource.TextLength) + return -1; + return textSource.GetCharAt (pos); + } + + public int Read () + { + if (pos >= textSource.TextLength) + return -1; + return textSource.GetCharAt (pos++); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/CryptoConvert.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/CryptoConvert.cs new file mode 100644 index 000000000..a56e94d01 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/CryptoConvert.cs @@ -0,0 +1,754 @@ +// +// CryptoConvert.cs - Crypto Convertion Routines +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com) +// +// 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.Globalization; +using System.Security.Cryptography; +using System.Text; + +namespace Mono.Security.Cryptography { + +#if INSIDE_CORLIB + internal +#else + public +#endif + sealed class CryptoConvert { + + private CryptoConvert () + { + } + + static private int ToInt32LE (byte [] bytes, int offset) + { + return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]; + } + + static private uint ToUInt32LE (byte [] bytes, int offset) + { + return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]); + } + + static private byte [] GetBytesLE (int val) + { + return new byte [] { + (byte) (val & 0xff), + (byte) ((val >> 8) & 0xff), + (byte) ((val >> 16) & 0xff), + (byte) ((val >> 24) & 0xff) + }; + } + + static private byte[] Trim (byte[] array) + { + for (int i=0; i < array.Length; i++) { + if (array [i] != 0x00) { + byte[] result = new byte [array.Length - i]; + Buffer.BlockCopy (array, i, result, 0, result.Length); + return result; + } + } + return null; + } + + // convert the key from PRIVATEKEYBLOB to RSA + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp + // e.g. SNK files, PVK files + static public RSA FromCapiPrivateKeyBlob (byte[] blob) + { + return FromCapiPrivateKeyBlob (blob, 0); + } + + static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + RSAParameters rsap = new RSAParameters (); + try { + if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) + (blob [offset+1] != 0x02) || // Version (0x02) + (blob [offset+2] != 0x00) || // Reserved (word) + (blob [offset+3] != 0x00) || + (ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset+12); + + // DWORD public exponent + byte[] exp = new byte [4]; + Buffer.BlockCopy (blob, offset+16, exp, 0, 4); + Array.Reverse (exp); + rsap.Exponent = Trim (exp); + + int pos = offset+20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); + pos += byteLen; + + // BYTE prime1[rsapubkey.bitlen/16]; + int byteHalfLen = (byteLen >> 1); + rsap.P = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen); + Array.Reverse (rsap.P); + pos += byteHalfLen; + + // BYTE prime2[rsapubkey.bitlen/16]; + rsap.Q = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen); + Array.Reverse (rsap.Q); + pos += byteHalfLen; + + // BYTE exponent1[rsapubkey.bitlen/16]; + rsap.DP = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen); + Array.Reverse (rsap.DP); + pos += byteHalfLen; + + // BYTE exponent2[rsapubkey.bitlen/16]; + rsap.DQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen); + Array.Reverse (rsap.DQ); + pos += byteHalfLen; + + // BYTE coefficient[rsapubkey.bitlen/16]; + rsap.InverseQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen); + Array.Reverse (rsap.InverseQ); + pos += byteHalfLen; + + // ok, this is hackish but CryptoAPI support it so... + // note: only works because CRT is used by default + // http://bugzilla.ximian.com/show_bug.cgi?id=57941 + rsap.D = new byte [byteLen]; // must be allocated + if (pos + byteLen + offset <= blob.Length) { + // BYTE privateExponent[rsapubkey.bitlen/8]; + Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen); + Array.Reverse (rsap.D); + } + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + +#if NET_2_1 + RSA rsa = RSA.Create (); + rsa.ImportParameters (rsap); +#else + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (rsap); + } + catch (CryptographicException ce) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + try { + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (rsap); + } + catch { + // rethrow original, not the later, exception if this fails + throw ce; + } + } +#endif + return rsa; + } + + static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob) + { + return FromCapiPrivateKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + DSAParameters dsap = new DSAParameters (); + try { + if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) + (blob [offset + 1] != 0x02) || // Version (0x02) + (blob [offset + 2] != 0x00) || // Reserved (word) + (blob [offset + 3] != 0x00) || + (ToUInt32LE (blob, offset + 8) != 0x32535344)) // DWORD magic + throw new CryptographicException ("Invalid blob header"); + + int bitlen = ToInt32LE (blob, offset + 12); + int bytelen = bitlen >> 3; + int pos = offset + 16; + + dsap.P = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); + Array.Reverse (dsap.P); + pos += bytelen; + + dsap.Q = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); + Array.Reverse (dsap.Q); + pos += 20; + + dsap.G = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); + Array.Reverse (dsap.G); + pos += bytelen; + + dsap.X = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.X, 0, 20); + Array.Reverse (dsap.X); + pos += 20; + + dsap.Counter = ToInt32LE (blob, pos); + pos += 4; + + dsap.Seed = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); + Array.Reverse (dsap.Seed); + pos += 20; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + +#if NET_2_1 + DSA dsa = (DSA)DSA.Create (); + dsa.ImportParameters (dsap); +#else + DSA dsa = null; + try { + dsa = (DSA)DSA.Create (); + dsa.ImportParameters (dsap); + } + catch (CryptographicException ce) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + try { + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + dsa = new DSACryptoServiceProvider (csp); + dsa.ImportParameters (dsap); + } + catch { + // rethrow original, not the later, exception if this fails + throw ce; + } + } +#endif + return dsa; + } + + static public byte[] ToCapiPrivateKeyBlob (RSA rsa) + { + RSAParameters p = rsa.ExportParameters (true); + int keyLength = p.Modulus.Length; // in bytes + byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)]; + + blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) + blob [8] = 0x52; // Magic - RSA2 (ASCII in hex) + blob [9] = 0x53; + blob [10] = 0x41; + blob [11] = 0x32; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; // bitlen + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + // public exponent (DWORD) + int pos = 16; + int n = p.Exponent.Length; + while (n > 0) + blob [pos++] = p.Exponent [--n]; + // modulus + pos = 20; + byte[] part = p.Modulus; + int len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + // private key + part = p.P; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.Q; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.DP; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.DQ; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.InverseQ; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.D; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + + return blob; + } + + static public byte[] ToCapiPrivateKeyBlob (DSA dsa) + { + DSAParameters p = dsa.ExportParameters (true); + int keyLength = p.P.Length; // in bytes + + // header + P + Q + G + X + count + seed + byte[] blob = new byte [16 + keyLength + 20 + keyLength + 20 + 4 + 20]; + + blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x22; // ALGID + blob [8] = 0x44; // Magic + blob [9] = 0x53; + blob [10] = 0x53; + blob [11] = 0x32; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + int pos = 16; + byte[] part = p.P; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Q; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + part = p.G; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.X; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); + pos += 4; + + part = p.Seed; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + + return blob; + } + + static public RSA FromCapiPublicKeyBlob (byte[] blob) + { + return FromCapiPublicKeyBlob (blob, 0); + } + + static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + try { + if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) + (blob [offset+1] != 0x02) || // Version (0x02) + (blob [offset+2] != 0x00) || // Reserved (word) + (blob [offset+3] != 0x00) || + (ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset+12); + + // DWORD public exponent + RSAParameters rsap = new RSAParameters (); + rsap.Exponent = new byte [3]; + rsap.Exponent [0] = blob [offset+18]; + rsap.Exponent [1] = blob [offset+17]; + rsap.Exponent [2] = blob [offset+16]; + + int pos = offset+20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); +#if NET_2_1 + RSA rsa = RSA.Create (); + rsa.ImportParameters (rsap); +#else + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (rsap); + } + catch (CryptographicException) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (rsap); + } +#endif + return rsa; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + } + + static public DSA FromCapiPublicKeyBlobDSA (byte[] blob) + { + return FromCapiPublicKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiPublicKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + try { + if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) + (blob [offset + 1] != 0x02) || // Version (0x02) + (blob [offset + 2] != 0x00) || // Reserved (word) + (blob [offset + 3] != 0x00) || + (ToUInt32LE (blob, offset + 8) != 0x31535344)) // DWORD magic + throw new CryptographicException ("Invalid blob header"); + + int bitlen = ToInt32LE (blob, offset + 12); + DSAParameters dsap = new DSAParameters (); + int bytelen = bitlen >> 3; + int pos = offset + 16; + + dsap.P = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); + Array.Reverse (dsap.P); + pos += bytelen; + + dsap.Q = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); + Array.Reverse (dsap.Q); + pos += 20; + + dsap.G = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); + Array.Reverse (dsap.G); + pos += bytelen; + + dsap.Y = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.Y, 0, bytelen); + Array.Reverse (dsap.Y); + pos += bytelen; + + dsap.Counter = ToInt32LE (blob, pos); + pos += 4; + + dsap.Seed = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); + Array.Reverse (dsap.Seed); + pos += 20; + + DSA dsa = (DSA)DSA.Create (); + dsa.ImportParameters (dsap); + return dsa; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + } + + static public byte[] ToCapiPublicKeyBlob (RSA rsa) + { + RSAParameters p = rsa.ExportParameters (false); + int keyLength = p.Modulus.Length; // in bytes + byte[] blob = new byte [20 + keyLength]; + + blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) + blob [8] = 0x52; // Magic - RSA1 (ASCII in hex) + blob [9] = 0x53; + blob [10] = 0x41; + blob [11] = 0x31; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; // bitlen + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + // public exponent (DWORD) + int pos = 16; + int n = p.Exponent.Length; + while (n > 0) + blob [pos++] = p.Exponent [--n]; + // modulus + pos = 20; + byte[] part = p.Modulus; + int len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + return blob; + } + + static public byte[] ToCapiPublicKeyBlob (DSA dsa) + { + DSAParameters p = dsa.ExportParameters (false); + int keyLength = p.P.Length; // in bytes + + // header + P + Q + G + Y + count + seed + byte[] blob = new byte [16 + keyLength + 20 + keyLength + keyLength + 4 + 20]; + + blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x22; // ALGID + blob [8] = 0x44; // Magic + blob [9] = 0x53; + blob [10] = 0x53; + blob [11] = 0x31; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + int pos = 16; + byte[] part; + + part = p.P; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Q; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + part = p.G; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Y; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); + pos += 4; + + part = p.Seed; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + + return blob; + } + + // PRIVATEKEYBLOB + // PUBLICKEYBLOB + static public RSA FromCapiKeyBlob (byte[] blob) + { + return FromCapiKeyBlob (blob, 0); + } + + static public RSA FromCapiKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + switch (blob [offset]) { + case 0x00: + // this could be a public key inside an header + // like "sn -e" would produce + if (blob [offset + 12] == 0x06) { + return FromCapiPublicKeyBlob (blob, offset + 12); + } + break; + case 0x06: + return FromCapiPublicKeyBlob (blob, offset); + case 0x07: + return FromCapiPrivateKeyBlob (blob, offset); + } + throw new CryptographicException ("Unknown blob format."); + } + + static public DSA FromCapiKeyBlobDSA (byte[] blob) + { + return FromCapiKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + switch (blob [offset]) { + case 0x06: + return FromCapiPublicKeyBlobDSA (blob, offset); + case 0x07: + return FromCapiPrivateKeyBlobDSA (blob, offset); + } + throw new CryptographicException ("Unknown blob format."); + } + + static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey) + { + if (keypair == null) + throw new ArgumentNullException ("keypair"); + + // check between RSA and DSA (and potentially others like DH) + if (keypair is RSA) + return ToCapiKeyBlob ((RSA)keypair, includePrivateKey); + else if (keypair is DSA) + return ToCapiKeyBlob ((DSA)keypair, includePrivateKey); + else + return null; // TODO + } + + static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey) + { + if (rsa == null) + throw new ArgumentNullException ("rsa"); + + if (includePrivateKey) + return ToCapiPrivateKeyBlob (rsa); + else + return ToCapiPublicKeyBlob (rsa); + } + + static public byte[] ToCapiKeyBlob (DSA dsa, bool includePrivateKey) + { + if (dsa == null) + throw new ArgumentNullException ("dsa"); + + if (includePrivateKey) + return ToCapiPrivateKeyBlob (dsa); + else + return ToCapiPublicKeyBlob (dsa); + } + + static public string ToHex (byte[] input) + { + if (input == null) + return null; + + StringBuilder sb = new StringBuilder (input.Length * 2); + foreach (byte b in input) { + sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture)); + } + return sb.ToString (); + } + + static private byte FromHexChar (char c) + { + if ((c >= 'a') && (c <= 'f')) + return (byte) (c - 'a' + 10); + if ((c >= 'A') && (c <= 'F')) + return (byte) (c - 'A' + 10); + if ((c >= '0') && (c <= '9')) + return (byte) (c - '0'); + throw new ArgumentException ("invalid hex char"); + } + + static public byte[] FromHex (string hex) + { + if (hex == null) + return null; + if ((hex.Length & 0x1) == 0x1) + throw new ArgumentException ("Length must be a multiple of 2"); + + byte[] result = new byte [hex.Length >> 1]; + int n = 0; + int i = 0; + while (n < result.Length) { + result [n] = (byte) (FromHexChar (hex [i++]) << 4); + result [n++] += FromHexChar (hex [i++]); + } + return result; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolFile.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolFile.cs new file mode 100644 index 000000000..8431c70a4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolFile.cs @@ -0,0 +1,637 @@ +// +// MonoSymbolFile.cs +// +// Authors: +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// (C) 2003 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com) +// +// +// 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.Reflection; +using System.Collections.Generic; +using System.IO; + +namespace Mono.CompilerServices.SymbolWriter +{ + public class MonoSymbolFileException : Exception + { + public MonoSymbolFileException () + : base () + { } + + public MonoSymbolFileException (string message, params object[] args) + : base (String.Format (message, args)) + { + } + + public MonoSymbolFileException (string message, Exception innerException) + : base (message, innerException) + { + } + } + + sealed class MyBinaryWriter : BinaryWriter + { + public MyBinaryWriter (Stream stream) + : base (stream) + { } + + public void WriteLeb128 (int value) + { + base.Write7BitEncodedInt (value); + } + } + + internal class MyBinaryReader : BinaryReader + { + public MyBinaryReader (Stream stream) + : base (stream) + { } + + public int ReadLeb128 () + { + return base.Read7BitEncodedInt (); + } + + public string ReadString (int offset) + { + long old_pos = BaseStream.Position; + BaseStream.Position = offset; + + string text = ReadString (); + + BaseStream.Position = old_pos; + return text; + } + } + + public interface ISourceFile + { + SourceFileEntry Entry { + get; + } + } + + public interface ICompileUnit + { + CompileUnitEntry Entry { + get; + } + } + + public interface IMethodDef + { + string Name { + get; + } + + int Token { + get; + } + } + + public class MonoSymbolFile : IDisposable + { + List methods = new List (); + List sources = new List (); + List comp_units = new List (); + Dictionary anonymous_scopes; + + OffsetTable ot; + int last_type_index; + int last_method_index; + int last_namespace_index; + + public readonly int MajorVersion = OffsetTable.MajorVersion; + public readonly int MinorVersion = OffsetTable.MinorVersion; + + public int NumLineNumbers; + + public MonoSymbolFile () + { + ot = new OffsetTable (); + } + + public int AddSource (SourceFileEntry source) + { + sources.Add (source); + return sources.Count; + } + + public int AddCompileUnit (CompileUnitEntry entry) + { + comp_units.Add (entry); + return comp_units.Count; + } + + public void AddMethod (MethodEntry entry) + { + methods.Add (entry); + } + + public MethodEntry DefineMethod (CompileUnitEntry comp_unit, int token, + ScopeVariable[] scope_vars, LocalVariableEntry[] locals, + LineNumberEntry[] lines, CodeBlockEntry[] code_blocks, + string real_name, MethodEntry.Flags flags, + int namespace_id) + { + if (reader != null) + throw new InvalidOperationException (); + + MethodEntry method = new MethodEntry ( + this, comp_unit, token, scope_vars, locals, lines, code_blocks, + real_name, flags, namespace_id); + AddMethod (method); + return method; + } + + internal void DefineAnonymousScope (int id) + { + if (reader != null) + throw new InvalidOperationException (); + + if (anonymous_scopes == null) + anonymous_scopes = new Dictionary (); + + anonymous_scopes.Add (id, new AnonymousScopeEntry (id)); + } + + internal void DefineCapturedVariable (int scope_id, string name, string captured_name, + CapturedVariable.CapturedKind kind) + { + if (reader != null) + throw new InvalidOperationException (); + + AnonymousScopeEntry scope = anonymous_scopes [scope_id]; + scope.AddCapturedVariable (name, captured_name, kind); + } + + internal void DefineCapturedScope (int scope_id, int id, string captured_name) + { + if (reader != null) + throw new InvalidOperationException (); + + AnonymousScopeEntry scope = anonymous_scopes [scope_id]; + scope.AddCapturedScope (id, captured_name); + } + + internal int GetNextTypeIndex () + { + return ++last_type_index; + } + + internal int GetNextMethodIndex () + { + return ++last_method_index; + } + + internal int GetNextNamespaceIndex () + { + return ++last_namespace_index; + } + + void Write (MyBinaryWriter bw, Guid guid) + { + // Magic number and file version. + bw.Write (OffsetTable.Magic); + bw.Write (MajorVersion); + bw.Write (MinorVersion); + + bw.Write (guid.ToByteArray ()); + + // + // Offsets of file sections; we must write this after we're done + // writing the whole file, so we just reserve the space for it here. + // + long offset_table_offset = bw.BaseStream.Position; + ot.Write (bw, MajorVersion, MinorVersion); + + // + // Sort the methods according to their tokens and update their index. + // + methods.Sort (); + for (int i = 0; i < methods.Count; i++) + methods [i].Index = i + 1; + + // + // Write data sections. + // + ot.DataSectionOffset = (int) bw.BaseStream.Position; + foreach (SourceFileEntry source in sources) + source.WriteData (bw); + foreach (CompileUnitEntry comp_unit in comp_units) + comp_unit.WriteData (bw); + foreach (MethodEntry method in methods) + method.WriteData (this, bw); + ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset; + + // + // Write the method index table. + // + ot.MethodTableOffset = (int) bw.BaseStream.Position; + for (int i = 0; i < methods.Count; i++) { + MethodEntry entry = methods [i]; + entry.Write (bw); + } + ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset; + + // + // Write source table. + // + ot.SourceTableOffset = (int) bw.BaseStream.Position; + for (int i = 0; i < sources.Count; i++) { + SourceFileEntry source = sources [i]; + source.Write (bw); + } + ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset; + + // + // Write compilation unit table. + // + ot.CompileUnitTableOffset = (int) bw.BaseStream.Position; + for (int i = 0; i < comp_units.Count; i++) { + CompileUnitEntry unit = comp_units [i]; + unit.Write (bw); + } + ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset; + + // + // Write anonymous scope table. + // + ot.AnonymousScopeCount = anonymous_scopes != null ? anonymous_scopes.Count : 0; + ot.AnonymousScopeTableOffset = (int) bw.BaseStream.Position; + if (anonymous_scopes != null) { + foreach (AnonymousScopeEntry scope in anonymous_scopes.Values) + scope.Write (bw); + } + ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset; + + // + // Fixup offset table. + // + ot.TypeCount = last_type_index; + ot.MethodCount = methods.Count; + ot.SourceCount = sources.Count; + ot.CompileUnitCount = comp_units.Count; + + // + // Write offset table. + // + ot.TotalFileSize = (int) bw.BaseStream.Position; + bw.Seek ((int) offset_table_offset, SeekOrigin.Begin); + ot.Write (bw, MajorVersion, MinorVersion); + bw.Seek (0, SeekOrigin.End); + +#if false + Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " + + "{3} methods.", NumLineNumbers, LineNumberSize, + ExtendedLineNumberSize, methods.Count); +#endif + } + + public void CreateSymbolFile (Guid guid, FileStream fs) + { + if (reader != null) + throw new InvalidOperationException (); + + Write (new MyBinaryWriter (fs), guid); + } + + MyBinaryReader reader; + Dictionary source_file_hash; + Dictionary compile_unit_hash; + + List method_list; + Dictionary method_token_hash; + Dictionary source_name_hash; + + Guid guid; + + MonoSymbolFile (Stream stream) + { + reader = new MyBinaryReader (stream); + + try { + long magic = reader.ReadInt64 (); + int major_version = reader.ReadInt32 (); + int minor_version = reader.ReadInt32 (); + + if (magic != OffsetTable.Magic) + throw new MonoSymbolFileException ("Symbol file is not a valid"); + if (major_version != OffsetTable.MajorVersion) + throw new MonoSymbolFileException ( + "Symbol file has version {0} but expected {1}", major_version, OffsetTable.MajorVersion); + if (minor_version != OffsetTable.MinorVersion) + throw new MonoSymbolFileException ("Symbol file has version {0}.{1} but expected {2}.{3}", + major_version, minor_version, + OffsetTable.MajorVersion, OffsetTable.MinorVersion); + + MajorVersion = major_version; + MinorVersion = minor_version; + guid = new Guid (reader.ReadBytes (16)); + + ot = new OffsetTable (reader, major_version, minor_version); + } catch (Exception e) { + throw new MonoSymbolFileException ("Cannot read symbol file", e); + } + + source_file_hash = new Dictionary (); + compile_unit_hash = new Dictionary (); + } + + public static MonoSymbolFile ReadSymbolFile (Assembly assembly) + { + string filename = assembly.Location; + string name = filename + ".mdb"; + + Module[] modules = assembly.GetModules (); + Guid assembly_guid = modules[0].ModuleVersionId; + + return ReadSymbolFile (name, assembly_guid); + } + + public static MonoSymbolFile ReadSymbolFile (string mdbFilename) + { + return ReadSymbolFile (new FileStream (mdbFilename, FileMode.Open, FileAccess.Read)); + } + + public static MonoSymbolFile ReadSymbolFile (string mdbFilename, Guid assemblyGuid) + { + var sf = ReadSymbolFile (mdbFilename); + if (assemblyGuid != sf.guid) + throw new MonoSymbolFileException ("Symbol file `{0}' does not match assembly", mdbFilename); + + return sf; + } + + public static MonoSymbolFile ReadSymbolFile (Stream stream) + { + return new MonoSymbolFile (stream); + } + + public int CompileUnitCount { + get { return ot.CompileUnitCount; } + } + + public int SourceCount { + get { return ot.SourceCount; } + } + + public int MethodCount { + get { return ot.MethodCount; } + } + + public int TypeCount { + get { return ot.TypeCount; } + } + + public int AnonymousScopeCount { + get { return ot.AnonymousScopeCount; } + } + + public int NamespaceCount { + get { return last_namespace_index; } + } + + public Guid Guid { + get { return guid; } + } + + public OffsetTable OffsetTable { + get { return ot; } + } + + internal int LineNumberCount = 0; + internal int LocalCount = 0; + internal int StringSize = 0; + + internal int LineNumberSize = 0; + internal int ExtendedLineNumberSize = 0; + + public SourceFileEntry GetSourceFile (int index) + { + if ((index < 1) || (index > ot.SourceCount)) + throw new ArgumentException (); + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + SourceFileEntry source; + if (source_file_hash.TryGetValue (index, out source)) + return source; + + long old_pos = reader.BaseStream.Position; + + reader.BaseStream.Position = ot.SourceTableOffset + + SourceFileEntry.Size * (index - 1); + source = new SourceFileEntry (this, reader); + source_file_hash.Add (index, source); + + reader.BaseStream.Position = old_pos; + return source; + } + } + + public SourceFileEntry[] Sources { + get { + if (reader == null) + throw new InvalidOperationException (); + + SourceFileEntry[] retval = new SourceFileEntry [SourceCount]; + for (int i = 0; i < SourceCount; i++) + retval [i] = GetSourceFile (i + 1); + return retval; + } + } + + public CompileUnitEntry GetCompileUnit (int index) + { + if ((index < 1) || (index > ot.CompileUnitCount)) + throw new ArgumentException (); + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + CompileUnitEntry unit; + if (compile_unit_hash.TryGetValue (index, out unit)) + return unit; + + long old_pos = reader.BaseStream.Position; + + reader.BaseStream.Position = ot.CompileUnitTableOffset + + CompileUnitEntry.Size * (index - 1); + unit = new CompileUnitEntry (this, reader); + compile_unit_hash.Add (index, unit); + + reader.BaseStream.Position = old_pos; + return unit; + } + } + + public CompileUnitEntry[] CompileUnits { + get { + if (reader == null) + throw new InvalidOperationException (); + + CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount]; + for (int i = 0; i < CompileUnitCount; i++) + retval [i] = GetCompileUnit (i + 1); + return retval; + } + } + + void read_methods () + { + lock (this) { + if (method_token_hash != null) + return; + + method_token_hash = new Dictionary (); + method_list = new List (); + + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = ot.MethodTableOffset; + + for (int i = 0; i < MethodCount; i++) { + MethodEntry entry = new MethodEntry (this, reader, i + 1); + method_token_hash.Add (entry.Token, entry); + method_list.Add (entry); + } + + reader.BaseStream.Position = old_pos; + } + } + + public MethodEntry GetMethodByToken (int token) + { + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + read_methods (); + MethodEntry me; + method_token_hash.TryGetValue (token, out me); + return me; + } + } + + public MethodEntry GetMethod (int index) + { + if ((index < 1) || (index > ot.MethodCount)) + throw new ArgumentException (); + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + read_methods (); + return method_list [index - 1]; + } + } + + public MethodEntry[] Methods { + get { + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + read_methods (); + MethodEntry[] retval = new MethodEntry [MethodCount]; + method_list.CopyTo (retval, 0); + return retval; + } + } + } + + public int FindSource (string file_name) + { + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + if (source_name_hash == null) { + source_name_hash = new Dictionary (); + + for (int i = 0; i < ot.SourceCount; i++) { + SourceFileEntry source = GetSourceFile (i + 1); + source_name_hash.Add (source.FileName, i); + } + } + + int value; + if (!source_name_hash.TryGetValue (file_name, out value)) + return -1; + return value; + } + } + + public AnonymousScopeEntry GetAnonymousScope (int id) + { + if (reader == null) + throw new InvalidOperationException (); + + AnonymousScopeEntry scope; + lock (this) { + if (anonymous_scopes != null) { + anonymous_scopes.TryGetValue (id, out scope); + return scope; + } + + anonymous_scopes = new Dictionary (); + reader.BaseStream.Position = ot.AnonymousScopeTableOffset; + for (int i = 0; i < ot.AnonymousScopeCount; i++) { + scope = new AnonymousScopeEntry (reader); + anonymous_scopes.Add (scope.ID, scope); + } + + return anonymous_scopes [id]; + } + } + + internal MyBinaryReader BinaryReader { + get { + if (reader == null) + throw new InvalidOperationException (); + + return reader; + } + } + + public void Dispose () + { + Dispose (true); + } + + protected virtual void Dispose (bool disposing) + { + if (disposing) { + if (reader != null) { + reader.Close (); + reader = null; + } + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolTable.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolTable.cs new file mode 100644 index 000000000..277f25a7f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolTable.cs @@ -0,0 +1,1437 @@ +// +// Mono.CSharp.Debugger/MonoSymbolTable.cs +// +// Author: +// Martin Baulig (martin@ximian.com) +// +// (C) 2002 Ximian, Inc. http://www.ximian.com +// + +// +// 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.Security.Cryptography; +using System.Collections.Generic; +using System.Text; +using System.IO; + +// +// Parts which are actually written into the symbol file are marked with +// +// #region This is actually written to the symbol file +// #endregion +// +// Please do not modify these regions without previously talking to me. +// +// All changes to the file format must be synchronized in several places: +// +// a) The fields in these regions (and their order) must match the actual +// contents of the symbol file. +// +// This helps people to understand the symbol file format without reading +// too much source code, ie. you look at the appropriate region and then +// you know what's actually in the file. +// +// It is also required to help me enforce b). +// +// b) The regions must be kept in sync with the unmanaged code in +// mono/metadata/debug-mono-symfile.h +// +// When making changes to the file format, you must also increase two version +// numbers: +// +// i) OffsetTable.Version in this file. +// ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h +// +// After doing so, recompile everything, including the debugger. Symbol files +// with different versions are incompatible to each other and the debugger and +// the runtime enfore this, so you need to recompile all your assemblies after +// changing the file format. +// + +namespace Mono.CompilerServices.SymbolWriter +{ + public class OffsetTable + { + public const int MajorVersion = 50; + public const int MinorVersion = 0; + public const long Magic = 0x45e82623fd7fa614; + + #region This is actually written to the symbol file + public int TotalFileSize; + public int DataSectionOffset; + public int DataSectionSize; + public int CompileUnitCount; + public int CompileUnitTableOffset; + public int CompileUnitTableSize; + public int SourceCount; + public int SourceTableOffset; + public int SourceTableSize; + public int MethodCount; + public int MethodTableOffset; + public int MethodTableSize; + public int TypeCount; + public int AnonymousScopeCount; + public int AnonymousScopeTableOffset; + public int AnonymousScopeTableSize; + + [Flags] + public enum Flags + { + IsAspxSource = 1, + WindowsFileNames = 2 + } + + public Flags FileFlags; + + public int LineNumberTable_LineBase = LineNumberTable.Default_LineBase; + public int LineNumberTable_LineRange = LineNumberTable.Default_LineRange; + public int LineNumberTable_OpcodeBase = LineNumberTable.Default_OpcodeBase; + #endregion + + internal OffsetTable () + { + int platform = (int) Environment.OSVersion.Platform; + if ((platform != 4) && (platform != 128)) + FileFlags |= Flags.WindowsFileNames; + } + + internal OffsetTable (BinaryReader reader, int major_version, int minor_version) + { + TotalFileSize = reader.ReadInt32 (); + DataSectionOffset = reader.ReadInt32 (); + DataSectionSize = reader.ReadInt32 (); + CompileUnitCount = reader.ReadInt32 (); + CompileUnitTableOffset = reader.ReadInt32 (); + CompileUnitTableSize = reader.ReadInt32 (); + SourceCount = reader.ReadInt32 (); + SourceTableOffset = reader.ReadInt32 (); + SourceTableSize = reader.ReadInt32 (); + MethodCount = reader.ReadInt32 (); + MethodTableOffset = reader.ReadInt32 (); + MethodTableSize = reader.ReadInt32 (); + TypeCount = reader.ReadInt32 (); + + AnonymousScopeCount = reader.ReadInt32 (); + AnonymousScopeTableOffset = reader.ReadInt32 (); + AnonymousScopeTableSize = reader.ReadInt32 (); + + LineNumberTable_LineBase = reader.ReadInt32 (); + LineNumberTable_LineRange = reader.ReadInt32 (); + LineNumberTable_OpcodeBase = reader.ReadInt32 (); + + FileFlags = (Flags) reader.ReadInt32 (); + } + + internal void Write (BinaryWriter bw, int major_version, int minor_version) + { + bw.Write (TotalFileSize); + bw.Write (DataSectionOffset); + bw.Write (DataSectionSize); + bw.Write (CompileUnitCount); + bw.Write (CompileUnitTableOffset); + bw.Write (CompileUnitTableSize); + bw.Write (SourceCount); + bw.Write (SourceTableOffset); + bw.Write (SourceTableSize); + bw.Write (MethodCount); + bw.Write (MethodTableOffset); + bw.Write (MethodTableSize); + bw.Write (TypeCount); + + bw.Write (AnonymousScopeCount); + bw.Write (AnonymousScopeTableOffset); + bw.Write (AnonymousScopeTableSize); + + bw.Write (LineNumberTable_LineBase); + bw.Write (LineNumberTable_LineRange); + bw.Write (LineNumberTable_OpcodeBase); + + bw.Write ((int) FileFlags); + } + + public override string ToString () + { + return String.Format ( + "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]", + TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount, + SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset, + MethodTableSize, TypeCount); + } + } + + public class LineNumberEntry + { + #region This is actually written to the symbol file + public readonly int Row; + public int Column; + public int EndRow, EndColumn; + public readonly int File; + public readonly int Offset; + public readonly bool IsHidden; // Obsolete is never used + #endregion + + public sealed class LocationComparer : IComparer + { + public static readonly LocationComparer Default = new LocationComparer (); + + public int Compare (LineNumberEntry l1, LineNumberEntry l2) + { + return l1.Row == l2.Row ? + l1.Column.CompareTo (l2.Column) : + l1.Row.CompareTo (l2.Row); + } + } + + public static readonly LineNumberEntry Null = new LineNumberEntry (0, 0, 0, 0); + + public LineNumberEntry (int file, int row, int column, int offset) + : this (file, row, offset, column, false) + { + } + + public LineNumberEntry (int file, int row, int offset) + : this (file, row, -1, offset, false) + { + } + + public LineNumberEntry (int file, int row, int column, int offset, bool is_hidden) + : this (file, row, column, -1, -1, offset, is_hidden) + { + } + + public LineNumberEntry (int file, int row, int column, int end_row, int end_column, int offset, bool is_hidden) + { + this.File = file; + this.Row = row; + this.Column = column; + this.EndRow = end_row; + this.EndColumn = end_column; + this.Offset = offset; + this.IsHidden = is_hidden; + } + + public override string ToString () + { + return String.Format ("[Line {0}:{1,2}-{3,4}:{5}]", File, Row, Column, EndRow, EndColumn, Offset); + } + } + + public class CodeBlockEntry + { + public int Index; + #region This is actually written to the symbol file + public int Parent; + public Type BlockType; + public int StartOffset; + public int EndOffset; + #endregion + + public enum Type { + Lexical = 1, + CompilerGenerated = 2, + IteratorBody = 3, + IteratorDispatcher = 4 + } + + public CodeBlockEntry (int index, int parent, Type type, int start_offset) + { + this.Index = index; + this.Parent = parent; + this.BlockType = type; + this.StartOffset = start_offset; + } + + internal CodeBlockEntry (int index, MyBinaryReader reader) + { + this.Index = index; + int type_flag = reader.ReadLeb128 (); + BlockType = (Type) (type_flag & 0x3f); + this.Parent = reader.ReadLeb128 (); + this.StartOffset = reader.ReadLeb128 (); + this.EndOffset = reader.ReadLeb128 (); + + /* Reserved for future extensions. */ + if ((type_flag & 0x40) != 0) { + int data_size = reader.ReadInt16 (); + reader.BaseStream.Position += data_size; + } + } + + public void Close (int end_offset) + { + this.EndOffset = end_offset; + } + + internal void Write (MyBinaryWriter bw) + { + bw.WriteLeb128 ((int) BlockType); + bw.WriteLeb128 (Parent); + bw.WriteLeb128 (StartOffset); + bw.WriteLeb128 (EndOffset); + } + + public override string ToString () + { + return String.Format ("[CodeBlock {0}:{1}:{2}:{3}:{4}]", + Index, Parent, BlockType, StartOffset, EndOffset); + } + } + + public struct LocalVariableEntry + { + #region This is actually written to the symbol file + public readonly int Index; + public readonly string Name; + public readonly int BlockIndex; + #endregion + + public LocalVariableEntry (int index, string name, int block) + { + this.Index = index; + this.Name = name; + this.BlockIndex = block; + } + + internal LocalVariableEntry (MonoSymbolFile file, MyBinaryReader reader) + { + Index = reader.ReadLeb128 (); + Name = reader.ReadString (); + BlockIndex = reader.ReadLeb128 (); + } + + internal void Write (MonoSymbolFile file, MyBinaryWriter bw) + { + bw.WriteLeb128 (Index); + bw.Write (Name); + bw.WriteLeb128 (BlockIndex); + } + + public override string ToString () + { + return String.Format ("[LocalVariable {0}:{1}:{2}]", + Name, Index, BlockIndex - 1); + } + } + + public struct CapturedVariable + { + #region This is actually written to the symbol file + public readonly string Name; + public readonly string CapturedName; + public readonly CapturedKind Kind; + #endregion + + public enum CapturedKind : byte + { + Local, + Parameter, + This + } + + public CapturedVariable (string name, string captured_name, + CapturedKind kind) + { + this.Name = name; + this.CapturedName = captured_name; + this.Kind = kind; + } + + internal CapturedVariable (MyBinaryReader reader) + { + Name = reader.ReadString (); + CapturedName = reader.ReadString (); + Kind = (CapturedKind) reader.ReadByte (); + } + + internal void Write (MyBinaryWriter bw) + { + bw.Write (Name); + bw.Write (CapturedName); + bw.Write ((byte) Kind); + } + + public override string ToString () + { + return String.Format ("[CapturedVariable {0}:{1}:{2}]", + Name, CapturedName, Kind); + } + } + + public struct CapturedScope + { + #region This is actually written to the symbol file + public readonly int Scope; + public readonly string CapturedName; + #endregion + + public CapturedScope (int scope, string captured_name) + { + this.Scope = scope; + this.CapturedName = captured_name; + } + + internal CapturedScope (MyBinaryReader reader) + { + Scope = reader.ReadLeb128 (); + CapturedName = reader.ReadString (); + } + + internal void Write (MyBinaryWriter bw) + { + bw.WriteLeb128 (Scope); + bw.Write (CapturedName); + } + + public override string ToString () + { + return String.Format ("[CapturedScope {0}:{1}]", + Scope, CapturedName); + } + } + + public struct ScopeVariable + { + #region This is actually written to the symbol file + public readonly int Scope; + public readonly int Index; + #endregion + + public ScopeVariable (int scope, int index) + { + this.Scope = scope; + this.Index = index; + } + + internal ScopeVariable (MyBinaryReader reader) + { + Scope = reader.ReadLeb128 (); + Index = reader.ReadLeb128 (); + } + + internal void Write (MyBinaryWriter bw) + { + bw.WriteLeb128 (Scope); + bw.WriteLeb128 (Index); + } + + public override string ToString () + { + return String.Format ("[ScopeVariable {0}:{1}]", Scope, Index); + } + } + + public class AnonymousScopeEntry + { + #region This is actually written to the symbol file + public readonly int ID; + #endregion + + List captured_vars = new List (); + List captured_scopes = new List (); + + public AnonymousScopeEntry (int id) + { + this.ID = id; + } + + internal AnonymousScopeEntry (MyBinaryReader reader) + { + ID = reader.ReadLeb128 (); + + int num_captured_vars = reader.ReadLeb128 (); + for (int i = 0; i < num_captured_vars; i++) + captured_vars.Add (new CapturedVariable (reader)); + + int num_captured_scopes = reader.ReadLeb128 (); + for (int i = 0; i < num_captured_scopes; i++) + captured_scopes.Add (new CapturedScope (reader)); + } + + internal void AddCapturedVariable (string name, string captured_name, + CapturedVariable.CapturedKind kind) + { + captured_vars.Add (new CapturedVariable (name, captured_name, kind)); + } + + public CapturedVariable[] CapturedVariables { + get { + CapturedVariable[] retval = new CapturedVariable [captured_vars.Count]; + captured_vars.CopyTo (retval, 0); + return retval; + } + } + + internal void AddCapturedScope (int scope, string captured_name) + { + captured_scopes.Add (new CapturedScope (scope, captured_name)); + } + + public CapturedScope[] CapturedScopes { + get { + CapturedScope[] retval = new CapturedScope [captured_scopes.Count]; + captured_scopes.CopyTo (retval, 0); + return retval; + } + } + + internal void Write (MyBinaryWriter bw) + { + bw.WriteLeb128 (ID); + + bw.WriteLeb128 (captured_vars.Count); + foreach (CapturedVariable cv in captured_vars) + cv.Write (bw); + + bw.WriteLeb128 (captured_scopes.Count); + foreach (CapturedScope cs in captured_scopes) + cs.Write (bw); + } + + public override string ToString () + { + return String.Format ("[AnonymousScope {0}]", ID); + } + } + + public class CompileUnitEntry : ICompileUnit + { + #region This is actually written to the symbol file + public readonly int Index; + int DataOffset; + #endregion + + MonoSymbolFile file; + SourceFileEntry source; + List include_files; + List namespaces; + + bool creating; + + public static int Size { + get { return 8; } + } + + CompileUnitEntry ICompileUnit.Entry { + get { return this; } + } + + public CompileUnitEntry (MonoSymbolFile file, SourceFileEntry source) + { + this.file = file; + this.source = source; + + this.Index = file.AddCompileUnit (this); + + creating = true; + namespaces = new List (); + } + + public void AddFile (SourceFileEntry file) + { + if (!creating) + throw new InvalidOperationException (); + + if (include_files == null) + include_files = new List (); + + include_files.Add (file); + } + + public SourceFileEntry SourceFile { + get { + if (creating) + return source; + + ReadData (); + return source; + } + } + + public int DefineNamespace (string name, string[] using_clauses, int parent) + { + if (!creating) + throw new InvalidOperationException (); + + int index = file.GetNextNamespaceIndex (); + NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent); + namespaces.Add (ns); + return index; + } + + internal void WriteData (MyBinaryWriter bw) + { + DataOffset = (int) bw.BaseStream.Position; + bw.WriteLeb128 (source.Index); + + int count_includes = include_files != null ? include_files.Count : 0; + bw.WriteLeb128 (count_includes); + if (include_files != null) { + foreach (SourceFileEntry entry in include_files) + bw.WriteLeb128 (entry.Index); + } + + bw.WriteLeb128 (namespaces.Count); + foreach (NamespaceEntry ns in namespaces) + ns.Write (file, bw); + } + + internal void Write (BinaryWriter bw) + { + bw.Write (Index); + bw.Write (DataOffset); + } + + internal CompileUnitEntry (MonoSymbolFile file, MyBinaryReader reader) + { + this.file = file; + + Index = reader.ReadInt32 (); + DataOffset = reader.ReadInt32 (); + } + + public void ReadAll () + { + ReadData (); + } + + void ReadData () + { + if (creating) + throw new InvalidOperationException (); + + lock (file) { + if (namespaces != null) + return; + + MyBinaryReader reader = file.BinaryReader; + int old_pos = (int) reader.BaseStream.Position; + + reader.BaseStream.Position = DataOffset; + + int source_idx = reader.ReadLeb128 (); + source = file.GetSourceFile (source_idx); + + int count_includes = reader.ReadLeb128 (); + if (count_includes > 0) { + include_files = new List (); + for (int i = 0; i < count_includes; i++) + include_files.Add (file.GetSourceFile (reader.ReadLeb128 ())); + } + + int count_ns = reader.ReadLeb128 (); + namespaces = new List (); + for (int i = 0; i < count_ns; i ++) + namespaces.Add (new NamespaceEntry (file, reader)); + + reader.BaseStream.Position = old_pos; + } + } + + public NamespaceEntry[] Namespaces { + get { + ReadData (); + NamespaceEntry[] retval = new NamespaceEntry [namespaces.Count]; + namespaces.CopyTo (retval, 0); + return retval; + } + } + + public SourceFileEntry[] IncludeFiles { + get { + ReadData (); + if (include_files == null) + return new SourceFileEntry [0]; + + SourceFileEntry[] retval = new SourceFileEntry [include_files.Count]; + include_files.CopyTo (retval, 0); + return retval; + } + } + } + + public class SourceFileEntry + { + #region This is actually written to the symbol file + public readonly int Index; + int DataOffset; + #endregion + + MonoSymbolFile file; + string file_name; + byte[] guid; + byte[] hash; + bool creating; + bool auto_generated; + + public static int Size { + get { return 8; } + } + + public SourceFileEntry (MonoSymbolFile file, string file_name) + { + this.file = file; + this.file_name = file_name; + this.Index = file.AddSource (this); + + creating = true; + } + + public SourceFileEntry (MonoSymbolFile file, string file_name, byte[] guid, byte[] checksum) + : this (file, file_name) + { + this.guid = guid; + this.hash = checksum; + } + + public byte[] Checksum { + get { + return hash; + } + } + + internal void WriteData (MyBinaryWriter bw) + { + DataOffset = (int) bw.BaseStream.Position; + bw.Write (file_name); + + if (guid == null) + guid = new byte[16]; + + if (hash == null) { + try { + using (FileStream fs = new FileStream (file_name, FileMode.Open, FileAccess.Read)) { + MD5 md5 = MD5.Create (); + hash = md5.ComputeHash (fs); + } + } catch { + hash = new byte [16]; + } + } + + bw.Write (guid); + bw.Write (hash); + bw.Write ((byte) (auto_generated ? 1 : 0)); + } + + internal void Write (BinaryWriter bw) + { + bw.Write (Index); + bw.Write (DataOffset); + } + + internal SourceFileEntry (MonoSymbolFile file, MyBinaryReader reader) + { + this.file = file; + + Index = reader.ReadInt32 (); + DataOffset = reader.ReadInt32 (); + + int old_pos = (int) reader.BaseStream.Position; + reader.BaseStream.Position = DataOffset; + + file_name = reader.ReadString (); + guid = reader.ReadBytes (16); + hash = reader.ReadBytes (16); + auto_generated = reader.ReadByte () == 1; + + reader.BaseStream.Position = old_pos; + } + + public string FileName { + get { return file_name; } + set { file_name = value; } + } + + public bool AutoGenerated { + get { return auto_generated; } + } + + public void SetAutoGenerated () + { + if (!creating) + throw new InvalidOperationException (); + + auto_generated = true; + file.OffsetTable.FileFlags |= OffsetTable.Flags.IsAspxSource; + } + + public bool CheckChecksum () + { + try { + using (FileStream fs = new FileStream (file_name, FileMode.Open)) { + MD5 md5 = MD5.Create (); + byte[] data = md5.ComputeHash (fs); + for (int i = 0; i < 16; i++) + if (data [i] != hash [i]) + return false; + return true; + } + } catch { + return false; + } + } + + public override string ToString () + { + return String.Format ("SourceFileEntry ({0}:{1})", Index, DataOffset); + } + } + + public class LineNumberTable + { + protected LineNumberEntry[] _line_numbers; + public LineNumberEntry[] LineNumbers { + get { return _line_numbers; } + } + + public readonly int LineBase; + public readonly int LineRange; + public readonly byte OpcodeBase; + public readonly int MaxAddressIncrement; + +#region Configurable constants + public const int Default_LineBase = -1; + public const int Default_LineRange = 8; + public const byte Default_OpcodeBase = 9; + +#endregion + + public const byte DW_LNS_copy = 1; + public const byte DW_LNS_advance_pc = 2; + public const byte DW_LNS_advance_line = 3; + public const byte DW_LNS_set_file = 4; + public const byte DW_LNS_const_add_pc = 8; + + public const byte DW_LNE_end_sequence = 1; + + // MONO extensions. + public const byte DW_LNE_MONO_negate_is_hidden = 0x40; + + internal const byte DW_LNE_MONO__extensions_start = 0x40; + internal const byte DW_LNE_MONO__extensions_end = 0x7f; + + protected LineNumberTable (MonoSymbolFile file) + { + this.LineBase = file.OffsetTable.LineNumberTable_LineBase; + this.LineRange = file.OffsetTable.LineNumberTable_LineRange; + this.OpcodeBase = (byte) file.OffsetTable.LineNumberTable_OpcodeBase; + this.MaxAddressIncrement = (255 - OpcodeBase) / LineRange; + } + + internal LineNumberTable (MonoSymbolFile file, LineNumberEntry[] lines) + : this (file) + { + this._line_numbers = lines; + } + + internal void Write (MonoSymbolFile file, MyBinaryWriter bw, bool hasColumnsInfo, bool hasEndInfo) + { + int start = (int) bw.BaseStream.Position; + + bool last_is_hidden = false; + int last_line = 1, last_offset = 0, last_file = 1; + for (int i = 0; i < LineNumbers.Length; i++) { + int line_inc = LineNumbers [i].Row - last_line; + int offset_inc = LineNumbers [i].Offset - last_offset; + + if (LineNumbers [i].File != last_file) { + bw.Write (DW_LNS_set_file); + bw.WriteLeb128 (LineNumbers [i].File); + last_file = LineNumbers [i].File; + } + + if (LineNumbers [i].IsHidden != last_is_hidden) { + bw.Write ((byte) 0); + bw.Write ((byte) 1); + bw.Write (DW_LNE_MONO_negate_is_hidden); + last_is_hidden = LineNumbers [i].IsHidden; + } + + if (offset_inc >= MaxAddressIncrement) { + if (offset_inc < 2 * MaxAddressIncrement) { + bw.Write (DW_LNS_const_add_pc); + offset_inc -= MaxAddressIncrement; + } else { + bw.Write (DW_LNS_advance_pc); + bw.WriteLeb128 (offset_inc); + offset_inc = 0; + } + } + + if ((line_inc < LineBase) || (line_inc >= LineBase + LineRange)) { + bw.Write (DW_LNS_advance_line); + bw.WriteLeb128 (line_inc); + if (offset_inc != 0) { + bw.Write (DW_LNS_advance_pc); + bw.WriteLeb128 (offset_inc); + } + bw.Write (DW_LNS_copy); + } else { + byte opcode; + opcode = (byte) (line_inc - LineBase + (LineRange * offset_inc) + + OpcodeBase); + bw.Write (opcode); + } + + last_line = LineNumbers [i].Row; + last_offset = LineNumbers [i].Offset; + } + + bw.Write ((byte) 0); + bw.Write ((byte) 1); + bw.Write (DW_LNE_end_sequence); + + if (hasColumnsInfo) { + for (int i = 0; i < LineNumbers.Length; i++) { + var ln = LineNumbers [i]; + if (ln.Row >= 0) + bw.WriteLeb128 (ln.Column); + } + } + + if (hasEndInfo) { + for (int i = 0; i < LineNumbers.Length; i++) { + var ln = LineNumbers [i]; + if (ln.EndRow == -1 || ln.EndColumn == -1 || ln.Row > ln.EndRow) { + bw.WriteLeb128 (0xffffff); + } else { + bw.WriteLeb128 (ln.EndRow - ln.Row); + bw.WriteLeb128 (ln.EndColumn); + } + } + } + + file.ExtendedLineNumberSize += (int) bw.BaseStream.Position - start; + } + + internal static LineNumberTable Read (MonoSymbolFile file, MyBinaryReader br, bool readColumnsInfo, bool readEndInfo) + { + LineNumberTable lnt = new LineNumberTable (file); + lnt.DoRead (file, br, readColumnsInfo, readEndInfo); + return lnt; + } + + void DoRead (MonoSymbolFile file, MyBinaryReader br, bool includesColumns, bool includesEnds) + { + var lines = new List (); + + bool is_hidden = false, modified = false; + int stm_line = 1, stm_offset = 0, stm_file = 1; + while (true) { + byte opcode = br.ReadByte (); + + if (opcode == 0) { + byte size = br.ReadByte (); + long end_pos = br.BaseStream.Position + size; + opcode = br.ReadByte (); + + if (opcode == DW_LNE_end_sequence) { + if (modified) + lines.Add (new LineNumberEntry ( + stm_file, stm_line, -1, stm_offset, is_hidden)); + break; + } else if (opcode == DW_LNE_MONO_negate_is_hidden) { + is_hidden = !is_hidden; + modified = true; + } else if ((opcode >= DW_LNE_MONO__extensions_start) && + (opcode <= DW_LNE_MONO__extensions_end)) { + ; // reserved for future extensions + } else { + throw new MonoSymbolFileException ("Unknown extended opcode {0:x}", opcode); + } + + br.BaseStream.Position = end_pos; + continue; + } else if (opcode < OpcodeBase) { + switch (opcode) { + case DW_LNS_copy: + lines.Add (new LineNumberEntry ( + stm_file, stm_line, -1, stm_offset, is_hidden)); + modified = false; + break; + case DW_LNS_advance_pc: + stm_offset += br.ReadLeb128 (); + modified = true; + break; + case DW_LNS_advance_line: + stm_line += br.ReadLeb128 (); + modified = true; + break; + case DW_LNS_set_file: + stm_file = br.ReadLeb128 (); + modified = true; + break; + case DW_LNS_const_add_pc: + stm_offset += MaxAddressIncrement; + modified = true; + break; + default: + throw new MonoSymbolFileException ( + "Unknown standard opcode {0:x} in LNT", + opcode); + } + } else { + opcode -= OpcodeBase; + + stm_offset += opcode / LineRange; + stm_line += LineBase + (opcode % LineRange); + lines.Add (new LineNumberEntry ( + stm_file, stm_line, -1, stm_offset, is_hidden)); + modified = false; + } + } + + _line_numbers = lines.ToArray (); + + if (includesColumns) { + for (int i = 0; i < _line_numbers.Length; ++i) { + var ln = _line_numbers[i]; + if (ln.Row >= 0) + ln.Column = br.ReadLeb128 (); + } + } + if (includesEnds) { + for (int i = 0; i < _line_numbers.Length; ++i) { + var ln = _line_numbers[i]; + + int row = br.ReadLeb128 (); + if (row == 0xffffff) { + ln.EndRow = -1; + ln.EndColumn = -1; + } else { + ln.EndRow = ln.Row + row; + ln.EndColumn = br.ReadLeb128 (); + } + } + } + } + + public bool GetMethodBounds (out LineNumberEntry start, out LineNumberEntry end) + { + if (_line_numbers.Length > 1) { + start = _line_numbers [0]; + end = _line_numbers [_line_numbers.Length - 1]; + return true; + } + + start = LineNumberEntry.Null; + end = LineNumberEntry.Null; + return false; + } + } + + public class MethodEntry : IComparable + { + #region This is actually written to the symbol file + public readonly int CompileUnitIndex; + public readonly int Token; + public readonly int NamespaceID; + + int DataOffset; + int LocalVariableTableOffset; + int LineNumberTableOffset; + int CodeBlockTableOffset; + int ScopeVariableTableOffset; + int RealNameOffset; + Flags flags; + #endregion + + int index; + + public Flags MethodFlags { + get { return flags; } + } + + public readonly CompileUnitEntry CompileUnit; + + LocalVariableEntry[] locals; + CodeBlockEntry[] code_blocks; + ScopeVariable[] scope_vars; + LineNumberTable lnt; + string real_name; + + public readonly MonoSymbolFile SymbolFile; + + public int Index { + get { return index; } + set { index = value; } + } + + [Flags] + public enum Flags + { + LocalNamesAmbiguous = 1, + ColumnsInfoIncluded = 1 << 1, + EndInfoIncluded = 1 << 2 + } + + public const int Size = 12; + + internal MethodEntry (MonoSymbolFile file, MyBinaryReader reader, int index) + { + this.SymbolFile = file; + this.index = index; + + Token = reader.ReadInt32 (); + DataOffset = reader.ReadInt32 (); + LineNumberTableOffset = reader.ReadInt32 (); + + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = DataOffset; + + CompileUnitIndex = reader.ReadLeb128 (); + LocalVariableTableOffset = reader.ReadLeb128 (); + NamespaceID = reader.ReadLeb128 (); + + CodeBlockTableOffset = reader.ReadLeb128 (); + ScopeVariableTableOffset = reader.ReadLeb128 (); + + RealNameOffset = reader.ReadLeb128 (); + + flags = (Flags) reader.ReadLeb128 (); + + reader.BaseStream.Position = old_pos; + + CompileUnit = file.GetCompileUnit (CompileUnitIndex); + } + + internal MethodEntry (MonoSymbolFile file, CompileUnitEntry comp_unit, + int token, ScopeVariable[] scope_vars, + LocalVariableEntry[] locals, LineNumberEntry[] lines, + CodeBlockEntry[] code_blocks, string real_name, + Flags flags, int namespace_id) + { + this.SymbolFile = file; + this.real_name = real_name; + this.locals = locals; + this.code_blocks = code_blocks; + this.scope_vars = scope_vars; + this.flags = flags; + + index = -1; + + Token = token; + CompileUnitIndex = comp_unit.Index; + CompileUnit = comp_unit; + NamespaceID = namespace_id; + + CheckLineNumberTable (lines); + lnt = new LineNumberTable (file, lines); + file.NumLineNumbers += lines.Length; + + int num_locals = locals != null ? locals.Length : 0; + + if (num_locals <= 32) { + // Most of the time, the O(n^2) factor is actually + // less than the cost of allocating the hash table, + // 32 is a rough number obtained through some testing. + + for (int i = 0; i < num_locals; i ++) { + string nm = locals [i].Name; + + for (int j = i + 1; j < num_locals; j ++) { + if (locals [j].Name == nm) { + flags |= Flags.LocalNamesAmbiguous; + goto locals_check_done; + } + } + } + locals_check_done : + ; + } else { + var local_names = new Dictionary (); + foreach (LocalVariableEntry local in locals) { + if (local_names.ContainsKey (local.Name)) { + flags |= Flags.LocalNamesAmbiguous; + break; + } + local_names.Add (local.Name, local); + } + } + } + + static void CheckLineNumberTable (LineNumberEntry[] line_numbers) + { + int last_offset = -1; + int last_row = -1; + + if (line_numbers == null) + return; + + for (int i = 0; i < line_numbers.Length; i++) { + LineNumberEntry line = line_numbers [i]; + + if (line.Equals (LineNumberEntry.Null)) + throw new MonoSymbolFileException (); + + if (line.Offset < last_offset) + throw new MonoSymbolFileException (); + + if (line.Offset > last_offset) { + last_row = line.Row; + last_offset = line.Offset; + } else if (line.Row > last_row) { + last_row = line.Row; + } + } + } + + internal void Write (MyBinaryWriter bw) + { + if ((index <= 0) || (DataOffset == 0)) + throw new InvalidOperationException (); + + bw.Write (Token); + bw.Write (DataOffset); + bw.Write (LineNumberTableOffset); + } + + internal void WriteData (MonoSymbolFile file, MyBinaryWriter bw) + { + if (index <= 0) + throw new InvalidOperationException (); + + LocalVariableTableOffset = (int) bw.BaseStream.Position; + int num_locals = locals != null ? locals.Length : 0; + bw.WriteLeb128 (num_locals); + for (int i = 0; i < num_locals; i++) + locals [i].Write (file, bw); + file.LocalCount += num_locals; + + CodeBlockTableOffset = (int) bw.BaseStream.Position; + int num_code_blocks = code_blocks != null ? code_blocks.Length : 0; + bw.WriteLeb128 (num_code_blocks); + for (int i = 0; i < num_code_blocks; i++) + code_blocks [i].Write (bw); + + ScopeVariableTableOffset = (int) bw.BaseStream.Position; + int num_scope_vars = scope_vars != null ? scope_vars.Length : 0; + bw.WriteLeb128 (num_scope_vars); + for (int i = 0; i < num_scope_vars; i++) + scope_vars [i].Write (bw); + + if (real_name != null) { + RealNameOffset = (int) bw.BaseStream.Position; + bw.Write (real_name); + } + + foreach (var lne in lnt.LineNumbers) { + if (lne.EndRow != -1 || lne.EndColumn != -1) + flags |= Flags.EndInfoIncluded; + } + + LineNumberTableOffset = (int) bw.BaseStream.Position; + lnt.Write (file, bw, (flags & Flags.ColumnsInfoIncluded) != 0, (flags & Flags.EndInfoIncluded) != 0); + + DataOffset = (int) bw.BaseStream.Position; + + bw.WriteLeb128 (CompileUnitIndex); + bw.WriteLeb128 (LocalVariableTableOffset); + bw.WriteLeb128 (NamespaceID); + + bw.WriteLeb128 (CodeBlockTableOffset); + bw.WriteLeb128 (ScopeVariableTableOffset); + + bw.WriteLeb128 (RealNameOffset); + bw.WriteLeb128 ((int) flags); + } + + public void ReadAll () + { + GetLineNumberTable (); + GetLocals (); + GetCodeBlocks (); + GetScopeVariables (); + GetRealName (); + } + + public LineNumberTable GetLineNumberTable () + { + lock (SymbolFile) { + if (lnt != null) + return lnt; + + if (LineNumberTableOffset == 0) + return null; + + MyBinaryReader reader = SymbolFile.BinaryReader; + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = LineNumberTableOffset; + + lnt = LineNumberTable.Read (SymbolFile, reader, (flags & Flags.ColumnsInfoIncluded) != 0, (flags & Flags.EndInfoIncluded) != 0); + + reader.BaseStream.Position = old_pos; + return lnt; + } + } + + public LocalVariableEntry[] GetLocals () + { + lock (SymbolFile) { + if (locals != null) + return locals; + + if (LocalVariableTableOffset == 0) + return null; + + MyBinaryReader reader = SymbolFile.BinaryReader; + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = LocalVariableTableOffset; + + int num_locals = reader.ReadLeb128 (); + locals = new LocalVariableEntry [num_locals]; + + for (int i = 0; i < num_locals; i++) + locals [i] = new LocalVariableEntry (SymbolFile, reader); + + reader.BaseStream.Position = old_pos; + return locals; + } + } + + public CodeBlockEntry[] GetCodeBlocks () + { + lock (SymbolFile) { + if (code_blocks != null) + return code_blocks; + + if (CodeBlockTableOffset == 0) + return null; + + MyBinaryReader reader = SymbolFile.BinaryReader; + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = CodeBlockTableOffset; + + int num_code_blocks = reader.ReadLeb128 (); + code_blocks = new CodeBlockEntry [num_code_blocks]; + + for (int i = 0; i < num_code_blocks; i++) + code_blocks [i] = new CodeBlockEntry (i, reader); + + reader.BaseStream.Position = old_pos; + return code_blocks; + } + } + + public ScopeVariable[] GetScopeVariables () + { + lock (SymbolFile) { + if (scope_vars != null) + return scope_vars; + + if (ScopeVariableTableOffset == 0) + return null; + + MyBinaryReader reader = SymbolFile.BinaryReader; + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = ScopeVariableTableOffset; + + int num_scope_vars = reader.ReadLeb128 (); + scope_vars = new ScopeVariable [num_scope_vars]; + + for (int i = 0; i < num_scope_vars; i++) + scope_vars [i] = new ScopeVariable (reader); + + reader.BaseStream.Position = old_pos; + return scope_vars; + } + } + + public string GetRealName () + { + lock (SymbolFile) { + if (real_name != null) + return real_name; + + if (RealNameOffset == 0) + return null; + + real_name = SymbolFile.BinaryReader.ReadString (RealNameOffset); + return real_name; + } + } + + public int CompareTo (object obj) + { + MethodEntry method = (MethodEntry) obj; + + if (method.Token < Token) + return 1; + else if (method.Token > Token) + return -1; + else + return 0; + } + + public override string ToString () + { + return String.Format ("[Method {0}:{1:x}:{2}:{3}]", + index, Token, CompileUnitIndex, CompileUnit); + } + } + + public struct NamespaceEntry + { + #region This is actually written to the symbol file + public readonly string Name; + public readonly int Index; + public readonly int Parent; + public readonly string[] UsingClauses; + #endregion + + public NamespaceEntry (string name, int index, string[] using_clauses, int parent) + { + this.Name = name; + this.Index = index; + this.Parent = parent; + this.UsingClauses = using_clauses != null ? using_clauses : new string [0]; + } + + internal NamespaceEntry (MonoSymbolFile file, MyBinaryReader reader) + { + Name = reader.ReadString (); + Index = reader.ReadLeb128 (); + Parent = reader.ReadLeb128 (); + + int count = reader.ReadLeb128 (); + UsingClauses = new string [count]; + for (int i = 0; i < count; i++) + UsingClauses [i] = reader.ReadString (); + } + + internal void Write (MonoSymbolFile file, MyBinaryWriter bw) + { + bw.Write (Name); + bw.WriteLeb128 (Index); + bw.WriteLeb128 (Parent); + bw.WriteLeb128 (UsingClauses.Length); + foreach (string uc in UsingClauses) + bw.Write (uc); + } + + public override string ToString () + { + return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolWriter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolWriter.cs new file mode 100644 index 000000000..b2c2afdba --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolWriter.cs @@ -0,0 +1,238 @@ +// +// Mono.CSharp.Debugger/MonoSymbolWriter.cs +// +// Author: +// Martin Baulig (martin@ximian.com) +// +// This is the default implementation of the System.Diagnostics.SymbolStore.ISymbolWriter +// interface. +// +// (C) 2002 Ximian, Inc. http://www.ximian.com +// + +// +// 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.Runtime.CompilerServices; +using System.Collections.Generic; +using System.IO; + +namespace Mono.CompilerServices.SymbolWriter +{ + public class MonoSymbolWriter + { + List methods; + List sources; + List comp_units; + protected readonly MonoSymbolFile file; + string filename; + + private SourceMethodBuilder current_method; + Stack current_method_stack = new Stack (); + + public MonoSymbolWriter (string filename) + { + this.methods = new List (); + this.sources = new List (); + this.comp_units = new List (); + this.file = new MonoSymbolFile (); + + this.filename = filename + ".mdb"; + } + + public MonoSymbolFile SymbolFile { + get { return file; } + } + + public void CloseNamespace () + { } + + public void DefineLocalVariable (int index, string name) + { + if (current_method == null) + return; + + current_method.AddLocal (index, name); + } + + public void DefineCapturedLocal (int scope_id, string name, string captured_name) + { + file.DefineCapturedVariable (scope_id, name, captured_name, + CapturedVariable.CapturedKind.Local); + } + + public void DefineCapturedParameter (int scope_id, string name, string captured_name) + { + file.DefineCapturedVariable (scope_id, name, captured_name, + CapturedVariable.CapturedKind.Parameter); + } + + public void DefineCapturedThis (int scope_id, string captured_name) + { + file.DefineCapturedVariable (scope_id, "this", captured_name, + CapturedVariable.CapturedKind.This); + } + + public void DefineCapturedScope (int scope_id, int id, string captured_name) + { + file.DefineCapturedScope (scope_id, id, captured_name); + } + + public void DefineScopeVariable (int scope, int index) + { + if (current_method == null) + return; + + current_method.AddScopeVariable (scope, index); + } + + public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, + bool is_hidden) + { + if (current_method == null) + return; + + current_method.MarkSequencePoint (offset, file, line, column, is_hidden); + } + + public SourceMethodBuilder OpenMethod (ICompileUnit file, int ns_id, IMethodDef method) + { + SourceMethodBuilder builder = new SourceMethodBuilder (file, ns_id, method); + current_method_stack.Push (current_method); + current_method = builder; + methods.Add (current_method); + return builder; + } + + public void CloseMethod () + { + current_method = (SourceMethodBuilder) current_method_stack.Pop (); + } + + public SourceFileEntry DefineDocument (string url) + { + SourceFileEntry entry = new SourceFileEntry (file, url); + sources.Add (entry); + return entry; + } + + public SourceFileEntry DefineDocument (string url, byte[] guid, byte[] checksum) + { + SourceFileEntry entry = new SourceFileEntry (file, url, guid, checksum); + sources.Add (entry); + return entry; + } + + public CompileUnitEntry DefineCompilationUnit (SourceFileEntry source) + { + CompileUnitEntry entry = new CompileUnitEntry (file, source); + comp_units.Add (entry); + return entry; + } + + public int DefineNamespace (string name, CompileUnitEntry unit, + string[] using_clauses, int parent) + { + if ((unit == null) || (using_clauses == null)) + throw new NullReferenceException (); + + return unit.DefineNamespace (name, using_clauses, parent); + } + + public int OpenScope (int start_offset) + { + if (current_method == null) + return 0; + + current_method.StartBlock (CodeBlockEntry.Type.Lexical, start_offset); + return 0; + } + + public void CloseScope (int end_offset) + { + if (current_method == null) + return; + + current_method.EndBlock (end_offset); + } + + public void OpenCompilerGeneratedBlock (int start_offset) + { + if (current_method == null) + return; + + current_method.StartBlock (CodeBlockEntry.Type.CompilerGenerated, + start_offset); + } + + public void CloseCompilerGeneratedBlock (int end_offset) + { + if (current_method == null) + return; + + current_method.EndBlock (end_offset); + } + + public void StartIteratorBody (int start_offset) + { + current_method.StartBlock (CodeBlockEntry.Type.IteratorBody, + start_offset); + } + + public void EndIteratorBody (int end_offset) + { + current_method.EndBlock (end_offset); + } + + public void StartIteratorDispatcher (int start_offset) + { + current_method.StartBlock (CodeBlockEntry.Type.IteratorDispatcher, + start_offset); + } + + public void EndIteratorDispatcher (int end_offset) + { + current_method.EndBlock (end_offset); + } + + public void DefineAnonymousScope (int id) + { + file.DefineAnonymousScope (id); + } + + public void WriteSymbolFile (Guid guid) + { + foreach (SourceMethodBuilder method in methods) + method.DefineMethod (file); + + try { + // We mmap the file, so unlink the previous version since it may be in use + File.Delete (filename); + } catch { + // We can safely ignore + } + using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) { + file.CreateSymbolFile (guid, fs); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/SourceMethodBuilder.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/SourceMethodBuilder.cs new file mode 100644 index 000000000..bd801f657 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/SourceMethodBuilder.cs @@ -0,0 +1,190 @@ +// +// SourceMethodBuilder.cs +// +// Authors: +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// (C) 2002 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com) +// +// 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.Collections.Generic; + +namespace Mono.CompilerServices.SymbolWriter +{ + public class SourceMethodBuilder + { + List _locals; + List _blocks; + List _scope_vars; + Stack _block_stack; + readonly List method_lines; + + readonly ICompileUnit _comp_unit; + readonly int ns_id; + readonly IMethodDef method; + + public SourceMethodBuilder (ICompileUnit comp_unit) + { + this._comp_unit = comp_unit; + method_lines = new List (); + } + + public SourceMethodBuilder (ICompileUnit comp_unit, int ns_id, IMethodDef method) + : this (comp_unit) + { + this.ns_id = ns_id; + this.method = method; + } + + public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, bool is_hidden) + { + MarkSequencePoint (offset, file, line, column, -1, -1, is_hidden); + } + + public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, int end_line, int end_column, bool is_hidden) + { + int file_idx = file != null ? file.Index : 0; + var lne = new LineNumberEntry (file_idx, line, column, end_line, end_column, offset, is_hidden); + + if (method_lines.Count > 0) { + var prev = method_lines[method_lines.Count - 1]; + + // + // Same offset cannot be used for multiple lines + // + if (prev.Offset == offset) { + // + // Use the new location because debugger will adjust + // the breakpoint to next line with sequence point + // + if (LineNumberEntry.LocationComparer.Default.Compare (lne, prev) > 0) + method_lines[method_lines.Count - 1] = lne; + + return; + } + } + + method_lines.Add (lne); + } + + public void StartBlock (CodeBlockEntry.Type type, int start_offset) + { + if (_block_stack == null) { + _block_stack = new Stack (); + } + + if (_blocks == null) + _blocks = new List (); + + int parent = CurrentBlock != null ? CurrentBlock.Index : -1; + + CodeBlockEntry block = new CodeBlockEntry ( + _blocks.Count + 1, parent, type, start_offset); + + _block_stack.Push (block); + _blocks.Add (block); + } + + public void EndBlock (int end_offset) + { + CodeBlockEntry block = (CodeBlockEntry) _block_stack.Pop (); + block.Close (end_offset); + } + + public CodeBlockEntry[] Blocks { + get { + if (_blocks == null) + return new CodeBlockEntry [0]; + + CodeBlockEntry[] retval = new CodeBlockEntry [_blocks.Count]; + _blocks.CopyTo (retval, 0); + return retval; + } + } + + public CodeBlockEntry CurrentBlock { + get { + if ((_block_stack != null) && (_block_stack.Count > 0)) + return (CodeBlockEntry) _block_stack.Peek (); + else + return null; + } + } + + public LocalVariableEntry[] Locals { + get { + if (_locals == null) + return new LocalVariableEntry [0]; + else { + return _locals.ToArray (); + } + } + } + + public ICompileUnit SourceFile { + get { + return _comp_unit; + } + } + + public void AddLocal (int index, string name) + { + if (_locals == null) + _locals = new List (); + int block_idx = CurrentBlock != null ? CurrentBlock.Index : 0; + _locals.Add (new LocalVariableEntry (index, name, block_idx)); + } + + public ScopeVariable[] ScopeVariables { + get { + if (_scope_vars == null) + return new ScopeVariable [0]; + + return _scope_vars.ToArray (); + } + } + + public void AddScopeVariable (int scope, int index) + { + if (_scope_vars == null) + _scope_vars = new List (); + _scope_vars.Add ( + new ScopeVariable (scope, index)); + } + + public void DefineMethod (MonoSymbolFile file) + { + DefineMethod (file, method.Token); + } + + public void DefineMethod (MonoSymbolFile file, int token) + { + MethodEntry entry = new MethodEntry ( + file, _comp_unit.Entry, token, ScopeVariables, + Locals, method_lines.ToArray (), Blocks, null, MethodEntry.Flags.ColumnsInfoIncluded, ns_id); + + file.AddMethod (entry); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/anonymous.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/anonymous.cs new file mode 100644 index 000000000..e7f003784 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/anonymous.cs @@ -0,0 +1,2292 @@ +// +// anonymous.cs: Support for anonymous methods and types +// +// Author: +// Miguel de Icaza (miguel@ximain.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// Copyright 2003-2011 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using Mono.CompilerServices.SymbolWriter; +using System.Diagnostics; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +using System.Diagnostics; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + public abstract class CompilerGeneratedContainer : ClassOrStruct + { + protected CompilerGeneratedContainer (TypeContainer parent, MemberName name, Modifiers mod) + : this (parent, name, mod, MemberKind.Class) + { + } + + protected CompilerGeneratedContainer (TypeContainer parent, MemberName name, Modifiers mod, MemberKind kind) + : base (parent, name, null, kind) + { + Debug.Assert ((mod & Modifiers.AccessibilityMask) != 0); + + ModFlags = mod | Modifiers.COMPILER_GENERATED | Modifiers.SEALED; + spec = new TypeSpec (Kind, null, this, null, ModFlags); + } + + protected void CheckMembersDefined () + { + if (HasMembersDefined) + throw new InternalErrorException ("Helper class already defined!"); + } + + protected override bool DoDefineMembers () + { + if (Kind == MemberKind.Class && !IsStatic && !PartialContainer.HasInstanceConstructor) { + DefineDefaultConstructor (false); + } + + return base.DoDefineMembers (); + } + + protected static MemberName MakeMemberName (MemberBase host, string name, int unique_id, TypeParameters tparams, Location loc) + { + string host_name = host == null ? null : host is InterfaceMemberBase ? ((InterfaceMemberBase)host).GetFullName (host.MemberName) : host.MemberName.Name; + string tname = MakeName (host_name, "c", name, unique_id); + TypeParameters args = null; + if (tparams != null) { + args = new TypeParameters (tparams.Count); + + // Type parameters will be filled later when we have TypeContainer + // instance, for now we need only correct arity to create valid name + for (int i = 0; i < tparams.Count; ++i) + args.Add ((TypeParameter) null); + } + + return new MemberName (tname, args, loc); + } + + public static string MakeName (string host, string typePrefix, string name, int id) + { + return "<" + host + ">" + typePrefix + "__" + name + id.ToString ("X"); + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_type = Compiler.BuiltinTypes.Object; + + base_class = null; + return null; + } + } + + public class HoistedStoreyClass : CompilerGeneratedContainer + { + public sealed class HoistedField : Field + { + public HoistedField (HoistedStoreyClass parent, FullNamedExpression type, Modifiers mod, string name, + Attributes attrs, Location loc) + : base (parent, type, mod, new MemberName (name, loc), attrs) + { + } + + protected override bool ResolveMemberType () + { + if (!base.ResolveMemberType ()) + return false; + + HoistedStoreyClass parent = ((HoistedStoreyClass) Parent).GetGenericStorey (); + if (parent != null && parent.Mutator != null) + member_type = parent.Mutator.Mutate (MemberType); + + return true; + } + } + + protected TypeParameterMutator mutator; + + public HoistedStoreyClass (TypeDefinition parent, MemberName name, TypeParameters tparams, Modifiers mods, MemberKind kind) + : base (parent, name, mods | Modifiers.PRIVATE, kind) + { + + if (tparams != null) { + var type_params = name.TypeParameters; + var src = new TypeParameterSpec[tparams.Count]; + var dst = new TypeParameterSpec[tparams.Count]; + + for (int i = 0; i < tparams.Count; ++i) { + type_params[i] = tparams[i].CreateHoistedCopy (spec); + + src[i] = tparams[i].Type; + dst[i] = type_params[i].Type; + } + + // A copy is not enough, inflate any type parameter constraints + // using a new type parameters + var inflator = new TypeParameterInflator (this, null, src, dst); + for (int i = 0; i < tparams.Count; ++i) { + src[i].InflateConstraints (inflator, dst[i]); + } + + mutator = new TypeParameterMutator (tparams, type_params); + } + } + + #region Properties + + public TypeParameterMutator Mutator { + get { + return mutator; + } + set { + mutator = value; + } + } + + #endregion + + public HoistedStoreyClass GetGenericStorey () + { + TypeContainer storey = this; + while (storey != null && storey.CurrentTypeParameters == null) + storey = storey.Parent; + + return storey as HoistedStoreyClass; + } + } + + + // + // Anonymous method storey is created when an anonymous method uses + // variable or parameter from outer scope. They are then hoisted to + // anonymous method storey (captured) + // + public class AnonymousMethodStorey : HoistedStoreyClass + { + struct StoreyFieldPair + { + public readonly AnonymousMethodStorey Storey; + public readonly Field Field; + + public StoreyFieldPair (AnonymousMethodStorey storey, Field field) + { + this.Storey = storey; + this.Field = field; + } + } + + // + // Needed to delay hoisted _this_ initialization. When an anonymous + // method is used inside ctor and _this_ is hoisted, base ctor has to + // be called first, otherwise _this_ will be initialized with + // uninitialized value. + // + sealed class ThisInitializer : Statement + { + readonly HoistedThis hoisted_this; + readonly AnonymousMethodStorey parent; + + public ThisInitializer (HoistedThis hoisted_this, AnonymousMethodStorey parent) + { + this.hoisted_this = hoisted_this; + this.parent = parent; + } + + protected override void DoEmit (EmitContext ec) + { + Expression source; + + if (parent == null) + source = new CompilerGeneratedThis (ec.CurrentType, loc); + else { + source = new FieldExpr (parent.HoistedThis.Field, Location.Null) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location.Null) + }; + } + + hoisted_this.EmitAssign (ec, source, false, false); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // Nothing to clone + } + } + + // Unique storey ID + public readonly int ID; + + public readonly ExplicitBlock OriginalSourceBlock; + + // A list of StoreyFieldPair with local field keeping parent storey instance + List used_parent_storeys; + List children_references; + + // A list of hoisted parameters + protected List hoisted_params; + List hoisted_local_params; + protected List hoisted_locals; + + // Hoisted this + protected HoistedThis hoisted_this; + + // Local variable which holds this storey instance + public Expression Instance; + + bool initialize_hoisted_this; + AnonymousMethodStorey hoisted_this_parent; + + public AnonymousMethodStorey (ExplicitBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind) + : base (parent, MakeMemberName (host, name, parent.PartialContainer.CounterAnonymousContainers, tparams, block.StartLocation), + tparams, 0, kind) + { + OriginalSourceBlock = block; + ID = parent.PartialContainer.CounterAnonymousContainers++; + } + + public void AddCapturedThisField (EmitContext ec, AnonymousMethodStorey parent) + { + TypeExpr type_expr = new TypeExpression (ec.CurrentType, Location); + Field f = AddCompilerGeneratedField ("$this", type_expr); + hoisted_this = new HoistedThis (this, f); + + initialize_hoisted_this = true; + hoisted_this_parent = parent; + } + + public Field AddCapturedVariable (string name, TypeSpec type) + { + CheckMembersDefined (); + + FullNamedExpression field_type = new TypeExpression (type, Location); + if (!spec.IsGenericOrParentIsGeneric) + return AddCompilerGeneratedField (name, field_type); + + const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED; + Field f = new HoistedField (this, field_type, mod, name, null, Location); + AddField (f); + return f; + } + + protected Field AddCompilerGeneratedField (string name, FullNamedExpression type) + { + return AddCompilerGeneratedField (name, type, false); + } + + protected Field AddCompilerGeneratedField (string name, FullNamedExpression type, bool privateAccess) + { + Modifiers mod = Modifiers.COMPILER_GENERATED | (privateAccess ? Modifiers.PRIVATE : Modifiers.INTERNAL); + Field f = new Field (this, type, mod, new MemberName (name, Location), null); + AddField (f); + return f; + } + + // + // Creates a link between hoisted variable block and the anonymous method storey + // + // An anonymous method can reference variables from any outer block, but they are + // hoisted in their own ExplicitBlock. When more than one block is referenced we + // need to create another link between those variable storeys + // + public void AddReferenceFromChildrenBlock (ExplicitBlock block) + { + if (children_references == null) + children_references = new List (); + + if (!children_references.Contains (block)) + children_references.Add (block); + } + + public void AddParentStoreyReference (EmitContext ec, AnonymousMethodStorey storey) + { + CheckMembersDefined (); + + if (used_parent_storeys == null) + used_parent_storeys = new List (); + else if (used_parent_storeys.Exists (i => i.Storey == storey)) + return; + + TypeExpr type_expr = storey.CreateStoreyTypeExpression (ec); + Field f = AddCompilerGeneratedField ("<>f__ref$" + storey.ID, type_expr); + used_parent_storeys.Add (new StoreyFieldPair (storey, f)); + } + + public void CaptureLocalVariable (ResolveContext ec, LocalVariable localVariable) + { + if (this is StateMachine) { + if (ec.CurrentBlock.ParametersBlock != localVariable.Block.ParametersBlock) + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } else { + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } + + var hoisted = localVariable.HoistedVariant; + if (hoisted != null && hoisted.Storey != this && hoisted.Storey is StateMachine) { + // + // Variable is already hoisted but we need it in storey which can be shared + // + hoisted.Storey.hoisted_locals.Remove (hoisted); + hoisted.Storey.Members.Remove (hoisted.Field); + hoisted = null; + } + + if (hoisted == null) { + hoisted = new HoistedLocalVariable (this, localVariable, GetVariableMangledName (localVariable)); + localVariable.HoistedVariant = hoisted; + + if (hoisted_locals == null) + hoisted_locals = new List (); + + hoisted_locals.Add (hoisted); + } + + if (ec.CurrentBlock.Explicit != localVariable.Block.Explicit && !(hoisted.Storey is StateMachine)) + hoisted.Storey.AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit); + } + + public void CaptureParameter (ResolveContext ec, ParametersBlock.ParameterInfo parameterInfo, ParameterReference parameterReference) + { + if (!(this is StateMachine)) { + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } + + var hoisted = parameterInfo.Parameter.HoistedVariant; + + if (parameterInfo.Block.StateMachine != null) { + // + // Another storey in same block exists but state machine does not + // have parameter captured. We need to add it there as well to + // proxy parameter value correctly. + // + if (hoisted == null && parameterInfo.Block.StateMachine != this) { + var storey = parameterInfo.Block.StateMachine; + + hoisted = new HoistedParameter (storey, parameterReference); + parameterInfo.Parameter.HoistedVariant = hoisted; + + if (storey.hoisted_params == null) + storey.hoisted_params = new List (); + + storey.hoisted_params.Add (hoisted); + } + + // + // Lift captured parameter from value type storey to reference type one. Otherwise + // any side effects would be done on a copy + // + if (hoisted != null && hoisted.Storey != this && hoisted.Storey is StateMachine) { + if (hoisted_local_params == null) + hoisted_local_params = new List (); + + hoisted_local_params.Add (hoisted); + hoisted = null; + } + } + + if (hoisted == null) { + hoisted = new HoistedParameter (this, parameterReference); + parameterInfo.Parameter.HoistedVariant = hoisted; + + if (hoisted_params == null) + hoisted_params = new List (); + + hoisted_params.Add (hoisted); + } + + // + // Register link between current block and parameter storey. It will + // be used when setting up storey definition to deploy storey reference + // when parameters are used from multiple blocks + // + if (ec.CurrentBlock.Explicit != parameterInfo.Block) { + hoisted.Storey.AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit); + } + } + + TypeExpr CreateStoreyTypeExpression (EmitContext ec) + { + // + // Create an instance of storey type + // + TypeExpr storey_type_expr; + if (CurrentTypeParameters != null) { + // + // Use current method type parameter (MVAR) for top level storey only. All + // nested storeys use class type parameter (VAR) + // + var tparams = ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null ? + ec.CurrentAnonymousMethod.Storey.CurrentTypeParameters : + ec.CurrentTypeParameters; + + TypeArguments targs = new TypeArguments (); + + // + // Use type parameter name instead of resolved type parameter + // specification to resolve to correctly nested type parameters + // + for (int i = 0; i < tparams.Count; ++i) + targs.Add (new SimpleName (tparams [i].Name, Location)); // new TypeParameterExpr (tparams[i], Location)); + + storey_type_expr = new GenericTypeExpr (Definition, targs, Location); + } else { + storey_type_expr = new TypeExpression (CurrentType, Location); + } + + return storey_type_expr; + } + + public void SetNestedStoryParent (AnonymousMethodStorey parentStorey) + { + Parent = parentStorey; + spec.IsGeneric = false; + spec.DeclaringType = parentStorey.CurrentType; + MemberName.TypeParameters = null; + } + + protected override bool DoResolveTypeParameters () + { + // Although any storey can have type parameters they are all clones of method type + // parameters therefore have to mutate MVAR references in any of cloned constraints + if (CurrentTypeParameters != null) { + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var spec = CurrentTypeParameters[i].Type; + spec.BaseType = mutator.Mutate (spec.BaseType); + if (spec.InterfacesDefined != null) { + var mutated = new TypeSpec[spec.InterfacesDefined.Length]; + for (int ii = 0; ii < mutated.Length; ++ii) { + mutated[ii] = mutator.Mutate (spec.InterfacesDefined[ii]); + } + + spec.InterfacesDefined = mutated; + } + + if (spec.TypeArguments != null) { + spec.TypeArguments = mutator.Mutate (spec.TypeArguments); + } + } + } + + // + // Update parent cache as we most likely passed the point + // where the cache was constructed + // + Parent.CurrentType.MemberCache.AddMember (this.spec); + + return true; + } + + // + // Initializes all hoisted variables + // + public void EmitStoreyInstantiation (EmitContext ec, ExplicitBlock block) + { + // There can be only one instance variable for each storey type + if (Instance != null) + throw new InternalErrorException (); + + // + // Create an instance of this storey + // + ResolveContext rc = new ResolveContext (ec.MemberContext); + rc.CurrentBlock = block; + + var storey_type_expr = CreateStoreyTypeExpression (ec); + var source = new New (storey_type_expr, null, Location).Resolve (rc); + + // + // When the current context is async (or iterator) lift local storey + // instantiation to the currect storey + // + if (ec.CurrentAnonymousMethod is StateMachineInitializer && (block.HasYield || block.HasAwait)) { + // + // Unfortunately, normal capture mechanism could not be used because we are + // too late in the pipeline and standart assign cannot be used either due to + // recursive nature of GetStoreyInstanceExpression + // + var field = ec.CurrentAnonymousMethod.Storey.AddCompilerGeneratedField ( + LocalVariable.GetCompilerGeneratedName (block), storey_type_expr, true); + + field.Define (); + field.Emit (); + + var fexpr = new FieldExpr (field, Location); + fexpr.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location); + fexpr.EmitAssign (ec, source, false, false); + Instance = fexpr; + } else { + var local = TemporaryVariableReference.Create (source.Type, block, Location); + if (source.Type.IsStruct) { + local.LocalInfo.CreateBuilder (ec); + } else { + local.EmitAssign (ec, source); + } + + Instance = local; + } + + EmitHoistedFieldsInitialization (rc, ec); + + // TODO: Implement properly + //SymbolWriter.DefineScopeVariable (ID, Instance.Builder); + } + + void EmitHoistedFieldsInitialization (ResolveContext rc, EmitContext ec) + { + // + // Initialize all storey reference fields by using local or hoisted variables + // + if (used_parent_storeys != null) { + foreach (StoreyFieldPair sf in used_parent_storeys) { + // + // Get instance expression of storey field + // + Expression instace_expr = GetStoreyInstanceExpression (ec); + var fs = sf.Field.Spec; + if (TypeManager.IsGenericType (instace_expr.Type)) + fs = MemberCache.GetMember (instace_expr.Type, fs); + + FieldExpr f_set_expr = new FieldExpr (fs, Location); + f_set_expr.InstanceExpression = instace_expr; + + // TODO: CompilerAssign expression + SimpleAssign a = new SimpleAssign (f_set_expr, sf.Storey.GetStoreyInstanceExpression (ec)); + if (a.Resolve (rc) != null) + a.EmitStatement (ec); + } + } + + // + // Initialize hoisted `this' only once, everywhere else will be + // referenced indirectly + // + if (initialize_hoisted_this) { + rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this, hoisted_this_parent)); + } + + // + // Setting currect anonymous method to null blocks any further variable hoisting + // + AnonymousExpression ae = ec.CurrentAnonymousMethod; + ec.CurrentAnonymousMethod = null; + + if (hoisted_params != null) { + EmitHoistedParameters (ec, hoisted_params); + } + + ec.CurrentAnonymousMethod = ae; + } + + protected virtual void EmitHoistedParameters (EmitContext ec, List hoisted) + { + foreach (HoistedParameter hp in hoisted) { + if (hp == null) + continue; + + // + // Parameters could be proxied via local fields for value type storey + // + if (hoisted_local_params != null) { + var local_param = hoisted_local_params.Find (l => l.Parameter.Parameter == hp.Parameter.Parameter); + var source = new FieldExpr (local_param.Field, Location); + source.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location); + hp.EmitAssign (ec, source, false, false); + continue; + } + + hp.EmitHoistingAssignment (ec); + } + } + + // + // Returns a field which holds referenced storey instance + // + Field GetReferencedStoreyField (AnonymousMethodStorey storey) + { + if (used_parent_storeys == null) + return null; + + foreach (StoreyFieldPair sf in used_parent_storeys) { + if (sf.Storey == storey) + return sf.Field; + } + + return null; + } + + // + // Creates storey instance expression regardless of currect IP + // + public Expression GetStoreyInstanceExpression (EmitContext ec) + { + AnonymousExpression am = ec.CurrentAnonymousMethod; + + // + // Access from original block -> storey + // + if (am == null) + return Instance; + + // + // Access from anonymous method implemented as a static -> storey + // + if (am.Storey == null) + return Instance; + + Field f = am.Storey.GetReferencedStoreyField (this); + if (f == null) { + if (am.Storey == this) { + // + // Access from inside of same storey (S -> S) + // + return new CompilerGeneratedThis (CurrentType, Location); + } + + // + // External field access + // + return Instance; + } + + // + // Storey was cached to local field + // + FieldExpr f_ind = new FieldExpr (f, Location); + f_ind.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location); + return f_ind; + } + + protected virtual string GetVariableMangledName (LocalVariable local_info) + { + // + // No need to mangle anonymous method hoisted variables cause they + // are hoisted in their own scopes + // + return local_info.Name; + } + + public HoistedThis HoistedThis { + get { + return hoisted_this; + } + set { + hoisted_this = value; + } + } + + public IList ReferencesFromChildrenBlock { + get { return children_references; } + } + } + + public abstract class HoistedVariable + { + // + // Hoisted version of variable references used in expression + // tree has to be delayed until we know its location. The variable + // doesn't know its location until all stories are calculated + // + class ExpressionTreeVariableReference : Expression + { + readonly HoistedVariable hv; + + public ExpressionTreeVariableReference (HoistedVariable hv) + { + this.hv = hv; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return hv.CreateExpressionTree (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Value; + type = ec.Module.PredefinedTypes.Expression.Resolve (); + return this; + } + + public override void Emit (EmitContext ec) + { + ResolveContext rc = new ResolveContext (ec.MemberContext); + Expression e = hv.GetFieldExpression (ec).CreateExpressionTree (rc, false); + // This should never fail + e = e.Resolve (rc); + if (e != null) + e.Emit (ec); + } + } + + protected readonly AnonymousMethodStorey storey; + protected Field field; + Dictionary cached_inner_access; // TODO: Hashtable is too heavyweight + FieldExpr cached_outer_access; + + protected HoistedVariable (AnonymousMethodStorey storey, string name, TypeSpec type) + : this (storey, storey.AddCapturedVariable (name, type)) + { + } + + protected HoistedVariable (AnonymousMethodStorey storey, Field field) + { + this.storey = storey; + this.field = field; + } + + public Field Field { + get { + return field; + } + } + + public AnonymousMethodStorey Storey { + get { + return storey; + } + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + GetFieldExpression (ec).AddressOf (ec, mode); + } + + public Expression CreateExpressionTree () + { + return new ExpressionTreeVariableReference (this); + } + + public void Emit (EmitContext ec) + { + GetFieldExpression (ec).Emit (ec); + } + + public Expression EmitToField (EmitContext ec) + { + return GetFieldExpression (ec); + } + + // + // Creates field access expression for hoisted variable + // + protected virtual FieldExpr GetFieldExpression (EmitContext ec) + { + if (ec.CurrentAnonymousMethod == null || ec.CurrentAnonymousMethod.Storey == null) { + if (cached_outer_access != null) + return cached_outer_access; + + // + // When setting top-level hoisted variable in generic storey + // change storey generic types to method generic types (VAR -> MVAR) + // + if (storey.Instance.Type.IsGenericOrParentIsGeneric) { + var fs = MemberCache.GetMember (storey.Instance.Type, field.Spec); + cached_outer_access = new FieldExpr (fs, field.Location); + } else { + cached_outer_access = new FieldExpr (field, field.Location); + } + + cached_outer_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec); + return cached_outer_access; + } + + FieldExpr inner_access; + if (cached_inner_access != null) { + if (!cached_inner_access.TryGetValue (ec.CurrentAnonymousMethod, out inner_access)) + inner_access = null; + } else { + inner_access = null; + cached_inner_access = new Dictionary (4); + } + + if (inner_access == null) { + if (field.Parent.IsGenericOrParentIsGeneric) { + var fs = MemberCache.GetMember (field.Parent.CurrentType, field.Spec); + inner_access = new FieldExpr (fs, field.Location); + } else { + inner_access = new FieldExpr (field, field.Location); + } + + inner_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec); + cached_inner_access.Add (ec.CurrentAnonymousMethod, inner_access); + } + + return inner_access; + } + + public void Emit (EmitContext ec, bool leave_copy) + { + GetFieldExpression (ec).Emit (ec, leave_copy); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + GetFieldExpression (ec).EmitAssign (ec, source, leave_copy, false); + } + } + + public class HoistedParameter : HoistedVariable + { + sealed class HoistedFieldAssign : CompilerAssign + { + public HoistedFieldAssign (Expression target, Expression source) + : base (target, source, target.Location) + { + } + + protected override Expression ResolveConversions (ResolveContext ec) + { + // + // Implicit conversion check fails for hoisted type arguments + // as they are of different types (!!0 x !0) + // + return this; + } + } + + readonly ParameterReference parameter; + + public HoistedParameter (AnonymousMethodStorey scope, ParameterReference par) + : base (scope, par.Name, par.Type) + { + this.parameter = par; + } + + public HoistedParameter (HoistedParameter hp, string name) + : base (hp.storey, name, hp.parameter.Type) + { + this.parameter = hp.parameter; + } + + #region Properties + + public bool IsAssigned { get; set; } + + public ParameterReference Parameter { + get { + return parameter; + } + } + + #endregion + + public void EmitHoistingAssignment (EmitContext ec) + { + // + // Remove hoisted redirection to emit assignment from original parameter + // + var temp = parameter.Parameter.HoistedVariant; + parameter.Parameter.HoistedVariant = null; + + var a = new HoistedFieldAssign (GetFieldExpression (ec), parameter); + a.EmitStatement (ec); + + parameter.Parameter.HoistedVariant = temp; + } + } + + class HoistedLocalVariable : HoistedVariable + { + public HoistedLocalVariable (AnonymousMethodStorey storey, LocalVariable local, string name) + : base (storey, name, local.Type) + { + } + } + + public class HoistedThis : HoistedVariable + { + public HoistedThis (AnonymousMethodStorey storey, Field field) + : base (storey, field) + { + } + } + + // + // Anonymous method expression as created by parser + // + public class AnonymousMethodExpression : Expression + { + // + // Special conversion for nested expression tree lambdas + // + class Quote : ShimExpression + { + public Quote (Expression expr) + : base (expr) + { + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + var args = new Arguments (1); + args.Add (new Argument (expr.CreateExpressionTree (ec))); + return CreateExpressionFactoryCall (ec, "Quote", args); + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.Resolve (rc); + if (expr == null) + return null; + + eclass = expr.eclass; + type = expr.Type; + return this; + } + } + + readonly Dictionary compatibles; + + public ParametersBlock Block; + + public AnonymousMethodExpression (Location loc) + { + this.loc = loc; + this.compatibles = new Dictionary (); + } + + #region Properties + + public override string ExprClassName { + get { + return "anonymous method"; + } + } + + public virtual bool HasExplicitParameters { + get { + return Parameters != ParametersCompiled.Undefined; + } + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public ParametersCompiled Parameters { + get { + return Block.Parameters; + } + } + + public bool IsAsync { + get; + internal set; + } + + public ReportPrinter TypeInferenceReportPrinter { + get; set; + } + + #endregion + + // + // Returns true if the body of lambda expression can be implicitly + // converted to the delegate of type `delegate_type' + // + public bool ImplicitStandardConversionExists (ResolveContext ec, TypeSpec delegate_type) + { + using (ec.With (ResolveContext.Options.InferReturnType, false)) { + using (ec.Set (ResolveContext.Options.ProbingMode)) { + var prev = ec.Report.SetPrinter (TypeInferenceReportPrinter ?? new NullReportPrinter ()); + + var res = Compatible (ec, delegate_type) != null; + + ec.Report.SetPrinter (prev); + + return res; + } + } + } + + TypeSpec CompatibleChecks (ResolveContext ec, TypeSpec delegate_type) + { + if (delegate_type.IsDelegate) + return delegate_type; + + if (delegate_type.IsExpressionTreeType) { + delegate_type = delegate_type.TypeArguments [0]; + if (delegate_type.IsDelegate) + return delegate_type; + + ec.Report.Error (835, loc, "Cannot convert `{0}' to an expression tree of non-delegate type `{1}'", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + return null; + } + + ec.Report.Error (1660, loc, "Cannot convert `{0}' to non-delegate type `{1}'", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + return null; + } + + protected bool VerifyExplicitParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection parameters) + { + if (VerifyParameterCompatibility (ec, tic, delegate_type, parameters, ec.IsInProbingMode)) + return true; + + if (!ec.IsInProbingMode) + ec.Report.Error (1661, loc, + "Cannot convert `{0}' to delegate type `{1}' since there is a parameter mismatch", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + + return false; + } + + protected bool VerifyParameterCompatibility (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors) + { + if (Parameters.Count != invoke_pd.Count) { + if (ignore_errors) + return false; + + ec.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments", + delegate_type.GetSignatureForError (), Parameters.Count.ToString ()); + return false; + } + + bool has_implicit_parameters = !HasExplicitParameters; + bool error = false; + + for (int i = 0; i < Parameters.Count; ++i) { + Parameter.Modifier p_mod = invoke_pd.FixedParameters [i].ModFlags; + if (Parameters.FixedParameters [i].ModFlags != p_mod && p_mod != Parameter.Modifier.PARAMS) { + if (ignore_errors) + return false; + + if (p_mod == Parameter.Modifier.NONE) + ec.Report.Error (1677, Parameters[i].Location, "Parameter `{0}' should not be declared with the `{1}' keyword", + (i + 1).ToString (), Parameter.GetModifierSignature (Parameters [i].ModFlags)); + else + ec.Report.Error (1676, Parameters[i].Location, "Parameter `{0}' must be declared with the `{1}' keyword", + (i+1).ToString (), Parameter.GetModifierSignature (p_mod)); + error = true; + } + + if (has_implicit_parameters) + continue; + + TypeSpec type = invoke_pd.Types [i]; + + if (tic != null) + type = tic.InflateGenericArgument (ec, type); + + if (!TypeSpecComparer.IsEqual (type, Parameters.Types [i])) { + if (ignore_errors) + return false; + + ec.Report.Error (1678, Parameters [i].Location, "Parameter `{0}' is declared as type `{1}' but should be `{2}'", + (i+1).ToString (), + Parameters.Types [i].GetSignatureForError (), + invoke_pd.Types [i].GetSignatureForError ()); + error = true; + } + } + + return !error; + } + + // + // Infers type arguments based on explicit arguments + // + public bool ExplicitTypeInference (TypeInferenceContext type_inference, TypeSpec delegate_type) + { + if (!HasExplicitParameters) + return false; + + if (!delegate_type.IsDelegate) { + if (!delegate_type.IsExpressionTreeType) + return false; + + delegate_type = TypeManager.GetTypeArguments (delegate_type) [0]; + if (!delegate_type.IsDelegate) + return false; + } + + AParametersCollection d_params = Delegate.GetParameters (delegate_type); + if (d_params.Count != Parameters.Count) + return false; + + var ptypes = Parameters.Types; + var dtypes = d_params.Types; + for (int i = 0; i < Parameters.Count; ++i) { + if (type_inference.ExactInference (ptypes[i], dtypes[i]) == 0) { + // + // Continue when 0 (quick path) does not mean inference failure. Checking for + // same type handles cases like int -> int + // + if (ptypes[i] == dtypes[i]) + continue; + + return false; + } + } + + return true; + } + + public TypeSpec InferReturnType (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type) + { + Expression expr; + AnonymousExpression am; + + if (compatibles.TryGetValue (delegate_type, out expr)) { + am = expr as AnonymousExpression; + return am == null ? null : am.ReturnType; + } + + using (ec.Set (ResolveContext.Options.ProbingMode | ResolveContext.Options.InferReturnType)) { + ReportPrinter prev; + if (TypeInferenceReportPrinter != null) { + prev = ec.Report.SetPrinter (TypeInferenceReportPrinter); + } else { + prev = null; + } + + var body = CompatibleMethodBody (ec, tic, null, delegate_type); + if (body != null) { + am = body.Compatible (ec, body); + } else { + am = null; + } + + if (TypeInferenceReportPrinter != null) { + ec.Report.SetPrinter (prev); + } + } + + if (am == null) + return null; + +// compatibles.Add (delegate_type, am); + return am.ReturnType; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + // + // Returns AnonymousMethod container if this anonymous method + // expression can be implicitly converted to the delegate type `delegate_type' + // + public Expression Compatible (ResolveContext ec, TypeSpec type) + { + Expression am; + if (compatibles.TryGetValue (type, out am)) + return am; + + TypeSpec delegate_type = CompatibleChecks (ec, type); + if (delegate_type == null) + return null; + + // + // At this point its the first time we know the return type that is + // needed for the anonymous method. We create the method here. + // + + var invoke_mb = Delegate.GetInvokeMethod (delegate_type); + TypeSpec return_type = invoke_mb.ReturnType; + + // + // Second: the return type of the delegate must be compatible with + // the anonymous type. Instead of doing a pass to examine the block + // we satisfy the rule by setting the return type on the EmitContext + // to be the delegate type return type. + // + + var body = CompatibleMethodBody (ec, null, return_type, delegate_type); + if (body == null) + return null; + + bool etree_conversion = delegate_type != type; + + try { + if (etree_conversion) { + if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + // + // Nested expression tree lambda use same scope as parent + // lambda, this also means no variable capturing between this + // and parent scope + // + am = body.Compatible (ec, ec.CurrentAnonymousMethod); + + // + // Quote nested expression tree + // + if (am != null) + am = new Quote (am); + } else { + int errors = ec.Report.Errors; + + if (Block.IsAsync) { + ec.Report.Error (1989, loc, "Async lambda expressions cannot be converted to expression trees"); + } + + using (ec.Set (ResolveContext.Options.ExpressionTreeConversion)) { + am = body.Compatible (ec); + } + + // + // Rewrite expressions into expression tree when targeting Expression + // + if (am != null && errors == ec.Report.Errors) + am = CreateExpressionTree (ec, delegate_type); + } + } else { + am = body.Compatible (ec); + + if (body.DirectMethodGroupConversion != null) { + var errors_printer = new SessionReportPrinter (); + var old = ec.Report.SetPrinter (errors_printer); + var expr = new ImplicitDelegateCreation (delegate_type, body.DirectMethodGroupConversion, loc) { + AllowSpecialMethodsInvocation = true + }.Resolve (ec); + ec.Report.SetPrinter (old); + if (expr != null && errors_printer.ErrorsCount == 0) + am = expr; + } + } + } catch (CompletionResult) { + throw; + } catch (FatalException) { + throw; + } catch (Exception e) { + throw new InternalErrorException (e, loc); + } + + if (!ec.IsInProbingMode && !etree_conversion) { + compatibles.Add (type, am ?? EmptyExpression.Null); + } + + return am; + } + + protected virtual Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type) + { + return CreateExpressionTree (ec); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (1946, loc, "An anonymous method cannot be converted to an expression tree"); + return null; + } + + protected virtual ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type) + { + var delegate_parameters = Delegate.GetParameters (delegate_type); + + if (Parameters == ParametersCompiled.Undefined) { + // + // We provide a set of inaccessible parameters + // + Parameter[] fixedpars = new Parameter[delegate_parameters.Count]; + + for (int i = 0; i < delegate_parameters.Count; i++) { + Parameter.Modifier i_mod = delegate_parameters.FixedParameters [i].ModFlags; + if ((i_mod & Parameter.Modifier.OUT) != 0) { + if (!ec.IsInProbingMode) { + ec.Report.Error (1688, loc, + "Cannot convert anonymous method block without a parameter list to delegate type `{0}' because it has one or more `out' parameters", + delegate_type.GetSignatureForError ()); + } + + return null; + } + fixedpars[i] = new Parameter ( + new TypeExpression (delegate_parameters.Types [i], loc), null, + delegate_parameters.FixedParameters [i].ModFlags, null, loc); + } + + return ParametersCompiled.CreateFullyResolved (fixedpars, delegate_parameters.Types); + } + + if (!VerifyExplicitParameters (ec, tic, delegate_type, delegate_parameters)) { + return null; + } + + return Parameters; + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (rc.HasSet (ResolveContext.Options.ConstantScope)) { + rc.Report.Error (1706, loc, "Anonymous methods and lambda expressions cannot be used in the current context"); + return null; + } + + // + // Update top-level block generated duting parsing with actual top-level block + // + if (rc.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.BaseInitializer) && rc.CurrentMemberDefinition.Parent.PartialContainer.PrimaryConstructorParameters != null) { + var tb = rc.ConstructorBlock.ParametersBlock.TopBlock; + if (Block.TopBlock != tb) { + Block b = Block; + while (b.Parent != Block.TopBlock && b != Block.TopBlock) + b = b.Parent; + + b.Parent = tb; + tb.IncludeBlock (Block, Block.TopBlock); + b.ParametersBlock.TopBlock = tb; + } + } + + eclass = ExprClass.Value; + + // + // This hack means `The type is not accessible + // anywhere', we depend on special conversion + // rules. + // + type = InternalType.AnonymousMethod; + + if (!DoResolveParameters (rc)) + return null; + + return this; + } + + protected virtual bool DoResolveParameters (ResolveContext rc) + { + return Parameters.Resolve (rc); + } + + public override void Emit (EmitContext ec) + { + // nothing, as we only exist to not do anything. + } + + public static void Error_AddressOfCapturedVar (ResolveContext rc, IVariableReference var, Location loc) + { + if (rc.CurrentAnonymousMethod is AsyncInitializer) + return; + + rc.Report.Error (1686, loc, + "Local variable or parameter `{0}' cannot have their address taken and be used inside an anonymous method, lambda expression or query expression", + var.Name); + } + + public override string GetSignatureForError () + { + return ExprClassName; + } + + AnonymousMethodBody CompatibleMethodBody (ResolveContext ec, TypeInferenceContext tic, TypeSpec return_type, TypeSpec delegate_type) + { + ParametersCompiled p = ResolveParameters (ec, tic, delegate_type); + if (p == null) + return null; + + ParametersBlock b = ec.IsInProbingMode ? (ParametersBlock) Block.PerformClone () : Block; + + if (b.IsAsync) { + var rt = return_type; + if (rt != null && rt.Kind != MemberKind.Void && rt != ec.Module.PredefinedTypes.Task.TypeSpec && !rt.IsGenericTask) { + ec.Report.Error (4010, loc, "Cannot convert async {0} to delegate type `{1}'", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + + return null; + } + + b = b.ConvertToAsyncTask (ec, ec.CurrentMemberDefinition.Parent.PartialContainer, p, return_type, delegate_type, loc); + } + + return CompatibleMethodFactory (return_type ?? InternalType.ErrorType, delegate_type, p, b); + } + + protected virtual AnonymousMethodBody CompatibleMethodFactory (TypeSpec return_type, TypeSpec delegate_type, ParametersCompiled p, ParametersBlock b) + { + return new AnonymousMethodBody (p, b, return_type, delegate_type, loc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + AnonymousMethodExpression target = (AnonymousMethodExpression) t; + + target.Block = (ParametersBlock) clonectx.LookupBlock (Block); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Abstract expression for any block which requires variables hoisting + // + public abstract class AnonymousExpression : ExpressionStatement + { + protected class AnonymousMethodMethod : Method + { + public readonly AnonymousExpression AnonymousMethod; + public readonly AnonymousMethodStorey Storey; + + public AnonymousMethodMethod (TypeDefinition parent, AnonymousExpression am, AnonymousMethodStorey storey, + TypeExpr return_type, + Modifiers mod, MemberName name, + ParametersCompiled parameters) + : base (parent, return_type, mod | Modifiers.COMPILER_GENERATED, + name, parameters, null) + { + this.AnonymousMethod = am; + this.Storey = storey; + + Parent.PartialContainer.Members.Add (this); + Block = new ToplevelBlock (am.block, parameters); + } + + public override EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + EmitContext ec = new EmitContext (this, ig, ReturnType, sourceMethod); + ec.CurrentAnonymousMethod = AnonymousMethod; + return ec; + } + + protected override void DefineTypeParameters () + { + // Type parameters were cloned + } + + protected override bool ResolveMemberType () + { + if (!base.ResolveMemberType ()) + return false; + + if (Storey != null && Storey.Mutator != null) { + if (!parameters.IsEmpty) { + var mutated = Storey.Mutator.Mutate (parameters.Types); + if (mutated != parameters.Types) + parameters = ParametersCompiled.CreateFullyResolved ((Parameter[]) parameters.FixedParameters, mutated); + } + + member_type = Storey.Mutator.Mutate (member_type); + } + + return true; + } + + public override void Emit () + { + if (MethodBuilder == null) { + Define (); + } + + base.Emit (); + } + } + + protected readonly ParametersBlock block; + + public TypeSpec ReturnType; + + protected AnonymousExpression (ParametersBlock block, TypeSpec return_type, Location loc) + { + this.ReturnType = return_type; + this.block = block; + this.loc = loc; + } + + public abstract string ContainerType { get; } + public abstract bool IsIterator { get; } + public abstract AnonymousMethodStorey Storey { get; } + + // + // The block that makes up the body for the anonymous method + // + public ParametersBlock Block { + get { + return block; + } + } + + public AnonymousExpression Compatible (ResolveContext ec) + { + return Compatible (ec, this); + } + + public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae) + { + if (block.Resolved) + return this; + + // TODO: Implement clone + BlockContext aec = new BlockContext (ec, block, ReturnType); + aec.CurrentAnonymousMethod = ae; + + var am = this as AnonymousMethodBody; + + if (ec.HasSet (ResolveContext.Options.InferReturnType) && am != null) { + am.ReturnTypeInference = new TypeInferenceContext (); + } + + var bc = ec as BlockContext; + + if (bc != null) { + aec.AssignmentInfoOffset = bc.AssignmentInfoOffset; + aec.EnclosingLoop = bc.EnclosingLoop; + aec.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch; + aec.Switch = bc.Switch; + } + + var errors = ec.Report.Errors; + + bool res = Block.Resolve (aec); + + if (res && errors == ec.Report.Errors) { + MarkReachable (new Reachability ()); + + if (!CheckReachableExit (ec.Report)) { + return null; + } + + if (bc != null) + bc.AssignmentInfoOffset = aec.AssignmentInfoOffset; + } + + if (am != null && am.ReturnTypeInference != null) { + am.ReturnTypeInference.FixAllTypes (ec); + ReturnType = am.ReturnTypeInference.InferredTypeArguments [0]; + am.ReturnTypeInference = null; + + // + // If e is synchronous the inferred return type is T + // If e is asynchronous and the body of F is either an expression classified as nothing + // or a statement block where no return statements have expressions, the inferred return type is Task + // If e is async and has an inferred result type T, the inferred return type is Task + // + if (block.IsAsync && ReturnType != null) { + ReturnType = ReturnType.Kind == MemberKind.Void ? + ec.Module.PredefinedTypes.Task.TypeSpec : + ec.Module.PredefinedTypes.TaskGeneric.TypeSpec.MakeGenericType (ec, new [] { ReturnType }); + } + } + + if (res && errors != ec.Report.Errors) + return null; + + return res ? this : null; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + bool CheckReachableExit (Report report) + { + if (block.HasReachableClosingBrace && ReturnType.Kind != MemberKind.Void) { + // FIXME: Flow-analysis on MoveNext generated code + if (!IsIterator) { + report.Error (1643, StartLocation, + "Not all code paths return a value in anonymous method of type `{0}'", GetSignatureForError ()); + + return false; + } + } + + return true; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + // We are reachable, mark block body reachable too + MarkReachable (new Reachability ()); + + CheckReachableExit (fc.Report); + + var das = fc.BranchDefiniteAssignment (); + var prev_pb = fc.ParametersBlock; + fc.ParametersBlock = Block; + var da_ontrue = fc.DefiniteAssignmentOnTrue; + var da_onfalse = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null; + block.FlowAnalysis (fc); + + fc.ParametersBlock = prev_pb; + fc.DefiniteAssignment = das; + fc.DefiniteAssignmentOnTrue = da_ontrue; + fc.DefiniteAssignmentOnFalse = da_onfalse; + } + + public override void MarkReachable (Reachability rc) + { + block.MarkReachable (rc); + } + + public void SetHasThisAccess () + { + ExplicitBlock b = block; + do { + if (b.HasCapturedThis) + return; + + b.HasCapturedThis = true; + b = b.Parent == null ? null : b.Parent.Explicit; + } while (b != null); + } + } + + public class AnonymousMethodBody : AnonymousExpression + { + protected readonly ParametersCompiled parameters; + AnonymousMethodStorey storey; + + AnonymousMethodMethod method; + Field am_cache; + string block_name; + TypeInferenceContext return_inference; + + public AnonymousMethodBody (ParametersCompiled parameters, + ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type, + Location loc) + : base (block, return_type, loc) + { + this.type = delegate_type; + this.parameters = parameters; + } + + #region Properties + + public override string ContainerType { + get { return "anonymous method"; } + } + + // + // Method-group instance for lambdas which can be replaced with + // simple method group call + // + public MethodGroupExpr DirectMethodGroupConversion { + get; set; + } + + public override bool IsIterator { + get { + return false; + } + } + + public ParametersCompiled Parameters { + get { + return parameters; + } + } + + public TypeInferenceContext ReturnTypeInference { + get { + return return_inference; + } + set { + return_inference = value; + } + } + + public override AnonymousMethodStorey Storey { + get { + return storey; + } + } + + #endregion + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (1945, loc, "An expression tree cannot contain an anonymous method expression"); + return null; + } + + bool Define (ResolveContext ec) + { + if (!Block.Resolved && Compatible (ec) == null) + return false; + + if (block_name == null) { + MemberCore mc = (MemberCore) ec.MemberContext; + block_name = mc.MemberName.Basename; + } + + return true; + } + + // + // Creates a host for the anonymous method + // + AnonymousMethodMethod DoCreateMethodHost (EmitContext ec) + { + // + // Anonymous method body can be converted to + // + // 1, an instance method in current scope when only `this' is hoisted + // 2, a static method in current scope when neither `this' nor any variable is hoisted + // 3, an instance method in compiler generated storey when any hoisted variable exists + // + + Modifiers modifiers; + TypeDefinition parent = null; + TypeParameters hoisted_tparams = null; + ParametersCompiled method_parameters = parameters; + + var src_block = Block.Original.Explicit; + if (src_block.HasCapturedVariable || src_block.HasCapturedThis) { + parent = storey = FindBestMethodStorey (); + + if (storey == null) { + var top_block = src_block.ParametersBlock.TopBlock; + var sm = top_block.StateMachine; + + if (src_block.HasCapturedThis) { + // + // Remove hoisted 'this' request when simple instance method is + // enough. No hoisted variables only 'this' and don't need to + // propagate this to value type state machine. + // + StateMachine sm_parent; + var pb = src_block.ParametersBlock; + do { + sm_parent = pb.StateMachine; + pb = pb.Parent == null ? null : pb.Parent.ParametersBlock; + } while (sm_parent == null && pb != null); + + if (sm_parent == null) { + top_block.RemoveThisReferenceFromChildrenBlock (src_block); + } else if (sm_parent.Kind == MemberKind.Struct) { + // + // Special case where parent class is used to emit instance method + // because currect storey is of value type (async host) and we cannot + // use ldftn on non-boxed instances either to share mutated state + // + parent = sm_parent.Parent.PartialContainer; + hoisted_tparams = sm_parent.OriginalTypeParameters; + } else if (sm is IteratorStorey) { + // + // For iterators we can host everything in one class + // + parent = storey = sm; + } + } + } + + modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE; + } else { + if (ec.CurrentAnonymousMethod != null) + parent = storey = ec.CurrentAnonymousMethod.Storey; + + modifiers = Modifiers.STATIC | Modifiers.PRIVATE; + + // + // Convert generated method to closed delegate method where unused + // this argument is generated during compilation which speeds up dispatch + // by about 25% + // + method_parameters = ParametersCompiled.Prefix (method_parameters, + new Parameter (null, null, 0, null, loc), ec.Module.Compiler.BuiltinTypes.Object); + } + + if (storey == null && hoisted_tparams == null) + hoisted_tparams = ec.CurrentTypeParameters; + + if (parent == null) + parent = ec.CurrentTypeDefinition.Parent.PartialContainer; + + string name = CompilerGeneratedContainer.MakeName (parent != storey ? block_name : null, + "m", null, parent.PartialContainer.CounterAnonymousMethods++); + + MemberName member_name; + if (hoisted_tparams != null) { + var type_params = new TypeParameters (hoisted_tparams.Count); + for (int i = 0; i < hoisted_tparams.Count; ++i) { + type_params.Add (hoisted_tparams[i].CreateHoistedCopy (null)); + } + + member_name = new MemberName (name, type_params, Location); + } else { + member_name = new MemberName (name, Location); + } + + return new AnonymousMethodMethod (parent, + this, storey, new TypeExpression (ReturnType, Location), modifiers, + member_name, method_parameters); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (!Define (ec)) + return null; + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + // + // Use same anonymous method implementation for scenarios where same + // code is used from multiple blocks, e.g. field initializers + // + if (method == null) { + // + // Delay an anonymous method definition to avoid emitting unused code + // for unreachable blocks or expression trees + // + method = DoCreateMethodHost (ec); + method.Define (); + method.PrepareEmit (); + } + + bool is_static = (method.ModFlags & Modifiers.STATIC) != 0; + if (is_static && am_cache == null && !ec.IsStaticConstructor) { + // + // Creates a field cache to store delegate instance if it's not generic + // + if (!method.MemberName.IsGeneric) { + var parent = method.Parent.PartialContainer; + int id = parent.AnonymousMethodsCounter++; + var cache_type = storey != null && storey.Mutator != null ? storey.Mutator.Mutate (type) : type; + + am_cache = new Field (parent, new TypeExpression (cache_type, loc), + Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, + new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "am$cache", id), loc), null); + am_cache.Define (); + parent.AddField (am_cache); + } else { + // TODO: Implement caching of generated generic static methods + // + // Idea: + // + // Some extra class is needed to capture variable generic type + // arguments. Maybe we could re-use anonymous types, with a unique + // anonymous method id, but they are quite heavy. + // + // Consider : "() => typeof(T);" + // + // We need something like + // static class Wrap { + // public static DelegateType cache; + // } + // + // We then specialize local variable to capture all generic parameters + // and delegate type, e.g. "Wrap cache;" + // + } + } + + Label l_initialized = ec.DefineLabel (); + + if (am_cache != null) { + ec.Emit (OpCodes.Ldsfld, am_cache.Spec); + ec.Emit (OpCodes.Brtrue_S, l_initialized); + } + + // + // Load method delegate implementation + // + + if (is_static) { + ec.EmitNull (); + } else if (storey != null) { + Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext)); + if (e != null) { + e.Emit (ec); + } + } else { + ec.EmitThis (); + + // + // Special case for value type storey where this is not lifted but + // droped off to parent class + // + if (ec.CurrentAnonymousMethod != null && ec.AsyncTaskStorey != null) + ec.Emit (OpCodes.Ldfld, ec.AsyncTaskStorey.HoistedThis.Field.Spec); + } + + var delegate_method = method.Spec; + if (storey != null && storey.MemberName.IsGeneric) { + TypeSpec t = storey.Instance.Type; + + // + // Mutate anonymous method instance type if we are in nested + // hoisted generic anonymous method storey + // + if (ec.IsAnonymousStoreyMutateRequired) { + t = storey.Mutator.Mutate (t); + } + + ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ())); + } else { + if (delegate_method.IsGeneric) { + TypeParameterSpec[] tparams; + var sm = ec.CurrentAnonymousMethod == null ? null : ec.CurrentAnonymousMethod.Storey as StateMachine; + if (sm != null && sm.OriginalTypeParameters != null) { + tparams = sm.CurrentTypeParameters.Types; + } else { + tparams = method.TypeParameters; + } + + delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, tparams); + } + + ec.Emit (OpCodes.Ldftn, delegate_method); + } + + var constructor_method = Delegate.GetConstructor (type); + ec.Emit (OpCodes.Newobj, constructor_method); + + if (am_cache != null) { + ec.Emit (OpCodes.Stsfld, am_cache.Spec); + ec.MarkLabel (l_initialized); + ec.Emit (OpCodes.Ldsfld, am_cache.Spec); + } + } + + public override void EmitStatement (EmitContext ec) + { + throw new NotImplementedException (); + } + + // + // Look for the best storey for this anonymous method + // + AnonymousMethodStorey FindBestMethodStorey () + { + // + // Use the nearest parent block which has a storey + // + for (Block b = Block.Parent; b != null; b = b.Parent) { + AnonymousMethodStorey s = b.Explicit.AnonymousMethodStorey; + if (s != null) + return s; + } + + return null; + } + + public override string GetSignatureForError () + { + return type.GetSignatureForError (); + } + } + + // + // Anonymous type container + // + public class AnonymousTypeClass : CompilerGeneratedContainer + { + public const string ClassNamePrefix = "<>__AnonType"; + public const string SignatureForError = "anonymous type"; + + readonly IList parameters; + + private AnonymousTypeClass (ModuleContainer parent, MemberName name, IList parameters, Location loc) + : base (parent, name, parent.Evaluator != null ? Modifiers.PUBLIC : Modifiers.INTERNAL) + { + this.parameters = parameters; + } + + public static AnonymousTypeClass Create (TypeContainer parent, IList parameters, Location loc) + { + string name = ClassNamePrefix + parent.Module.CounterAnonymousTypes++; + + ParametersCompiled all_parameters; + TypeParameters tparams = null; + SimpleName[] t_args; + + if (parameters.Count == 0) { + all_parameters = ParametersCompiled.EmptyReadOnlyParameters; + t_args = null; + } else { + t_args = new SimpleName[parameters.Count]; + tparams = new TypeParameters (); + Parameter[] ctor_params = new Parameter[parameters.Count]; + for (int i = 0; i < parameters.Count; ++i) { + AnonymousTypeParameter p = parameters[i]; + for (int ii = 0; ii < i; ++ii) { + if (parameters[ii].Name == p.Name) { + parent.Compiler.Report.Error (833, parameters[ii].Location, + "`{0}': An anonymous type cannot have multiple properties with the same name", + p.Name); + + p = new AnonymousTypeParameter (null, "$" + i.ToString (), p.Location); + parameters[i] = p; + break; + } + } + + t_args[i] = new SimpleName ("<" + p.Name + ">__T", p.Location); + tparams.Add (new TypeParameter (i, new MemberName (t_args[i].Name, p.Location), null, null, Variance.None)); + ctor_params[i] = new Parameter (t_args[i], p.Name, Parameter.Modifier.NONE, null, p.Location); + } + + all_parameters = new ParametersCompiled (ctor_params); + } + + // + // Create generic anonymous type host with generic arguments + // named upon properties names + // + AnonymousTypeClass a_type = new AnonymousTypeClass (parent.Module, new MemberName (name, tparams, loc), parameters, loc); + + Constructor c = new Constructor (a_type, name, Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN, + null, all_parameters, loc); + c.Block = new ToplevelBlock (parent.Module.Compiler, c.ParameterInfo, loc); + + // + // Create fields and constructor body with field initialization + // + bool error = false; + for (int i = 0; i < parameters.Count; ++i) { + AnonymousTypeParameter p = parameters [i]; + + Field f = new Field (a_type, t_args [i], Modifiers.PRIVATE | Modifiers.READONLY | Modifiers.DEBUGGER_HIDDEN, + new MemberName ("<" + p.Name + ">", p.Location), null); + + if (!a_type.AddField (f)) { + error = true; + continue; + } + + c.Block.AddStatement (new StatementExpression ( + new SimpleAssign (new MemberAccess (new This (p.Location), f.Name), + c.Block.GetParameterReference (i, p.Location)))); + + ToplevelBlock get_block = new ToplevelBlock (parent.Module.Compiler, p.Location); + get_block.AddStatement (new Return ( + new MemberAccess (new This (p.Location), f.Name), p.Location)); + + Property prop = new Property (a_type, t_args [i], Modifiers.PUBLIC, + new MemberName (p.Name, p.Location), null); + prop.Get = new Property.GetMethod (prop, 0, null, p.Location); + prop.Get.Block = get_block; + a_type.AddMember (prop); + } + + if (error) + return null; + + a_type.AddConstructor (c); + return a_type; + } + + protected override bool DoDefineMembers () + { + if (!base.DoDefineMembers ()) + return false; + + Location loc = Location; + + var equals_parameters = ParametersCompiled.CreateFullyResolved ( + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "obj", 0, null, loc), Compiler.BuiltinTypes.Object); + + Method equals = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Bool, loc), + Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("Equals", loc), + equals_parameters, null); + + equals_parameters[0].Resolve (equals, 0); + + Method tostring = new Method (this, new TypeExpression (Compiler.BuiltinTypes.String, loc), + Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("ToString", loc), + ParametersCompiled.EmptyReadOnlyParameters, null); + + ToplevelBlock equals_block = new ToplevelBlock (Compiler, equals.ParameterInfo, loc); + + TypeExpr current_type; + if (CurrentTypeParameters != null) { + var targs = new TypeArguments (); + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + targs.Add (new TypeParameterExpr (CurrentTypeParameters[i], Location)); + } + + current_type = new GenericTypeExpr (Definition, targs, loc); + } else { + current_type = new TypeExpression (Definition, loc); + } + + var li_other = LocalVariable.CreateCompilerGenerated (CurrentType, equals_block, loc); + equals_block.AddStatement (new BlockVariable (new TypeExpression (li_other.Type, loc), li_other)); + var other_variable = new LocalVariableReference (li_other, loc); + + MemberAccess system_collections_generic = new MemberAccess (new MemberAccess ( + new QualifiedAliasMember ("global", "System", loc), "Collections", loc), "Generic", loc); + + Expression rs_equals = null; + Expression string_concat = new StringConstant (Compiler.BuiltinTypes, "{", loc); + Expression rs_hashcode = new IntConstant (Compiler.BuiltinTypes, -2128831035, loc); + for (int i = 0; i < parameters.Count; ++i) { + var p = parameters [i]; + var f = (Field) Members [i * 2]; + + MemberAccess equality_comparer = new MemberAccess (new MemberAccess ( + system_collections_generic, "EqualityComparer", + new TypeArguments (new SimpleName (CurrentTypeParameters [i].Name, loc)), loc), + "Default", loc); + + Arguments arguments_equal = new Arguments (2); + arguments_equal.Add (new Argument (new MemberAccess (new This (f.Location), f.Name))); + arguments_equal.Add (new Argument (new MemberAccess (other_variable, f.Name))); + + Expression field_equal = new Invocation (new MemberAccess (equality_comparer, + "Equals", loc), arguments_equal); + + Arguments arguments_hashcode = new Arguments (1); + arguments_hashcode.Add (new Argument (new MemberAccess (new This (f.Location), f.Name))); + Expression field_hashcode = new Invocation (new MemberAccess (equality_comparer, + "GetHashCode", loc), arguments_hashcode); + + IntConstant FNV_prime = new IntConstant (Compiler.BuiltinTypes, 16777619, loc); + rs_hashcode = new Binary (Binary.Operator.Multiply, + new Binary (Binary.Operator.ExclusiveOr, rs_hashcode, field_hashcode), + FNV_prime); + + Expression field_to_string = new Conditional (new BooleanExpression (new Binary (Binary.Operator.Inequality, + new MemberAccess (new This (f.Location), f.Name), new NullLiteral (loc))), + new Invocation (new MemberAccess ( + new MemberAccess (new This (f.Location), f.Name), "ToString"), null), + new StringConstant (Compiler.BuiltinTypes, string.Empty, loc), loc); + + if (rs_equals == null) { + rs_equals = field_equal; + string_concat = new Binary (Binary.Operator.Addition, + string_concat, + new Binary (Binary.Operator.Addition, + new StringConstant (Compiler.BuiltinTypes, " " + p.Name + " = ", loc), + field_to_string)); + continue; + } + + // + // Implementation of ToString () body using string concatenation + // + string_concat = new Binary (Binary.Operator.Addition, + new Binary (Binary.Operator.Addition, + string_concat, + new StringConstant (Compiler.BuiltinTypes, ", " + p.Name + " = ", loc)), + field_to_string); + + rs_equals = new Binary (Binary.Operator.LogicalAnd, rs_equals, field_equal); + } + + string_concat = new Binary (Binary.Operator.Addition, + string_concat, + new StringConstant (Compiler.BuiltinTypes, " }", loc)); + + // + // Equals (object obj) override + // + var other_variable_assign = new TemporaryVariableReference (li_other, loc); + equals_block.AddStatement (new StatementExpression ( + new SimpleAssign (other_variable_assign, + new As (equals_block.GetParameterReference (0, loc), + current_type, loc), loc))); + + Expression equals_test = new Binary (Binary.Operator.Inequality, other_variable, new NullLiteral (loc)); + if (rs_equals != null) + equals_test = new Binary (Binary.Operator.LogicalAnd, equals_test, rs_equals); + equals_block.AddStatement (new Return (equals_test, loc)); + + equals.Block = equals_block; + equals.Define (); + Members.Add (equals); + + // + // GetHashCode () override + // + Method hashcode = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Int, loc), + Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, + new MemberName ("GetHashCode", loc), + ParametersCompiled.EmptyReadOnlyParameters, null); + + // + // Modified FNV with good avalanche behavior and uniform + // distribution with larger hash sizes. + // + // const int FNV_prime = 16777619; + // int hash = (int) 2166136261; + // foreach (int d in data) + // hash = (hash ^ d) * FNV_prime; + // hash += hash << 13; + // hash ^= hash >> 7; + // hash += hash << 3; + // hash ^= hash >> 17; + // hash += hash << 5; + + ToplevelBlock hashcode_top = new ToplevelBlock (Compiler, loc); + Block hashcode_block = new Block (hashcode_top, loc, loc); + hashcode_top.AddStatement (new Unchecked (hashcode_block, loc)); + + var li_hash = LocalVariable.CreateCompilerGenerated (Compiler.BuiltinTypes.Int, hashcode_top, loc); + hashcode_block.AddStatement (new BlockVariable (new TypeExpression (li_hash.Type, loc), li_hash)); + LocalVariableReference hash_variable_assign = new LocalVariableReference (li_hash, loc); + hashcode_block.AddStatement (new StatementExpression ( + new SimpleAssign (hash_variable_assign, rs_hashcode))); + + var hash_variable = new LocalVariableReference (li_hash, loc); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.Addition, hash_variable, + new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 13, loc))))); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable, + new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 7, loc))))); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.Addition, hash_variable, + new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 3, loc))))); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable, + new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 17, loc))))); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.Addition, hash_variable, + new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 5, loc))))); + + hashcode_block.AddStatement (new Return (hash_variable, loc)); + hashcode.Block = hashcode_top; + hashcode.Define (); + Members.Add (hashcode); + + // + // ToString () override + // + + ToplevelBlock tostring_block = new ToplevelBlock (Compiler, loc); + tostring_block.AddStatement (new Return (string_concat, loc)); + tostring.Block = tostring_block; + tostring.Define (); + Members.Add (tostring); + + return true; + } + + public override string GetSignatureForError () + { + return SignatureForError; + } + + public override CompilationSourceFile GetCompilationSourceFile () + { + return null; + } + + public IList Parameters { + get { + return parameters; + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs new file mode 100644 index 000000000..a5edeb53c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs @@ -0,0 +1,674 @@ +// +// argument.cs: Argument expressions +// +// Author: +// Miguel de Icaza (miguel@ximain.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// Copyright 2003-2011 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // + // Argument expression used for invocation + // + public class Argument + { + public enum AType : byte + { + None = 0, + Ref = 1, // ref modifier used + Out = 2, // out modifier used + Default = 3, // argument created from default parameter value + DynamicTypeName = 4, // System.Type argument for dynamic binding + ExtensionType = 5, // Instance expression inserted as the first argument + + // Conditional instance expression inserted as the first argument + ExtensionTypeConditionalAccess = 5 | ConditionalAccessFlag, + + ConditionalAccessFlag = 1 << 7 + } + + public readonly AType ArgType; + public Expression Expr; + + public Argument (Expression expr, AType type) + { + this.Expr = expr; + this.ArgType = type; + } + + public Argument (Expression expr) + { + this.Expr = expr; + } + + #region Properties + + public bool IsByRef { + get { return ArgType == AType.Ref || ArgType == AType.Out; } + } + + public bool IsDefaultArgument { + get { return ArgType == AType.Default; } + } + + public bool IsExtensionType { + get { + return (ArgType & AType.ExtensionType) == AType.ExtensionType; + } + } + + public Parameter.Modifier Modifier { + get { + switch (ArgType) { + case AType.Out: + return Parameter.Modifier.OUT; + + case AType.Ref: + return Parameter.Modifier.REF; + + default: + return Parameter.Modifier.NONE; + } + } + } + + public TypeSpec Type { + get { return Expr.Type; } + } + + #endregion + + public Argument Clone (Expression expr) + { + Argument a = (Argument) MemberwiseClone (); + a.Expr = expr; + return a; + } + + public Argument Clone (CloneContext clonectx) + { + return Clone (Expr.Clone (clonectx)); + } + + public virtual Expression CreateExpressionTree (ResolveContext ec) + { + if (ArgType == AType.Default) + ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter"); + + return Expr.CreateExpressionTree (ec); + } + + + public virtual void Emit (EmitContext ec) + { + if (!IsByRef) { + if (ArgType == AType.ExtensionTypeConditionalAccess) { + var ie = new InstanceEmitter (Expr, false); + ie.Emit (ec, true); + } else { + Expr.Emit (ec); + } + + return; + } + + AddressOp mode = AddressOp.Store; + if (ArgType == AType.Ref) + mode |= AddressOp.Load; + + IMemoryLocation ml = (IMemoryLocation) Expr; + ml.AddressOf (ec, mode); + } + + public Argument EmitToField (EmitContext ec, bool cloneResult) + { + var res = Expr.EmitToField (ec); + if (cloneResult && res != Expr) + return new Argument (res, ArgType); + + Expr = res; + return this; + } + + public void FlowAnalysis (FlowAnalysisContext fc) + { + if (ArgType == AType.Out) { + var vr = Expr as VariableReference; + if (vr != null) { + if (vr.VariableInfo != null) + fc.SetVariableAssigned (vr.VariableInfo); + + return; + } + + var fe = Expr as FieldExpr; + if (fe != null) { + fe.SetFieldAssigned (fc); + return; + } + + return; + } + + Expr.FlowAnalysis (fc); + } + + public string GetSignatureForError () + { + if (Expr.eclass == ExprClass.MethodGroup) + return Expr.ExprClassName; + + return Expr.Type.GetSignatureForError (); + } + + public bool ResolveMethodGroup (ResolveContext ec) + { + SimpleName sn = Expr as SimpleName; + if (sn != null) + Expr = sn.GetMethodGroup (); + + // FIXME: csc doesn't report any error if you try to use `ref' or + // `out' in a delegate creation expression. + Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup); + if (Expr == null) + return false; + + return true; + } + + public void Resolve (ResolveContext ec) + { + // Verify that the argument is readable + if (ArgType != AType.Out) + Expr = Expr.Resolve (ec); + + // Verify that the argument is writeable + if (Expr != null && IsByRef) + Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess); + + if (Expr == null) + Expr = ErrorExpression.Instance; + } + } + + public class MovableArgument : Argument + { + LocalTemporary variable; + + public MovableArgument (Argument arg) + : this (arg.Expr, arg.ArgType) + { + } + + protected MovableArgument (Expression expr, AType modifier) + : base (expr, modifier) + { + } + + public override void Emit (EmitContext ec) + { + // TODO: Should guard against multiple emits + base.Emit (ec); + + // Release temporary variable when used + if (variable != null) + variable.Release (ec); + } + + public void EmitToVariable (EmitContext ec) + { + var type = Expr.Type; + if (IsByRef) { + var ml = (IMemoryLocation) Expr; + ml.AddressOf (ec, AddressOp.LoadStore); + type = ReferenceContainer.MakeType (ec.Module, type); + } else { + Expr.Emit (ec); + } + + variable = new LocalTemporary (type); + variable.Store (ec); + + Expr = variable; + } + } + + public class NamedArgument : MovableArgument + { + public readonly string Name; + readonly Location loc; + + public NamedArgument (string name, Location loc, Expression expr) + : this (name, loc, expr, AType.None) + { + } + + public NamedArgument (string name, Location loc, Expression expr, AType modifier) + : base (expr, modifier) + { + this.Name = name; + this.loc = loc; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (853, loc, "An expression tree cannot contain named argument"); + return base.CreateExpressionTree (ec); + } + + public Location Location { + get { return loc; } + } + } + + public class Arguments + { + sealed class ArgumentsOrdered : Arguments + { + readonly List ordered; + + public ArgumentsOrdered (Arguments args) + : base (args.Count) + { + AddRange (args); + ordered = new List (); + } + + public void AddOrdered (MovableArgument arg) + { + ordered.Add (arg); + } + + public override void FlowAnalysis (FlowAnalysisContext fc, List movable = null) + { + foreach (var arg in ordered) { + if (arg.ArgType != Argument.AType.Out) + arg.FlowAnalysis (fc); + } + + base.FlowAnalysis (fc, ordered); + } + + public override Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait) + { + foreach (var a in ordered) { + if (prepareAwait) + a.EmitToField (ec, false); + else + a.EmitToVariable (ec); + } + + return base.Emit (ec, dup_args, prepareAwait); + } + } + + // Try not to add any more instances to this class, it's allocated a lot + List args; + + public Arguments (int capacity) + { + args = new List (capacity); + } + + private Arguments (List args) + { + this.args = args; + } + + public void Add (Argument arg) + { + args.Add (arg); + } + + public void AddRange (Arguments args) + { + this.args.AddRange (args.args); + } + + public bool ContainsEmitWithAwait () + { + foreach (var arg in args) { + if (arg.Expr.ContainsEmitWithAwait ()) + return true; + } + + return false; + } + + public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc) + { + Location loc = Location.Null; + var all = new ArrayInitializer (args.Count, loc); + + MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc); + + foreach (Argument a in args) { + Arguments dargs = new Arguments (2); + + // CSharpArgumentInfoFlags.None = 0 + const string info_flags_enum = "CSharpArgumentInfoFlags"; + Expression info_flags = new IntLiteral (rc.BuiltinTypes, 0, loc); + + if (a.Expr is Constant) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc)); + } else if (a.ArgType == Argument.AType.Ref) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", loc)); + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc)); + } else if (a.ArgType == Argument.AType.Out) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc)); + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc)); + } else if (a.ArgType == Argument.AType.DynamicTypeName) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc)); + } + + var arg_type = a.Expr.Type; + + if (arg_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && arg_type != InternalType.NullLiteral) { + MethodGroupExpr mg = a.Expr as MethodGroupExpr; + if (mg != null) { + rc.Report.Error (1976, a.Expr.Location, + "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method", + mg.Name); + } else if (arg_type == InternalType.AnonymousMethod) { + rc.Report.Error (1977, a.Expr.Location, + "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast"); + } else if (arg_type.Kind == MemberKind.Void || arg_type == InternalType.Arglist || arg_type.IsPointer) { + rc.Report.Error (1978, a.Expr.Location, + "An expression of type `{0}' cannot be used as an argument of dynamic operation", + arg_type.GetSignatureForError ()); + } + + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc)); + } + + string named_value; + NamedArgument na = a as NamedArgument; + if (na != null) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc)); + + named_value = na.Name; + } else { + named_value = null; + } + + dargs.Add (new Argument (info_flags)); + dargs.Add (new Argument (new StringLiteral (rc.BuiltinTypes, named_value, loc))); + all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs)); + } + + return all; + } + + public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e) + { + Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length); + for (int i = 0; i < e.Length; ++i) { + if (e [i] != null) + all.Add (new Argument (e[i])); + } + + if (args != null) { + foreach (Argument a in args.args) { + Expression tree_arg = a.CreateExpressionTree (ec); + if (tree_arg != null) + all.Add (new Argument (tree_arg)); + } + } + + return all; + } + + public void CheckArrayAsAttribute (CompilerContext ctx) + { + foreach (Argument arg in args) { + // Type is undefined (was error 246) + if (arg.Type == null) + continue; + + if (arg.Type.IsArray) + ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant"); + } + } + + public Arguments Clone (CloneContext ctx) + { + Arguments cloned = new Arguments (args.Count); + foreach (Argument a in args) + cloned.Add (a.Clone (ctx)); + + return cloned; + } + + public int Count { + get { return args.Count; } + } + + // + // Emits a list of resolved Arguments + // + public void Emit (EmitContext ec) + { + Emit (ec, false, false); + } + + // + // if `dup_args' is true or any of arguments contains await. + // A copy of all arguments will be returned to the caller + // + public virtual Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait) + { + List dups; + + if ((dup_args && Count != 0) || prepareAwait) + dups = new List (Count); + else + dups = null; + + LocalTemporary lt; + foreach (Argument a in args) { + if (prepareAwait) { + dups.Add (a.EmitToField (ec, true)); + continue; + } + + a.Emit (ec); + + if (!dup_args) { + continue; + } + + if (a.Expr.IsSideEffectFree) { + // + // No need to create a temporary variable for side effect free expressions. I assume + // all side-effect free expressions are cheap, this has to be tweaked when we become + // more aggressive on detection + // + dups.Add (a); + } else { + ec.Emit (OpCodes.Dup); + + // TODO: Release local temporary on next Emit + // Need to add a flag to argument to indicate this + lt = new LocalTemporary (a.Type); + lt.Store (ec); + + dups.Add (new Argument (lt, a.ArgType)); + } + } + + if (dups != null) + return new Arguments (dups); + + return null; + } + + public virtual void FlowAnalysis (FlowAnalysisContext fc, List movable = null) + { + bool has_out = false; + foreach (var arg in args) { + if (arg.ArgType == Argument.AType.Out) { + has_out = true; + continue; + } + + if (movable == null) { + arg.FlowAnalysis (fc); + continue; + } + + var ma = arg as MovableArgument; + if (ma != null && !movable.Contains (ma)) + arg.FlowAnalysis (fc); + } + + if (!has_out) + return; + + foreach (var arg in args) { + if (arg.ArgType != Argument.AType.Out) + continue; + + arg.FlowAnalysis (fc); + } + } + + public List.Enumerator GetEnumerator () + { + return args.GetEnumerator (); + } + + // + // At least one argument is of dynamic type + // + public bool HasDynamic { + get { + foreach (Argument a in args) { + if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef) + return true; + } + + return false; + } + } + + // + // At least one argument is named argument + // + public bool HasNamed { + get { + foreach (Argument a in args) { + if (a is NamedArgument) + return true; + } + + return false; + } + } + + + public void Insert (int index, Argument arg) + { + args.Insert (index, arg); + } + + public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx) + { + if (args == null || args.Count == 0) + return null; + + var exprs = new System.Linq.Expressions.Expression [args.Count]; + for (int i = 0; i < exprs.Length; ++i) { + Argument a = args.args [i]; + exprs[i] = a.Expr.MakeExpression (ctx); + } + + return exprs; + } + + // + // For named arguments when the order of execution is different + // to order of invocation + // + public Arguments MarkOrderedArgument (NamedArgument a) + { + // + // An expression has no effect on left-to-right execution + // + if (a.Expr.IsSideEffectFree) + return this; + + ArgumentsOrdered ra = this as ArgumentsOrdered; + if (ra == null) { + ra = new ArgumentsOrdered (this); + + for (int i = 0; i < args.Count; ++i) { + var la = args [i]; + if (la == a) + break; + + // + // When the argument is filled later by default expression + // + if (la == null) + continue; + + var ma = la as MovableArgument; + if (ma == null) { + ma = new MovableArgument (la); + ra.args[i] = ma; + } + + ra.AddOrdered (ma); + } + } + + ra.AddOrdered (a); + return ra; + } + + // + // Returns dynamic when at least one argument is of dynamic type + // + public void Resolve (ResolveContext ec, out bool dynamic) + { + dynamic = false; + foreach (Argument a in args) { + a.Resolve (ec); + if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef) + dynamic = true; + } + } + + public void RemoveAt (int index) + { + args.RemoveAt (index); + } + + public Argument this [int index] { + get { return args [index]; } + set { args [index] = value; } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assembly.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assembly.cs new file mode 100644 index 000000000..f93c03839 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assembly.cs @@ -0,0 +1,1257 @@ +// +// assembly.cs: Assembly declaration and specifications +// +// Authors: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004-2011 Novell, Inc. +// Copyright 2011-2013 Xamarin Inc +// + + +using System; +using System.IO; +using System.Collections.Generic; +using System.Globalization; +using System.Security; +using System.Security.Cryptography; +using System.Security.Permissions; +using Mono.Security.Cryptography; +using Mono.CompilerServices.SymbolWriter; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +using SecurityType = System.Collections.Generic.List; +#else +using SecurityType = System.Collections.Generic.Dictionary; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public interface IAssemblyDefinition + { + string FullName { get; } + bool IsCLSCompliant { get; } + bool IsMissing { get; } + string Name { get; } + + byte[] GetPublicKeyToken (); + bool IsFriendAssemblyTo (IAssemblyDefinition assembly); + } + + public abstract class AssemblyDefinition : IAssemblyDefinition + { + // TODO: make it private and move all builder based methods here + public AssemblyBuilder Builder; + protected AssemblyBuilderExtension builder_extra; + MonoSymbolFile symbol_writer; + + bool is_cls_compliant; + bool wrap_non_exception_throws; + bool wrap_non_exception_throws_custom; + bool has_user_debuggable; + + protected ModuleContainer module; + readonly string name; + protected readonly string file_name; + + byte[] public_key, public_key_token; + bool delay_sign; + + // Holds private/public key pair when private key + // was available + StrongNameKeyPair private_key; + + Attribute cls_attribute; + Method entry_point; + + protected List added_modules; + SecurityType declarative_security; + Dictionary emitted_forwarders; + AssemblyAttributesPlaceholder module_target_attrs; + + // Win32 version info values + string vi_product, vi_product_version, vi_company, vi_copyright, vi_trademark; + + protected AssemblyDefinition (ModuleContainer module, string name) + { + this.module = module; + this.name = Path.GetFileNameWithoutExtension (name); + + wrap_non_exception_throws = true; + + delay_sign = Compiler.Settings.StrongNameDelaySign; + + // + // Load strong name key early enough for assembly importer to be able to + // use the keys for InternalsVisibleTo + // This should go somewhere close to ReferencesLoading but don't have the place yet + // + if (Compiler.Settings.HasKeyFileOrContainer) { + LoadPublicKey (Compiler.Settings.StrongNameKeyFile, Compiler.Settings.StrongNameKeyContainer); + } + } + + protected AssemblyDefinition (ModuleContainer module, string name, string fileName) + : this (module, name) + { + this.file_name = fileName; + } + + #region Properties + + public Attribute CLSCompliantAttribute { + get { + return cls_attribute; + } + } + + public CompilerContext Compiler { + get { + return module.Compiler; + } + } + + // + // Assembly entry point, aka Main method + // + public Method EntryPoint { + get { + return entry_point; + } + set { + entry_point = value; + } + } + + public string FullName { + get { + return Builder.FullName; + } + } + + public bool HasCLSCompliantAttribute { + get { + return cls_attribute != null; + } + } + + // TODO: This should not exist here but will require more changes + public MetadataImporter Importer { + get; set; + } + + public bool IsCLSCompliant { + get { + return is_cls_compliant; + } + } + + bool IAssemblyDefinition.IsMissing { + get { + return false; + } + } + + public bool IsSatelliteAssembly { get; private set; } + + public string Name { + get { + return name; + } + } + + public bool WrapNonExceptionThrows { + get { + return wrap_non_exception_throws; + } + } + + protected Report Report { + get { + return Compiler.Report; + } + } + + public MonoSymbolFile SymbolWriter { + get { + return symbol_writer; + } + } + + #endregion + + public void AddModule (ImportedModuleDefinition module) + { + if (added_modules == null) { + added_modules = new List (); + added_modules.Add (module); + } + } + + public void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (a.Type == pa.AssemblyCulture) { + string value = a.GetString (); + if (value == null || value.Length == 0) + return; + + if (Compiler.Settings.Target == Target.Exe) { + Report.Error (7059, a.Location, "Executables cannot be satellite assemblies. Remove the attribute or keep it empty"); + return; + } + + if (value == "neutral") + value = ""; + + if (Compiler.Settings.Target == Target.Module) { + SetCustomAttribute (ctor, cdata); + } else { + builder_extra.SetCulture (value, a.Location); + } + + IsSatelliteAssembly = true; + return; + } + + if (a.Type == pa.AssemblyVersion) { + string value = a.GetString (); + if (value == null || value.Length == 0) + return; + + var vinfo = IsValidAssemblyVersion (value, true); + if (vinfo == null) { + Report.Error (7034, a.Location, "The specified version string `{0}' does not conform to the required format - major[.minor[.build[.revision]]]", + value); + return; + } + + if (Compiler.Settings.Target == Target.Module) { + SetCustomAttribute (ctor, cdata); + } else { + builder_extra.SetVersion (vinfo, a.Location); + } + + return; + } + + if (a.Type == pa.AssemblyAlgorithmId) { + const int pos = 2; // skip CA header + uint alg = (uint) cdata [pos]; + alg |= ((uint) cdata [pos + 1]) << 8; + alg |= ((uint) cdata [pos + 2]) << 16; + alg |= ((uint) cdata [pos + 3]) << 24; + + if (Compiler.Settings.Target == Target.Module) { + SetCustomAttribute (ctor, cdata); + } else { + builder_extra.SetAlgorithmId (alg, a.Location); + } + + return; + } + + if (a.Type == pa.AssemblyFlags) { + const int pos = 2; // skip CA header + uint flags = (uint) cdata[pos]; + flags |= ((uint) cdata [pos + 1]) << 8; + flags |= ((uint) cdata [pos + 2]) << 16; + flags |= ((uint) cdata [pos + 3]) << 24; + + // Ignore set PublicKey flag if assembly is not strongnamed + if ((flags & (uint) AssemblyNameFlags.PublicKey) != 0 && public_key == null) + flags &= ~(uint) AssemblyNameFlags.PublicKey; + + if (Compiler.Settings.Target == Target.Module) { + SetCustomAttribute (ctor, cdata); + } else { + builder_extra.SetFlags (flags, a.Location); + } + + return; + } + + if (a.Type == pa.TypeForwarder) { + TypeSpec t = a.GetArgumentType (); + if (t == null || TypeManager.HasElementType (t)) { + Report.Error (735, a.Location, "Invalid type specified as an argument for TypeForwardedTo attribute"); + return; + } + + if (emitted_forwarders == null) { + emitted_forwarders = new Dictionary (); + } else if (emitted_forwarders.ContainsKey (t.MemberDefinition)) { + Report.SymbolRelatedToPreviousError (emitted_forwarders[t.MemberDefinition].Location, null); + Report.Error (739, a.Location, "A duplicate type forward of type `{0}'", + t.GetSignatureForError ()); + return; + } + + emitted_forwarders.Add (t.MemberDefinition, a); + + if (t.MemberDefinition.DeclaringAssembly == this) { + Report.SymbolRelatedToPreviousError (t); + Report.Error (729, a.Location, "Cannot forward type `{0}' because it is defined in this assembly", + t.GetSignatureForError ()); + return; + } + + if (t.IsNested) { + Report.Error (730, a.Location, "Cannot forward type `{0}' because it is a nested type", + t.GetSignatureForError ()); + return; + } + + builder_extra.AddTypeForwarder (t.GetDefinition (), a.Location); + return; + } + + if (a.Type == pa.Extension) { + a.Error_MisusedExtensionAttribute (); + return; + } + + if (a.Type == pa.InternalsVisibleTo) { + string assembly_name = a.GetString (); + if (assembly_name == null) { + Report.Error (7030, a.Location, "Friend assembly reference cannot have `null' value"); + return; + } + + if (assembly_name.Length == 0) + return; +#if STATIC + ParsedAssemblyName aname; + ParseAssemblyResult r = Fusion.ParseAssemblyName (assembly_name, out aname); + if (r != ParseAssemblyResult.OK) { + Report.Warning (1700, 3, a.Location, "Friend assembly reference `{0}' is invalid and cannot be resolved", + assembly_name); + return; + } + + if (aname.Version != null || aname.Culture != null || aname.ProcessorArchitecture != ProcessorArchitecture.None) { + Report.Error (1725, a.Location, + "Friend assembly reference `{0}' is invalid. InternalsVisibleTo declarations cannot have a version, culture or processor architecture specified", + assembly_name); + + return; + } + + if (public_key != null && !aname.HasPublicKey) { + Report.Error (1726, a.Location, + "Friend assembly reference `{0}' is invalid. Strong named assemblies must specify a public key in their InternalsVisibleTo declarations", + assembly_name); + return; + } +#endif + } else if (a.Type == pa.RuntimeCompatibility) { + wrap_non_exception_throws_custom = true; + } else if (a.Type == pa.AssemblyFileVersion) { + vi_product_version = a.GetString (); + if (string.IsNullOrEmpty (vi_product_version) || IsValidAssemblyVersion (vi_product_version, false) == null) { + Report.Warning (7035, 1, a.Location, "The specified version string `{0}' does not conform to the recommended format major.minor.build.revision", + vi_product_version, a.Name); + return; + } + + // File version info decoding from blob is not supported + var cab = new CustomAttributeBuilder ((ConstructorInfo) ctor.GetMetaInfo (), new object[] { vi_product_version }); + Builder.SetCustomAttribute (cab); + return; + } else if (a.Type == pa.AssemblyProduct) { + vi_product = a.GetString (); + } else if (a.Type == pa.AssemblyCompany) { + vi_company = a.GetString (); + } else if (a.Type == pa.AssemblyDescription) { + // TODO: Needs extra api + } else if (a.Type == pa.AssemblyCopyright) { + vi_copyright = a.GetString (); + } else if (a.Type == pa.AssemblyTrademark) { + vi_trademark = a.GetString (); + } else if (a.Type == pa.Debuggable) { + has_user_debuggable = true; + } + + SetCustomAttribute (ctor, cdata); + } + + // + // When using assembly public key attributes InternalsVisibleTo key + // was not checked, we have to do it later when we actually know what + // our public key token is + // + void CheckReferencesPublicToken () + { + // TODO: It should check only references assemblies but there is + // no working SRE API + foreach (var entry in Importer.Assemblies) { + var a = entry as ImportedAssemblyDefinition; + if (a == null || a.IsMissing) + continue; + + if (public_key != null && !a.HasStrongName) { + Report.Error (1577, "Referenced assembly `{0}' does not have a strong name", + a.FullName); + } + + var ci = a.Assembly.GetName ().CultureInfo; + if (!ci.Equals (CultureInfo.InvariantCulture)) { + Report.Warning (8009, 1, "Referenced assembly `{0}' has different culture setting of `{1}'", + a.Name, ci.Name); + } + + if (!a.IsFriendAssemblyTo (this)) + continue; + + var attr = a.GetAssemblyVisibleToName (this); + var atoken = attr.GetPublicKeyToken (); + + if (ArrayComparer.IsEqual (GetPublicKeyToken (), atoken)) + continue; + + Report.SymbolRelatedToPreviousError (a.Location); + Report.Error (281, + "Friend access was granted to `{0}', but the output assembly is named `{1}'. Try adding a reference to `{0}' or change the output assembly name to match it", + attr.FullName, FullName); + } + } + + protected AssemblyName CreateAssemblyName () + { + var an = new AssemblyName (name); + + if (public_key != null && Compiler.Settings.Target != Target.Module) { + if (delay_sign) { + an.SetPublicKey (public_key); + } else { + if (public_key.Length == 16) { + Report.Error (1606, "Could not sign the assembly. ECMA key can only be used to delay-sign assemblies"); + } else if (private_key == null) { + Error_AssemblySigning ("The specified key file does not have a private key"); + } else { + an.KeyPair = private_key; + } + } + } + + return an; + } + + public virtual ModuleBuilder CreateModuleBuilder () + { + if (file_name == null) + throw new NotSupportedException ("transient module in static assembly"); + + var module_name = Path.GetFileName (file_name); + + // Always initialize module without symbolInfo. We could be framework dependent + // but returned ISymbolWriter does not have all what we need therefore some + // adaptor will be needed for now we alwayas emit MDB format when generating + // debug info + return Builder.DefineDynamicModule (module_name, module_name, false); + } + + public virtual void Emit () + { + if (Compiler.Settings.Target == Target.Module) { + module_target_attrs = new AssemblyAttributesPlaceholder (module, name); + module_target_attrs.CreateContainer (); + module_target_attrs.DefineContainer (); + module_target_attrs.Define (); + module.AddCompilerGeneratedClass (module_target_attrs); + } else if (added_modules != null) { + ReadModulesAssemblyAttributes (); + } + + if (Compiler.Settings.GenerateDebugInfo) { + symbol_writer = new MonoSymbolFile (); + } + + module.EmitContainer (); + + if (module.HasExtensionMethod) { + var pa = module.PredefinedAttributes.Extension; + if (pa.IsDefined) { + SetCustomAttribute (pa.Constructor, AttributeEncoder.Empty); + } + } + + if (!IsSatelliteAssembly) { + if (!has_user_debuggable && Compiler.Settings.GenerateDebugInfo) { + var pa = module.PredefinedAttributes.Debuggable; + if (pa.IsDefined) { + var modes = System.Diagnostics.DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints; + if (!Compiler.Settings.Optimize) + modes |= System.Diagnostics.DebuggableAttribute.DebuggingModes.DisableOptimizations; + + pa.EmitAttribute (Builder, modes); + } + } + + if (!wrap_non_exception_throws_custom) { + PredefinedAttribute pa = module.PredefinedAttributes.RuntimeCompatibility; + if (pa.IsDefined && pa.ResolveBuilder ()) { + var prop = module.PredefinedMembers.RuntimeCompatibilityWrapNonExceptionThrows.Get (); + if (prop != null) { + AttributeEncoder encoder = new AttributeEncoder (); + encoder.EncodeNamedPropertyArgument (prop, new BoolLiteral (Compiler.BuiltinTypes, true, Location.Null)); + SetCustomAttribute (pa.Constructor, encoder.ToArray ()); + } + } + } + + if (declarative_security != null) { +#if STATIC + foreach (var entry in declarative_security) { + Builder.__AddDeclarativeSecurity (entry); + } +#else + throw new NotSupportedException ("Assembly-level security"); +#endif + } + } + + CheckReferencesPublicToken (); + + SetEntryPoint (); + } + + public byte[] GetPublicKeyToken () + { + if (public_key == null || public_key_token != null) + return public_key_token; + + HashAlgorithm ha = SHA1.Create (); + byte[] hash = ha.ComputeHash (public_key); + // we need the last 8 bytes in reverse order + public_key_token = new byte[8]; + Buffer.BlockCopy (hash, hash.Length - 8, public_key_token, 0, 8); + Array.Reverse (public_key_token, 0, 8); + return public_key_token; + } + + // + // Either keyFile or keyContainer has to be non-null + // + void LoadPublicKey (string keyFile, string keyContainer) + { + if (keyContainer != null) { + try { + private_key = new StrongNameKeyPair (keyContainer); + public_key = private_key.PublicKey; + } catch { + Error_AssemblySigning ("The specified key container `" + keyContainer + "' does not exist"); + } + + return; + } + + bool key_file_exists = File.Exists (keyFile); + + // + // For attribute based KeyFile do additional lookup + // in output assembly path + // + if (!key_file_exists && Compiler.Settings.StrongNameKeyFile == null) { + // + // The key file can be relative to output assembly + // + string test_path = Path.Combine (Path.GetDirectoryName (file_name), keyFile); + key_file_exists = File.Exists (test_path); + if (key_file_exists) + keyFile = test_path; + } + + if (!key_file_exists) { + Error_AssemblySigning ("The specified key file `" + keyFile + "' does not exist"); + return; + } + + using (FileStream fs = new FileStream (keyFile, FileMode.Open, FileAccess.Read)) { + byte[] snkeypair = new byte[fs.Length]; + fs.Read (snkeypair, 0, snkeypair.Length); + + // check for ECMA key + if (snkeypair.Length == 16) { + public_key = snkeypair; + return; + } + + try { + // take it, with or without, a private key + RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair); + // and make sure we only feed the public part to Sys.Ref + byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa); + + // AssemblyName.SetPublicKey requires an additional header + byte[] publicKeyHeader = new byte[8] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00 }; + + // Encode public key + public_key = new byte[12 + publickey.Length]; + Buffer.BlockCopy (publicKeyHeader, 0, public_key, 0, publicKeyHeader.Length); + + // Length of Public Key (in bytes) + int lastPart = public_key.Length - 12; + public_key[8] = (byte) (lastPart & 0xFF); + public_key[9] = (byte) ((lastPart >> 8) & 0xFF); + public_key[10] = (byte) ((lastPart >> 16) & 0xFF); + public_key[11] = (byte) ((lastPart >> 24) & 0xFF); + + Buffer.BlockCopy (publickey, 0, public_key, 12, publickey.Length); + } catch { + Error_AssemblySigning ("The specified key file `" + keyFile + "' has incorrect format"); + return; + } + + if (delay_sign) + return; + + try { + // TODO: Is there better way to test for a private key presence ? + CryptoConvert.FromCapiPrivateKeyBlob (snkeypair); + private_key = new StrongNameKeyPair (snkeypair); + } catch { } + } + } + + void ReadModulesAssemblyAttributes () + { + foreach (var m in added_modules) { + var cattrs = m.ReadAssemblyAttributes (); + if (cattrs == null) + continue; + + module.OptAttributes.AddAttributes (cattrs); + } + } + + public void Resolve () + { + if (Compiler.Settings.Unsafe && module.PredefinedTypes.SecurityAction.Define ()) { + // + // Emits [assembly: SecurityPermissionAttribute (SecurityAction.RequestMinimum, SkipVerification = true)] + // when -unsafe option was specified + // + Location loc = Location.Null; + + MemberAccess system_security_permissions = new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Security", loc), "Permissions", loc); + + var req_min = module.PredefinedMembers.SecurityActionRequestMinimum.Resolve (loc); + + Arguments pos = new Arguments (1); + pos.Add (new Argument (req_min.GetConstant (null))); + + Arguments named = new Arguments (1); + named.Add (new NamedArgument ("SkipVerification", loc, new BoolLiteral (Compiler.BuiltinTypes, true, loc))); + + Attribute g = new Attribute ("assembly", + new MemberAccess (system_security_permissions, "SecurityPermissionAttribute"), + new Arguments[] { pos, named }, loc, false); + g.AttachTo (module, module); + + // Disable no-location warnings (e.g. obsolete) for compiler generated attribute + Compiler.Report.DisableReporting (); + try { + var ctor = g.Resolve (); + if (ctor != null) { + g.ExtractSecurityPermissionSet (ctor, ref declarative_security); + } + } finally { + Compiler.Report.EnableReporting (); + } + } + + if (module.OptAttributes == null) + return; + + // Ensure that we only have GlobalAttributes, since the Search isn't safe with other types. + if (!module.OptAttributes.CheckTargets()) + return; + + cls_attribute = module.ResolveAssemblyAttribute (module.PredefinedAttributes.CLSCompliant); + + if (cls_attribute != null) { + is_cls_compliant = cls_attribute.GetClsCompliantAttributeValue (); + } + + if (added_modules != null && Compiler.Settings.VerifyClsCompliance && is_cls_compliant) { + foreach (var m in added_modules) { + if (!m.IsCLSCompliant) { + Report.Error (3013, + "Added modules must be marked with the CLSCompliant attribute to match the assembly", + m.Name); + } + } + } + + Attribute a = module.ResolveAssemblyAttribute (module.PredefinedAttributes.RuntimeCompatibility); + if (a != null) { + var val = a.GetNamedValue ("WrapNonExceptionThrows") as BoolConstant; + if (val != null) + wrap_non_exception_throws = val.Value; + } + } + + protected void ResolveAssemblySecurityAttributes () + { + string key_file = null; + string key_container = null; + + if (module.OptAttributes != null) { + foreach (Attribute a in module.OptAttributes.Attrs) { + // cannot rely on any resolve-based members before you call Resolve + if (a.ExplicitTarget != "assembly") + continue; + + // TODO: This code is buggy: comparing Attribute name without resolving is wrong. + // However, this is invoked by CodeGen.Init, when none of the namespaces + // are loaded yet. + // TODO: Does not handle quoted attributes properly + switch (a.Name) { + case "AssemblyKeyFile": + case "AssemblyKeyFileAttribute": + case "System.Reflection.AssemblyKeyFileAttribute": + if (Compiler.Settings.StrongNameKeyFile != null) { + Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ()); + Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module", + "keyfile", "System.Reflection.AssemblyKeyFileAttribute"); + } else { + string value = a.GetString (); + if (!string.IsNullOrEmpty (value)) { + Error_ObsoleteSecurityAttribute (a, "keyfile"); + key_file = value; + } + } + break; + case "AssemblyKeyName": + case "AssemblyKeyNameAttribute": + case "System.Reflection.AssemblyKeyNameAttribute": + if (Compiler.Settings.StrongNameKeyContainer != null) { + Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ()); + Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module", + "keycontainer", "System.Reflection.AssemblyKeyNameAttribute"); + } else { + string value = a.GetString (); + if (!string.IsNullOrEmpty (value)) { + Error_ObsoleteSecurityAttribute (a, "keycontainer"); + key_container = value; + } + } + break; + case "AssemblyDelaySign": + case "AssemblyDelaySignAttribute": + case "System.Reflection.AssemblyDelaySignAttribute": + bool b = a.GetBoolean (); + if (b) { + Error_ObsoleteSecurityAttribute (a, "delaysign"); + } + + delay_sign = b; + break; + } + } + } + + // We came here only to report assembly attributes warnings + if (public_key != null) + return; + + // + // Load the strong key file found in attributes when no + // command line key was given + // + if (key_file != null || key_container != null) { + LoadPublicKey (key_file, key_container); + } else if (delay_sign) { + Report.Warning (1607, 1, "Delay signing was requested but no key file was given"); + } + } + + public void EmbedResources () + { + // + // Add Win32 resources + // + if (Compiler.Settings.Win32ResourceFile != null) { + Builder.DefineUnmanagedResource (Compiler.Settings.Win32ResourceFile); + } else { + Builder.DefineVersionInfoResource (vi_product, vi_product_version, vi_company, vi_copyright, vi_trademark); + } + + if (Compiler.Settings.Win32IconFile != null) { + builder_extra.DefineWin32IconResource (Compiler.Settings.Win32IconFile); + } + + if (Compiler.Settings.Resources != null) { + if (Compiler.Settings.Target == Target.Module) { + Report.Error (1507, "Cannot link resource file when building a module"); + } else { + int counter = 0; + foreach (var res in Compiler.Settings.Resources) { + if (!File.Exists (res.FileName)) { + Report.Error (1566, "Error reading resource file `{0}'", res.FileName); + continue; + } + + if (res.IsEmbeded) { + Stream stream; + if (counter++ < 10) { + stream = File.OpenRead (res.FileName); + } else { + // TODO: SRE API requires resource stream to be available during AssemblyBuilder::Save + // we workaround it by reading everything into memory to compile projects with + // many embedded resource (over 3500) references + stream = new MemoryStream (File.ReadAllBytes (res.FileName)); + } + + module.Builder.DefineManifestResource (res.Name, stream, res.Attributes); + } else { + Builder.AddResourceFile (res.Name, Path.GetFileName (res.FileName), res.Attributes); + } + } + } + } + } + + public void Save () + { + PortableExecutableKinds pekind = PortableExecutableKinds.ILOnly; + ImageFileMachine machine; + + switch (Compiler.Settings.Platform) { + case Platform.X86: + pekind |= PortableExecutableKinds.Required32Bit; + machine = ImageFileMachine.I386; + break; + case Platform.X64: + pekind |= PortableExecutableKinds.PE32Plus; + machine = ImageFileMachine.AMD64; + break; + case Platform.IA64: + machine = ImageFileMachine.IA64; + break; + case Platform.AnyCPU32Preferred: +#if STATIC + pekind |= PortableExecutableKinds.Preferred32Bit; + machine = ImageFileMachine.I386; + break; +#else + throw new NotSupportedException (); +#endif + case Platform.Arm: +#if STATIC + machine = ImageFileMachine.ARM; + break; +#else + throw new NotSupportedException (); +#endif + case Platform.AnyCPU: + default: + machine = ImageFileMachine.I386; + break; + } + + Compiler.TimeReporter.Start (TimeReporter.TimerType.OutputSave); + try { + if (Compiler.Settings.Target == Target.Module) { + SaveModule (pekind, machine); + } else { + Builder.Save (module.Builder.ScopeName, pekind, machine); + } + } catch (Exception e) { + Report.Error (16, "Could not write to file `" + name + "', cause: " + e.Message); + } + Compiler.TimeReporter.Stop (TimeReporter.TimerType.OutputSave); + + // Save debug symbols file + if (symbol_writer != null && Compiler.Report.Errors == 0) { + // TODO: it should run in parallel + Compiler.TimeReporter.Start (TimeReporter.TimerType.DebugSave); + + var filename = file_name + ".mdb"; + try { + // We mmap the file, so unlink the previous version since it may be in use + File.Delete (filename); + } catch { + // We can safely ignore + } + + module.WriteDebugSymbol (symbol_writer); + + using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) { + symbol_writer.CreateSymbolFile (module.Builder.ModuleVersionId, fs); + } + + Compiler.TimeReporter.Stop (TimeReporter.TimerType.DebugSave); + } + } + + protected virtual void SaveModule (PortableExecutableKinds pekind, ImageFileMachine machine) + { + Report.RuntimeMissingSupport (Location.Null, "-target:module"); + } + + void SetCustomAttribute (MethodSpec ctor, byte[] data) + { + if (module_target_attrs != null) + module_target_attrs.AddAssemblyAttribute (ctor, data); + else + Builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), data); + } + + void SetEntryPoint () + { + if (!Compiler.Settings.NeedsEntryPoint) { + if (Compiler.Settings.MainClass != null) + Report.Error (2017, "Cannot specify -main if building a module or library"); + + return; + } + + PEFileKinds file_kind; + + switch (Compiler.Settings.Target) { + case Target.Library: + case Target.Module: + file_kind = PEFileKinds.Dll; + break; + case Target.WinExe: + file_kind = PEFileKinds.WindowApplication; + break; + default: + file_kind = PEFileKinds.ConsoleApplication; + break; + } + + if (entry_point == null) { + string main_class = Compiler.Settings.MainClass; + if (main_class != null) { + // TODO: Handle dotted names + var texpr = module.GlobalRootNamespace.LookupType (module, main_class, 0, LookupMode.Probing, Location.Null); + if (texpr == null) { + Report.Error (1555, "Could not find `{0}' specified for Main method", main_class); + return; + } + + var mtype = texpr.MemberDefinition as ClassOrStruct; + if (mtype == null) { + Report.Error (1556, "`{0}' specified for Main method must be a valid class or struct", main_class); + return; + } + + Report.Error (1558, mtype.Location, "`{0}' does not have a suitable static Main method", mtype.GetSignatureForError ()); + } else { + string pname = file_name == null ? name : Path.GetFileName (file_name); + Report.Error (5001, "Program `{0}' does not contain a static `Main' method suitable for an entry point", + pname); + } + + return; + } + + Builder.SetEntryPoint (entry_point.MethodBuilder, file_kind); + } + + void Error_ObsoleteSecurityAttribute (Attribute a, string option) + { + Report.Warning (1699, 1, a.Location, + "Use compiler option `{0}' or appropriate project settings instead of `{1}' attribute", + option, a.Name); + } + + void Error_AssemblySigning (string text) + { + Report.Error (1548, "Error during assembly signing. " + text); + } + + public bool IsFriendAssemblyTo (IAssemblyDefinition assembly) + { + return false; + } + + static Version IsValidAssemblyVersion (string version, bool allowGenerated) + { + string[] parts = version.Split ('.'); + if (parts.Length < 1 || parts.Length > 4) + return null; + + var values = new int[4]; + for (int i = 0; i < parts.Length; ++i) { + if (!int.TryParse (parts[i], out values[i])) { + if (parts[i].Length == 1 && parts[i][0] == '*' && allowGenerated) { + if (i == 2) { + // Nothing can follow * + if (parts.Length > 3) + return null; + + // Generate Build value based on days since 1/1/2000 + TimeSpan days = DateTime.Today - new DateTime (2000, 1, 1); + values[i] = System.Math.Max (days.Days, 0); + i = 3; + } + + if (i == 3) { + // Generate Revision value based on every other second today + var seconds = DateTime.Now - DateTime.Today; + values[i] = (int) seconds.TotalSeconds / 2; + continue; + } + } + + return null; + } + + if (values[i] > ushort.MaxValue) + return null; + } + + return new Version (values[0], values[1], values[2], values[3]); + } + } + + public class AssemblyResource : IEquatable + { + public AssemblyResource (string fileName, string name) + : this (fileName, name, false) + { + } + + public AssemblyResource (string fileName, string name, bool isPrivate) + { + FileName = fileName; + Name = name; + Attributes = isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public; + } + + public ResourceAttributes Attributes { get; private set; } + public string Name { get; private set; } + public string FileName { get; private set; } + public bool IsEmbeded { get; set; } + + #region IEquatable Members + + public bool Equals (AssemblyResource other) + { + return Name == other.Name; + } + + #endregion + } + + // + // A placeholder class for assembly attributes when emitting module + // + class AssemblyAttributesPlaceholder : CompilerGeneratedContainer + { + static readonly string TypeNamePrefix = "<$AssemblyAttributes${0}>"; + public static readonly string AssemblyFieldName = "attributes"; + + Field assembly; + + public AssemblyAttributesPlaceholder (ModuleContainer parent, string outputName) + : base (parent, new MemberName (GetGeneratedName (outputName)), Modifiers.STATIC | Modifiers.INTERNAL) + { + assembly = new Field (this, new TypeExpression (parent.Compiler.BuiltinTypes.Object, Location), Modifiers.PUBLIC | Modifiers.STATIC, + new MemberName (AssemblyFieldName), null); + + AddField (assembly); + } + + public void AddAssemblyAttribute (MethodSpec ctor, byte[] data) + { + assembly.SetCustomAttribute (ctor, data); + } + + public static string GetGeneratedName (string outputName) + { + return string.Format (TypeNamePrefix, outputName); + } + } + + // + // Extension to System.Reflection.Emit.AssemblyBuilder to have fully compatible + // compiler. This is a default implementation for framework System.Reflection.Emit + // which does not implement any of the methods + // + public class AssemblyBuilderExtension + { + readonly CompilerContext ctx; + + public AssemblyBuilderExtension (CompilerContext ctx) + { + this.ctx = ctx; + } + + public virtual System.Reflection.Module AddModule (string module) + { + ctx.Report.RuntimeMissingSupport (Location.Null, "-addmodule"); + return null; + } + + public virtual void AddPermissionRequests (PermissionSet[] permissions) + { + ctx.Report.RuntimeMissingSupport (Location.Null, "assembly declarative security"); + } + + public virtual void AddTypeForwarder (TypeSpec type, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "TypeForwardedToAttribute"); + } + + public virtual void DefineWin32IconResource (string fileName) + { + ctx.Report.RuntimeMissingSupport (Location.Null, "-win32icon"); + } + + public virtual void SetAlgorithmId (uint value, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "AssemblyAlgorithmIdAttribute"); + } + + public virtual void SetCulture (string culture, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "AssemblyCultureAttribute"); + } + + public virtual void SetFlags (uint flags, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "AssemblyFlagsAttribute"); + } + + public virtual void SetVersion (Version version, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "AssemblyVersionAttribute"); + } + } + + abstract class AssemblyReferencesLoader where T : class + { + protected readonly CompilerContext compiler; + + protected readonly List paths; + + protected AssemblyReferencesLoader (CompilerContext compiler) + { + this.compiler = compiler; + + paths = new List (); + paths.Add (Directory.GetCurrentDirectory ()); + paths.AddRange (compiler.Settings.ReferencesLookupPaths); + } + + public abstract bool HasObjectType (T assembly); + protected abstract string[] GetDefaultReferences (); + public abstract T LoadAssemblyFile (string fileName, bool isImplicitReference); + public abstract void LoadReferences (ModuleContainer module); + + protected void Error_FileNotFound (string fileName) + { + compiler.Report.Error (6, "Metadata file `{0}' could not be found", fileName); + } + + protected void Error_FileCorrupted (string fileName) + { + compiler.Report.Error (9, "Metadata file `{0}' does not contain valid metadata", fileName); + } + + protected void Error_AssemblyIsModule (string fileName) + { + compiler.Report.Error (1509, + "Referenced assembly file `{0}' is a module. Consider using `-addmodule' option to add the module", + fileName); + } + + protected void Error_ModuleIsAssembly (string fileName) + { + compiler.Report.Error (1542, + "Added module file `{0}' is an assembly. Consider using `-r' option to reference the file", + fileName); + } + + protected void LoadReferencesCore (ModuleContainer module, out T corlib_assembly, out List> loaded) + { + compiler.TimeReporter.Start (TimeReporter.TimerType.ReferencesLoading); + + loaded = new List> (); + + // + // Load mscorlib.dll as the first + // + if (module.Compiler.Settings.StdLib) { + corlib_assembly = LoadAssemblyFile ("mscorlib.dll", true); + } else { + corlib_assembly = default (T); + } + + T a; + foreach (string r in module.Compiler.Settings.AssemblyReferences) { + a = LoadAssemblyFile (r, false); + if (a == null || EqualityComparer.Default.Equals (a, corlib_assembly)) + continue; + + var key = Tuple.Create (module.GlobalRootNamespace, a); + if (loaded.Contains (key)) + continue; + + loaded.Add (key); + } + + if (corlib_assembly == null) { + // + // Requires second pass because HasObjectType can trigger assembly load event + // + for (int i = 0; i < loaded.Count; ++i) { + var assembly = loaded [i]; + + // + // corlib assembly is the first referenced assembly which contains System.Object + // + if (HasObjectType (assembly.Item2)) { + corlib_assembly = assembly.Item2; + loaded.RemoveAt (i); + break; + } + } + } + + foreach (var entry in module.Compiler.Settings.AssemblyReferencesAliases) { + a = LoadAssemblyFile (entry.Item2, false); + if (a == null) + continue; + + var key = Tuple.Create (module.CreateRootNamespace (entry.Item1), a); + if (loaded.Contains (key)) + continue; + + loaded.Add (key); + } + + if (compiler.Settings.LoadDefaultReferences) { + foreach (string r in GetDefaultReferences ()) { + a = LoadAssemblyFile (r, true); + if (a == null) + continue; + + var key = Tuple.Create (module.GlobalRootNamespace, a); + if (loaded.Contains (key)) + continue; + + loaded.Add (key); + } + } + + compiler.TimeReporter.Stop (TimeReporter.TimerType.ReferencesLoading); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assign.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assign.cs new file mode 100644 index 000000000..2bdc3331a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assign.cs @@ -0,0 +1,956 @@ +// +// assign.cs: Assignments. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// +using System; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// This interface is implemented by expressions that can be assigned to. + /// + /// + /// This interface is implemented by Expressions whose values can not + /// store the result on the top of the stack. + /// + /// Expressions implementing this (Properties, Indexers and Arrays) would + /// perform an assignment of the Expression "source" into its final + /// location. + /// + /// No values on the top of the stack are expected to be left by + /// invoking this method. + /// + public interface IAssignMethod { + // + // This is an extra version of Emit. If leave_copy is `true' + // A copy of the expression will be left on the stack at the + // end of the code generated for EmitAssign + // + void Emit (EmitContext ec, bool leave_copy); + + // + // This method does the assignment + // `source' will be stored into the location specified by `this' + // if `leave_copy' is true, a copy of `source' will be left on the stack + // if `prepare_for_load' is true, when `source' is emitted, there will + // be data on the stack that it can use to compuatate its value. This is + // for expressions like a [f ()] ++, where you can't call `f ()' twice. + // + void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound); + + /* + For simple assignments, this interface is very simple, EmitAssign is called with source + as the source expression and leave_copy and prepare_for_load false. + + For compound assignments it gets complicated. + + EmitAssign will be called as before, however, prepare_for_load will be + true. The @source expression will contain an expression + which calls Emit. So, the calls look like: + + this.EmitAssign (ec, source, false, true) -> + source.Emit (ec); -> + [...] -> + this.Emit (ec, false); -> + end this.Emit (ec, false); -> + end [...] + end source.Emit (ec); + end this.EmitAssign (ec, source, false, true) + + + When prepare_for_load is true, EmitAssign emits a `token' on the stack that + Emit will use for its state. + + Let's take FieldExpr as an example. assume we are emitting f ().y += 1; + + Here is the call tree again. This time, each call is annotated with the IL + it produces: + + this.EmitAssign (ec, source, false, true) + call f + dup + + Binary.Emit () + this.Emit (ec, false); + ldfld y + end this.Emit (ec, false); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + stfld + end this.EmitAssign (ec, source, false, true) + + Observe two things: + 1) EmitAssign left a token on the stack. It was the result of f (). + 2) This token was used by Emit + + leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy + of the expression at that point in evaluation. This is used for pre/post inc/dec + and for a = x += y. Let's do the above example with leave_copy true in EmitAssign + + this.EmitAssign (ec, source, true, true) + call f + dup + + Binary.Emit () + this.Emit (ec, false); + ldfld y + end this.Emit (ec, false); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + dup + stloc temp + stfld + ldloc temp + end this.EmitAssign (ec, source, true, true) + + And with it true in Emit + + this.EmitAssign (ec, source, false, true) + call f + dup + + Binary.Emit () + this.Emit (ec, true); + ldfld y + dup + stloc temp + end this.Emit (ec, true); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + stfld + ldloc temp + end this.EmitAssign (ec, source, false, true) + + Note that these two examples are what happens for ++x and x++, respectively. + */ + } + + /// + /// An Expression to hold a temporary value. + /// + /// + /// The LocalTemporary class is used to hold temporary values of a given + /// type to "simulate" the expression semantics. The local variable is + /// never captured. + /// + /// The local temporary is used to alter the normal flow of code generation + /// basically it creates a local variable, and its emit instruction generates + /// code to access this value, return its address or save its value. + /// + /// If `is_address' is true, then the value that we store is the address to the + /// real value, and not the value itself. + /// + /// This is needed for a value type, because otherwise you just end up making a + /// copy of the value on the stack and modifying it. You really need a pointer + /// to the origional value so that you can modify it in that location. This + /// Does not happen with a class because a class is a pointer -- so you always + /// get the indirection. + /// + /// + public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod { + LocalBuilder builder; + + public LocalTemporary (TypeSpec t) + { + type = t; + eclass = ExprClass.Value; + } + + public LocalTemporary (LocalBuilder b, TypeSpec t) + : this (t) + { + builder = b; + } + + public void Release (EmitContext ec) + { + ec.FreeTemporaryLocal (builder, type); + builder = null; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (1); + args.Add (new Argument (this)); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return this; + } + + public override void Emit (EmitContext ec) + { + if (builder == null) + throw new InternalErrorException ("Emit without Store, or after Release"); + + ec.Emit (OpCodes.Ldloc, builder); + } + + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + Emit (ec); + + if (leave_copy) + Emit (ec); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + if (isCompound) + throw new NotImplementedException (); + + source.Emit (ec); + + Store (ec); + + if (leave_copy) + Emit (ec); + } + + #endregion + + public LocalBuilder Builder { + get { return builder; } + } + + public void Store (EmitContext ec) + { + if (builder == null) + builder = ec.GetTemporaryLocal (type); + + ec.Emit (OpCodes.Stloc, builder); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + if (builder == null) + builder = ec.GetTemporaryLocal (type); + + if (builder.LocalType.IsByRef) { + // + // if is_address, than this is just the address anyways, + // so we just return this. + // + ec.Emit (OpCodes.Ldloc, builder); + } else { + ec.Emit (OpCodes.Ldloca, builder); + } + } + } + + /// + /// The Assign node takes care of assigning the value of source into + /// the expression represented by target. + /// + public abstract class Assign : ExpressionStatement { + protected Expression target, source; + + protected Assign (Expression target, Expression source, Location loc) + { + this.target = target; + this.source = source; + this.loc = loc; + } + + public Expression Target { + get { return target; } + } + + public Expression Source { + get { + return source; + } + } + + public override Location StartLocation { + get { + return target.StartLocation; + } + } + + public override bool ContainsEmitWithAwait () + { + return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator"); + return null; + } + + protected override Expression DoResolve (ResolveContext ec) + { + bool ok = true; + source = source.Resolve (ec); + + if (source == null) { + ok = false; + source = ErrorExpression.Instance; + } + + target = target.ResolveLValue (ec, source); + + if (target == null || !ok) + return null; + + TypeSpec target_type = target.Type; + TypeSpec source_type = source.Type; + + eclass = ExprClass.Value; + type = target_type; + + if (!(target is IAssignMethod)) { + target.Error_ValueAssignment (ec, source); + return null; + } + + if (target_type != source_type) { + Expression resolved = ResolveConversions (ec); + + if (resolved != this) + return resolved; + } + + return this; + } + +#if NET_4_0 || MOBILE_DYNAMIC + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { + var tassign = target as IDynamicAssign; + if (tassign == null) + throw new InternalErrorException (target.GetType () + " does not support dynamic assignment"); + + var target_object = tassign.MakeAssignExpression (ctx, source); + + // + // Some hacking is needed as DLR does not support void type and requires + // always have object convertible return type to support caching and chaining + // + // We do this by introducing an explicit block which returns RHS value when + // available or null + // + if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block) + return target_object; + + System.Linq.Expressions.UnaryExpression source_object; + if (ctx.HasSet (BuilderContext.Options.CheckedScope)) { + source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type); + } else { + source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type); + } + + return System.Linq.Expressions.Expression.Assign (target_object, source_object); + } +#endif + protected virtual Expression ResolveConversions (ResolveContext ec) + { + source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location); + if (source == null) + return null; + + return this; + } + + void Emit (EmitContext ec, bool is_statement) + { + IAssignMethod t = (IAssignMethod) target; + t.EmitAssign (ec, source, !is_statement, this is CompoundAssign); + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public override void EmitStatement (EmitContext ec) + { + Emit (ec, true); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + source.FlowAnalysis (fc); + + if (target is ArrayAccess || target is IndexerExpr || target is PropertyExpr) + target.FlowAnalysis (fc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Assign _target = (Assign) t; + + _target.target = target.Clone (clonectx); + _target.source = source.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class SimpleAssign : Assign + { + public SimpleAssign (Expression target, Expression source) + : this (target, source, target.Location) + { + } + + public SimpleAssign (Expression target, Expression source, Location loc) + : base (target, source, loc) + { + } + + bool CheckEqualAssign (Expression t) + { + if (source is Assign) { + Assign a = (Assign) source; + if (t.Equals (a.Target)) + return true; + return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t); + } + return t.Equals (source); + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression e = base.DoResolve (ec); + if (e == null || e != this) + return e; + + if (CheckEqualAssign (target)) + ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?"); + + return this; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + + var vr = target as VariableReference; + if (vr != null) { + if (vr.VariableInfo != null) + fc.SetVariableAssigned (vr.VariableInfo); + + return; + } + + var fe = target as FieldExpr; + if (fe != null) { + fe.SetFieldAssigned (fc); + return; + } + } + + public override void MarkReachable (Reachability rc) + { + var es = source as ExpressionStatement; + if (es != null) + es.MarkReachable (rc); + } + } + + public class RuntimeExplicitAssign : Assign + { + public RuntimeExplicitAssign (Expression target, Expression source) + : base (target, source, target.Location) + { + } + + protected override Expression ResolveConversions (ResolveContext ec) + { + source = EmptyCast.Create (source, target.Type); + return this; + } + } + + // + // Compiler generated assign + // + class CompilerAssign : Assign + { + public CompilerAssign (Expression target, Expression source, Location loc) + : base (target, source, loc) + { + if (target.Type != null) { + type = target.Type; + eclass = ExprClass.Value; + } + } + + protected override Expression DoResolve (ResolveContext ec) + { + var expr = base.DoResolve (ec); + var vr = target as VariableReference; + if (vr != null && vr.VariableInfo != null) + vr.VariableInfo.IsEverAssigned = false; + + return expr; + } + + public void UpdateSource (Expression source) + { + base.source = source; + } + } + + // + // Implements fields and events class initializers + // + public class FieldInitializer : Assign + { + // + // Field initializers are tricky for partial classes. They have to + // share same constructor (block) for expression trees resolve but + // they have they own resolve scope + // + sealed class FieldInitializerContext : BlockContext + { + readonly ExplicitBlock ctor_block; + + public FieldInitializerContext (IMemberContext mc, BlockContext constructorContext) + : base (mc, null, constructorContext.ReturnType) + { + flags |= Options.FieldInitializerScope | Options.ConstructorScope; + this.ctor_block = constructorContext.CurrentBlock.Explicit; + + if (ctor_block.IsCompilerGenerated) + CurrentBlock = ctor_block; + } + + public override ExplicitBlock ConstructorBlock { + get { + return ctor_block; + } + } + } + + // + // Keep resolved value because field initializers have their own rules + // + ExpressionStatement resolved; + FieldBase mc; + + public FieldInitializer (FieldBase mc, Expression expression, Location loc) + : base (new FieldExpr (mc.Spec, expression.Location), expression, loc) + { + this.mc = mc; + if (!mc.IsStatic) + ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location); + } + + public int AssignmentOffset { get; private set; } + + public FieldBase Field { + get { + return mc; + } + } + + public override Location StartLocation { + get { + return loc; + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + // Field initializer can be resolved (fail) many times + if (source == null) + return null; + + if (resolved == null) { + var bc = (BlockContext) rc; + var ctx = new FieldInitializerContext (mc, bc); + resolved = base.DoResolve (ctx) as ExpressionStatement; + AssignmentOffset = ctx.AssignmentInfoOffset - bc.AssignmentInfoOffset; + } + + return resolved; + } + + public override void EmitStatement (EmitContext ec) + { + if (resolved == null) + return; + + // + // Emit sequence symbol info even if we are in compiler generated + // block to allow debugging field initializers when constructor is + // compiler generated + // + if (ec.HasSet (BuilderContext.Options.OmitDebugInfo) && ec.HasMethodSymbolBuilder) { + using (ec.With (BuilderContext.Options.OmitDebugInfo, false)) { + ec.Mark (loc); + } + } + + if (resolved != this) + resolved.EmitStatement (ec); + else + base.EmitStatement (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + source.FlowAnalysis (fc); + ((FieldExpr) target).SetFieldAssigned (fc); + } + + public bool IsDefaultInitializer { + get { + Constant c = source as Constant; + if (c == null) + return false; + + FieldExpr fe = (FieldExpr)target; + return c.IsDefaultInitializer (fe.Type); + } + } + + public override bool IsSideEffectFree { + get { + return source.IsSideEffectFree; + } + } + } + + class PrimaryConstructorAssign : SimpleAssign + { + readonly Field field; + readonly Parameter parameter; + + public PrimaryConstructorAssign (Field field, Parameter parameter) + : base (null, null, parameter.Location) + { + this.field = field; + this.parameter = parameter; + } + + protected override Expression DoResolve (ResolveContext rc) + { + target = new FieldExpr (field, loc); + source = rc.CurrentBlock.ParametersBlock.GetParameterInfo (parameter).CreateReferenceExpression (rc, loc); + return base.DoResolve (rc); + } + + public override void EmitStatement (EmitContext ec) + { + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + base.EmitStatement (ec); + } + } + } + + // + // This class is used for compound assignments. + // + public class CompoundAssign : Assign + { + // This is just a hack implemented for arrays only + public sealed class TargetExpression : Expression + { + readonly Expression child; + + public TargetExpression (Expression child) + { + this.child = child; + this.loc = child.Location; + } + + public override bool ContainsEmitWithAwait () + { + return child.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = child.Type; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + } + + public override Expression EmitToField (EmitContext ec) + { + return child.EmitToField (ec); + } + } + + // Used for underlying binary operator + readonly Binary.Operator op; + Expression right; + Expression left; + + public Binary.Operator Op { + get { + return op; + } + } + + public CompoundAssign (Binary.Operator op, Expression target, Expression source) + : base (target, source, target.Location) + { + right = source; + this.op = op; + } + + public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left) + : this (op, target, source) + { + this.left = left; + } + + public Binary.Operator Operator { + get { + return op; + } + } + + protected override Expression DoResolve (ResolveContext ec) + { + right = right.Resolve (ec); + if (right == null) + return null; + + MemberAccess ma = target as MemberAccess; + using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) { + target = target.Resolve (ec); + } + + if (target == null) + return null; + + if (target is MethodGroupExpr){ + ec.Report.Error (1656, loc, + "Cannot assign to `{0}' because it is a `{1}'", + ((MethodGroupExpr)target).Name, target.ExprClassName); + return null; + } + + var event_expr = target as EventExpr; + if (event_expr != null) { + source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc); + if (source == null) + return null; + + Expression rside; + if (op == Binary.Operator.Addition) + rside = EmptyExpression.EventAddition; + else if (op == Binary.Operator.Subtraction) + rside = EmptyExpression.EventSubtraction; + else + rside = null; + + target = target.ResolveLValue (ec, rside); + if (target == null) + return null; + + eclass = ExprClass.Value; + type = event_expr.Operator.ReturnType; + return this; + } + + // + // Only now we can decouple the original source/target + // into a tree, to guarantee that we do not have side + // effects. + // + if (left == null) + left = new TargetExpression (target); + + source = new Binary (op, left, right, true); + + if (target is DynamicMemberAssignable) { + Arguments targs = ((DynamicMemberAssignable) target).Arguments; + source = source.Resolve (ec); + + Arguments args = new Arguments (targs.Count + 1); + args.AddRange (targs); + args.Add (new Argument (source)); + + var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment; + + // + // Compound assignment does target conversion using additional method + // call, set checked context as the binary operation can overflow + // + if (ec.HasSet (ResolveContext.Options.CheckedScope)) + binder_flags |= CSharpBinderFlags.CheckedContext; + + if (target is DynamicMemberBinder) { + source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec); + + // Handles possible event addition/subtraction + if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) { + args = new Arguments (targs.Count + 1); + args.AddRange (targs); + args.Add (new Argument (right)); + string method_prefix = op == Binary.Operator.Addition ? + Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix; + + var invoke = DynamicInvocation.CreateSpecialNameInvoke ( + new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec); + + args = new Arguments (targs.Count); + args.AddRange (targs); + source = new DynamicEventCompoundAssign (ma.Name, args, + (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec); + } + } else { + source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec); + } + + return source; + } + + return base.DoResolve (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + target.FlowAnalysis (fc); + source.FlowAnalysis (fc); + } + + protected override Expression ResolveConversions (ResolveContext ec) + { + // + // LAMESPEC: Under dynamic context no target conversion is happening + // This allows more natual dynamic behaviour but breaks compatibility + // with static binding + // + if (target is RuntimeValueExpression) + return this; + + TypeSpec target_type = target.Type; + + // + // 1. the return type is implicitly convertible to the type of target + // + if (Convert.ImplicitConversionExists (ec, source, target_type)) { + source = Convert.ImplicitConversion (ec, source, target_type, loc); + return this; + } + + // + // Otherwise, if the selected operator is a predefined operator + // + Binary b = source as Binary; + if (b == null) { + if (source is ReducedExpression) + b = ((ReducedExpression) source).OriginalExpression as Binary; + else if (source is ReducedExpression.ReducedConstantExpression) { + b = ((ReducedExpression.ReducedConstantExpression) source).OriginalExpression as Binary; + } else if (source is Nullable.LiftedBinaryOperator) { + var po = ((Nullable.LiftedBinaryOperator) source); + if (po.UserOperator == null) + b = po.Binary; + } else if (source is TypeCast) { + b = ((TypeCast) source).Child as Binary; + } + } + + if (b != null) { + // + // 2a. the operator is a shift operator + // + // 2b. the return type is explicitly convertible to the type of x, and + // y is implicitly convertible to the type of x + // + if ((b.Oper & Binary.Operator.ShiftMask) != 0 || + Convert.ImplicitConversionExists (ec, right, target_type)) { + source = Convert.ExplicitConversion (ec, source, target_type, loc); + return this; + } + } + + if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments arg = new Arguments (1); + arg.Add (new Argument (source)); + return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec); + } + + right.Error_ValueCannotBeConverted (ec, target_type, false); + return null; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CompoundAssign ctarget = (CompoundAssign) t; + + ctarget.right = ctarget.source = source.Clone (clonectx); + ctarget.target = target.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/async.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/async.cs new file mode 100644 index 000000000..68bbd3e18 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/async.cs @@ -0,0 +1,1005 @@ +// +// async.cs: Asynchronous functions +// +// Author: +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2011 Novell, Inc. +// Copyright 2011-2012 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Collections; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public class Await : ExpressionStatement + { + Expression expr; + AwaitStatement stmt; + + public Expression Expression { + get { + return expr; + } + } + + public Await (Expression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public Expression Expr { + get { + return expr; + } + } + + public AwaitStatement Statement { + get { + return stmt; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + var t = (Await) target; + + t.expr = expr.Clone (clonectx); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotImplementedException ("ET"); + } + + public override bool ContainsEmitWithAwait () + { + return true; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + stmt.Expr.FlowAnalysis (fc); + + stmt.RegisterResumePoint (); + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (rc.HasSet (ResolveContext.Options.LockScope)) { + rc.Report.Error (1996, loc, + "The `await' operator cannot be used in the body of a lock statement"); + } + + if (rc.IsUnsafe) { + rc.Report.Error (4004, loc, + "The `await' operator cannot be used in an unsafe context"); + } + + var bc = (BlockContext) rc; + + stmt = new AwaitStatement (expr, loc); + if (!stmt.Resolve (bc)) + return null; + + type = stmt.ResultType; + eclass = ExprClass.Variable; + return this; + } + + public override void Emit (EmitContext ec) + { + stmt.EmitPrologue (ec); + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + stmt.Emit (ec); + } + } + + public override Expression EmitToField (EmitContext ec) + { + stmt.EmitPrologue (ec); + return stmt.GetResultExpression (ec); + } + + public void EmitAssign (EmitContext ec, FieldExpr field) + { + stmt.EmitPrologue (ec); + field.InstanceExpression.Emit (ec); + stmt.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + stmt.EmitStatement (ec); + } + + public override void MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + stmt.MarkReachable (rc); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class AwaitStatement : YieldStatement + { + public sealed class AwaitableMemberAccess : MemberAccess + { + public AwaitableMemberAccess (Expression expr) + : base (expr, "GetAwaiter") + { + } + + public bool ProbingMode { get; set; } + + protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name) + { + Error_OperatorCannotBeApplied (rc, type); + } + + protected override void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type) + { + if (ProbingMode) + return; + + var invocation = LeftExpression as Invocation; + if (invocation != null && invocation.MethodGroup != null && (invocation.MethodGroup.BestCandidate.Modifiers & Modifiers.ASYNC) != 0) { + rc.Report.Error (4008, loc, "Cannot await void method `{0}'. Consider changing method return type to `Task'", + invocation.GetSignatureForError ()); + } else { + rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ()); + } + } + } + + sealed class GetResultInvocation : Invocation + { + public GetResultInvocation (MethodGroupExpr mge, Arguments arguments) + : base (null, arguments) + { + mg = mge; + type = mg.BestCandidateReturnType; + } + + public override Expression EmitToField (EmitContext ec) + { + return this; + } + } + + Field awaiter; + AwaiterDefinition awaiter_definition; + TypeSpec type; + TypeSpec result_type; + + public AwaitStatement (Expression expr, Location loc) + : base (expr, loc) + { + unwind_protect = true; + } + + #region Properties + + bool IsDynamic { + get { + return awaiter_definition == null; + } + } + + public TypeSpec ResultType { + get { + return result_type; + } + } + + #endregion + + protected override void DoEmit (EmitContext ec) + { + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + GetResultExpression (ec).Emit (ec); + } + } + + public Expression GetResultExpression (EmitContext ec) + { + var fe_awaiter = new FieldExpr (awaiter, loc); + fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + + // + // result = awaiter.GetResult (); + // + if (IsDynamic) { + var rc = new ResolveContext (ec.MemberContext); + return new Invocation (new MemberAccess (fe_awaiter, "GetResult"), new Arguments (0)).Resolve (rc); + } + + var mg_result = MethodGroupExpr.CreatePredefined (awaiter_definition.GetResult, fe_awaiter.Type, loc); + mg_result.InstanceExpression = fe_awaiter; + + return new GetResultInvocation (mg_result, new Arguments (0)); + } + + public void EmitPrologue (EmitContext ec) + { + awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (expr.Type); + + var fe_awaiter = new FieldExpr (awaiter, loc); + fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + + Label skip_continuation = ec.DefineLabel (); + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + // + // awaiter = expr.GetAwaiter (); + // + fe_awaiter.EmitAssign (ec, expr, false, false); + + Expression completed_expr; + if (IsDynamic) { + var rc = new ResolveContext (ec.MemberContext); + + Arguments dargs = new Arguments (1); + dargs.Add (new Argument (fe_awaiter)); + completed_expr = new DynamicMemberBinder ("IsCompleted", dargs, loc).Resolve (rc); + + dargs = new Arguments (1); + dargs.Add (new Argument (completed_expr)); + completed_expr = new DynamicConversion (ec.Module.Compiler.BuiltinTypes.Bool, 0, dargs, loc).Resolve (rc); + } else { + var pe = PropertyExpr.CreatePredefined (awaiter_definition.IsCompleted, loc); + pe.InstanceExpression = fe_awaiter; + completed_expr = pe; + } + + completed_expr.EmitBranchable (ec, skip_continuation, true); + } + + base.DoEmit (ec); + + // + // The stack has to be empty before calling await continuation. We handle this + // by lifting values which would be left on stack into class fields. The process + // is quite complicated and quite hard to test because any expression can possibly + // leave a value on the stack. + // + // Following assert fails when some of expression called before is missing EmitToField + // or parent expression fails to find await in children expressions + // + ec.AssertEmptyStack (); + + var storey = (AsyncTaskStorey) machine_initializer.Storey; + if (IsDynamic) { + storey.EmitAwaitOnCompletedDynamic (ec, fe_awaiter); + } else { + storey.EmitAwaitOnCompleted (ec, fe_awaiter); + } + + // Return ok + machine_initializer.EmitLeave (ec, unwind_protect); + + ec.MarkLabel (resume_point); + ec.MarkLabel (skip_continuation); + } + + public void EmitStatement (EmitContext ec) + { + EmitPrologue (ec); + DoEmit (ec); + + awaiter.IsAvailableForReuse = true; + + if (ResultType.Kind != MemberKind.Void) + ec.Emit (OpCodes.Pop); + } + + void Error_WrongAwaiterPattern (ResolveContext rc, TypeSpec awaiter) + { + rc.Report.Error (4011, loc, "The awaiter type `{0}' must have suitable IsCompleted and GetResult members", + awaiter.GetSignatureForError ()); + } + + public override bool Resolve (BlockContext bc) + { + if (bc.CurrentBlock is Linq.QueryBlock) { + bc.Report.Error (1995, loc, + "The `await' operator may only be used in a query expression within the first collection expression of the initial `from' clause or within the collection expression of a `join' clause"); + return false; + } + + if (!base.Resolve (bc)) + return false; + + type = expr.Type; + Arguments args = new Arguments (0); + + // + // The await expression is of dynamic type + // + if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + result_type = type; + expr = new Invocation (new MemberAccess (expr, "GetAwaiter"), args).Resolve (bc); + return true; + } + + // + // Check whether the expression is awaitable + // + Expression ama = new AwaitableMemberAccess (expr).Resolve (bc); + if (ama == null) + return false; + + var errors_printer = new SessionReportPrinter (); + var old = bc.Report.SetPrinter (errors_printer); + ama = new Invocation (ama, args).Resolve (bc); + bc.Report.SetPrinter (old); + + if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) { + bc.Report.Error (1986, expr.Location, + "The `await' operand type `{0}' must have suitable GetAwaiter method", + expr.Type.GetSignatureForError ()); + + return false; + } + + var awaiter_type = ama.Type; + + awaiter_definition = bc.Module.GetAwaiter (awaiter_type); + + if (!awaiter_definition.IsValidPattern) { + Error_WrongAwaiterPattern (bc, awaiter_type); + return false; + } + + if (!awaiter_definition.INotifyCompletion) { + bc.Report.Error (4027, loc, "The awaiter type `{0}' must implement interface `{1}'", + awaiter_type.GetSignatureForError (), bc.Module.PredefinedTypes.INotifyCompletion.GetSignatureForError ()); + return false; + } + + expr = ama; + result_type = awaiter_definition.GetResult.ReturnType; + + return true; + } + } + + class AsyncInitializerStatement : StatementExpression + { + public AsyncInitializerStatement (AsyncInitializer expr) + : base (expr) + { + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + base.DoFlowAnalysis (fc); + + var init = (AsyncInitializer) Expr; + var res = !init.Block.HasReachableClosingBrace; + var storey = (AsyncTaskStorey) init.Storey; + + if (storey.ReturnType.IsGenericTask) + return res; + + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (!rc.IsUnreachable) + reachable = true; + + var init = (AsyncInitializer) Expr; + rc = init.Block.MarkReachable (rc); + + var storey = (AsyncTaskStorey) init.Storey; + + // + // Explicit return is required for Task state machine + // + if (storey.ReturnType != null && storey.ReturnType.IsGenericTask) + return rc; + + return Reachability.CreateUnreachable (); + } + } + + public class AsyncInitializer : StateMachineInitializer + { + TypeInferenceContext return_inference; + + public AsyncInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType) + : base (block, host, returnType) + { + } + + #region Properties + + public override string ContainerType { + get { + return "async state machine block"; + } + } + + public TypeSpec DelegateType { + get; set; + } + + public StackFieldExpr HoistedReturnState { + get; set; + } + + public override bool IsIterator { + get { + return false; + } + } + + public TypeInferenceContext ReturnTypeInference { + get { + return return_inference; + } + } + + #endregion + + protected override BlockContext CreateBlockContext (BlockContext bc) + { + var ctx = base.CreateBlockContext (bc); + var am = bc.CurrentAnonymousMethod as AnonymousMethodBody; + if (am != null) + return_inference = am.ReturnTypeInference; + + ctx.Set (ResolveContext.Options.TryScope); + + return ctx; + } + + public override void Emit (EmitContext ec) + { + throw new NotImplementedException (); + } + + public void EmitCatchBlock (EmitContext ec) + { + var catch_value = LocalVariable.CreateCompilerGenerated (ec.Module.Compiler.BuiltinTypes.Exception, block, Location); + + ec.BeginCatchBlock (catch_value.Type); + catch_value.EmitAssign (ec); + + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + ((AsyncTaskStorey) Storey).EmitSetException (ec, new LocalVariableReference (catch_value, Location)); + + ec.Emit (OpCodes.Leave, move_next_ok); + ec.EndExceptionBlock (); + + } + + protected override void EmitMoveNextEpilogue (EmitContext ec) + { + var storey = (AsyncTaskStorey) Storey; + storey.EmitSetResult (ec); + } + + public override void EmitStatement (EmitContext ec) + { + var storey = (AsyncTaskStorey) Storey; + storey.EmitInitializer (ec); + ec.Emit (OpCodes.Ret); + } + + public override void MarkReachable (Reachability rc) + { + // + // Reachability has been done in AsyncInitializerStatement + // + } + } + + class AsyncTaskStorey : StateMachine + { + int awaiters; + Field builder; + readonly TypeSpec return_type; + MethodSpec set_result; + MethodSpec set_exception; + MethodSpec builder_factory; + MethodSpec builder_start; + PropertySpec task; + int locals_captured; + Dictionary> stack_fields; + Dictionary> awaiter_fields; + + public AsyncTaskStorey (ParametersBlock block, IMemberContext context, AsyncInitializer initializer, TypeSpec type) + : base (block, initializer.Host, context.CurrentMemberDefinition as MemberBase, context.CurrentTypeParameters, "async", MemberKind.Struct) + { + return_type = type; + awaiter_fields = new Dictionary> (); + } + + #region Properties + + public Expression HoistedReturnValue { get; set; } + + public TypeSpec ReturnType { + get { + return return_type; + } + } + + public PropertySpec Task { + get { + return task; + } + } + + protected override TypeAttributes TypeAttr { + get { + return base.TypeAttr & ~TypeAttributes.SequentialLayout; + } + } + + #endregion + + public Field AddAwaiter (TypeSpec type) + { + if (mutator != null) + type = mutator.Mutate (type); + + List existing_fields; + if (awaiter_fields.TryGetValue (type, out existing_fields)) { + foreach (var f in existing_fields) { + if (f.IsAvailableForReuse) { + f.IsAvailableForReuse = false; + return f; + } + } + } + + var field = AddCompilerGeneratedField ("$awaiter" + awaiters++.ToString ("X"), new TypeExpression (type, Location), true); + field.Define (); + + if (existing_fields == null) { + existing_fields = new List (); + awaiter_fields.Add (type, existing_fields); + } + + existing_fields.Add (field); + return field; + } + + public Field AddCapturedLocalVariable (TypeSpec type, bool requiresUninitialized = false) + { + if (mutator != null) + type = mutator.Mutate (type); + + List existing_fields = null; + if (stack_fields == null) { + stack_fields = new Dictionary> (); + } else if (stack_fields.TryGetValue (type, out existing_fields) && !requiresUninitialized) { + foreach (var f in existing_fields) { + if (f.IsAvailableForReuse) { + f.IsAvailableForReuse = false; + return f; + } + } + } + + var field = AddCompilerGeneratedField ("$stack" + locals_captured++.ToString ("X"), new TypeExpression (type, Location), true); + field.Define (); + + if (existing_fields == null) { + existing_fields = new List (); + stack_fields.Add (type, existing_fields); + } + + existing_fields.Add (field); + + return field; + } + + protected override bool DoDefineMembers () + { + PredefinedType builder_type; + PredefinedMember bf; + PredefinedMember bs; + PredefinedMember sr; + PredefinedMember se; + PredefinedMember sm; + bool has_task_return_type = false; + var pred_members = Module.PredefinedMembers; + + if (return_type.Kind == MemberKind.Void) { + builder_type = Module.PredefinedTypes.AsyncVoidMethodBuilder; + bf = pred_members.AsyncVoidMethodBuilderCreate; + bs = pred_members.AsyncVoidMethodBuilderStart; + sr = pred_members.AsyncVoidMethodBuilderSetResult; + se = pred_members.AsyncVoidMethodBuilderSetException; + sm = pred_members.AsyncVoidMethodBuilderSetStateMachine; + } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) { + builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilder; + bf = pred_members.AsyncTaskMethodBuilderCreate; + bs = pred_members.AsyncTaskMethodBuilderStart; + sr = pred_members.AsyncTaskMethodBuilderSetResult; + se = pred_members.AsyncTaskMethodBuilderSetException; + sm = pred_members.AsyncTaskMethodBuilderSetStateMachine; + task = pred_members.AsyncTaskMethodBuilderTask.Get (); + } else { + builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilderGeneric; + bf = pred_members.AsyncTaskMethodBuilderGenericCreate; + bs = pred_members.AsyncTaskMethodBuilderGenericStart; + sr = pred_members.AsyncTaskMethodBuilderGenericSetResult; + se = pred_members.AsyncTaskMethodBuilderGenericSetException; + sm = pred_members.AsyncTaskMethodBuilderGenericSetStateMachine; + task = pred_members.AsyncTaskMethodBuilderGenericTask.Get (); + has_task_return_type = true; + } + + set_result = sr.Get (); + set_exception = se.Get (); + builder_factory = bf.Get (); + builder_start = bs.Get (); + + var istate_machine = Module.PredefinedTypes.IAsyncStateMachine; + var set_statemachine = sm.Get (); + + if (!builder_type.Define () || !istate_machine.Define () || set_result == null || builder_factory == null || + set_exception == null || set_statemachine == null || builder_start == null || + !Module.PredefinedTypes.INotifyCompletion.Define ()) { + Report.Error (1993, Location, + "Cannot find compiler required types for asynchronous functions support. Are you targeting the wrong framework version?"); + return base.DoDefineMembers (); + } + + var bt = builder_type.TypeSpec; + + // + // Inflate generic Task types + // + if (has_task_return_type) { + var task_return_type = return_type.TypeArguments; + if (mutator != null) + task_return_type = mutator.Mutate (task_return_type); + + bt = bt.MakeGenericType (Module, task_return_type); + set_result = MemberCache.GetMember (bt, set_result); + set_exception = MemberCache.GetMember (bt, set_exception); + set_statemachine = MemberCache.GetMember (bt, set_statemachine); + + if (task != null) + task = MemberCache.GetMember (bt, task); + } + + builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location)); + + var set_state_machine = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Void, Location), + Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN | Modifiers.PUBLIC, + new MemberName ("SetStateMachine"), + ParametersCompiled.CreateFullyResolved ( + new Parameter (new TypeExpression (istate_machine.TypeSpec, Location), "stateMachine", Parameter.Modifier.NONE, null, Location), + istate_machine.TypeSpec), + null); + + ToplevelBlock block = new ToplevelBlock (Compiler, set_state_machine.ParameterInfo, Location); + block.IsCompilerGenerated = true; + set_state_machine.Block = block; + + Members.Add (set_state_machine); + + if (!base.DoDefineMembers ()) + return false; + + // + // Fabricates SetStateMachine method + // + // public void SetStateMachine (IAsyncStateMachine stateMachine) + // { + // $builder.SetStateMachine (stateMachine); + // } + // + var mg = MethodGroupExpr.CreatePredefined (set_statemachine, bt, Location); + mg.InstanceExpression = new FieldExpr (builder, Location); + + var param_reference = block.GetParameterReference (0, Location); + param_reference.Type = istate_machine.TypeSpec; + param_reference.eclass = ExprClass.Variable; + + var args = new Arguments (1); + args.Add (new Argument (param_reference)); + set_state_machine.Block.AddStatement (new StatementExpression (new Invocation (mg, args))); + + if (has_task_return_type) { + HoistedReturnValue = TemporaryVariableReference.Create (bt.TypeArguments [0], StateMachineMethod.Block, Location); + } + + return true; + } + + public void EmitAwaitOnCompletedDynamic (EmitContext ec, FieldExpr awaiter) + { + var critical = Module.PredefinedTypes.ICriticalNotifyCompletion; + if (!critical.Define ()) { + throw new NotImplementedException (); + } + + var temp_critical = new LocalTemporary (critical.TypeSpec); + var label_critical = ec.DefineLabel (); + var label_end = ec.DefineLabel (); + + // + // Special path for dynamic awaiters + // + // var awaiter = this.$awaiter as ICriticalNotifyCompletion; + // if (awaiter == null) { + // var completion = (INotifyCompletion) this.$awaiter; + // this.$builder.AwaitOnCompleted (ref completion, ref this); + // } else { + // this.$builder.AwaitUnsafeOnCompleted (ref awaiter, ref this); + // } + // + awaiter.Emit (ec); + ec.Emit (OpCodes.Isinst, critical.TypeSpec); + temp_critical.Store (ec); + temp_critical.Emit (ec); + ec.Emit (OpCodes.Brtrue_S, label_critical); + + var temp = new LocalTemporary (Module.PredefinedTypes.INotifyCompletion.TypeSpec); + awaiter.Emit (ec); + ec.Emit (OpCodes.Castclass, temp.Type); + temp.Store (ec); + EmitOnCompleted (ec, temp, false); + temp.Release (ec); + ec.Emit (OpCodes.Br_S, label_end); + + ec.MarkLabel (label_critical); + + EmitOnCompleted (ec, temp_critical, true); + + ec.MarkLabel (label_end); + + temp_critical.Release (ec); + } + + public void EmitAwaitOnCompleted (EmitContext ec, FieldExpr awaiter) + { + bool unsafe_version = false; + if (Module.PredefinedTypes.ICriticalNotifyCompletion.Define ()) { + unsafe_version = awaiter.Type.ImplementsInterface (Module.PredefinedTypes.ICriticalNotifyCompletion.TypeSpec, false); + } + + EmitOnCompleted (ec, awaiter, unsafe_version); + } + + void EmitOnCompleted (EmitContext ec, Expression awaiter, bool unsafeVersion) + { + var pm = Module.PredefinedMembers; + PredefinedMember predefined; + bool has_task_return_type = false; + if (return_type.Kind == MemberKind.Void) { + predefined = unsafeVersion ? pm.AsyncVoidMethodBuilderOnCompletedUnsafe : pm.AsyncVoidMethodBuilderOnCompleted; + } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) { + predefined = unsafeVersion ? pm.AsyncTaskMethodBuilderOnCompletedUnsafe : pm.AsyncTaskMethodBuilderOnCompleted; + } else { + predefined = unsafeVersion ? pm.AsyncTaskMethodBuilderGenericOnCompletedUnsafe : pm.AsyncTaskMethodBuilderGenericOnCompleted; + has_task_return_type = true; + } + + var on_completed = predefined.Resolve (Location); + if (on_completed == null) + return; + + if (has_task_return_type) + on_completed = MemberCache.GetMember (set_result.DeclaringType, on_completed); + + on_completed = on_completed.MakeGenericMethod (this, awaiter.Type, ec.CurrentType); + + var mg = MethodGroupExpr.CreatePredefined (on_completed, on_completed.DeclaringType, Location); + mg.InstanceExpression = new FieldExpr (builder, Location) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location) + }; + + var args = new Arguments (2); + args.Add (new Argument (awaiter, Argument.AType.Ref)); + args.Add (new Argument (new CompilerGeneratedThis (CurrentType, Location), Argument.AType.Ref)); + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + mg.EmitCall (ec, args, true); + } + } + + public void EmitInitializer (EmitContext ec) + { + // + // Some predefined types are missing + // + if (builder == null) + return; + + var instance = (TemporaryVariableReference) Instance; + var builder_field = builder.Spec; + if (MemberName.Arity > 0) { + builder_field = MemberCache.GetMember (instance.Type, builder_field); + } + + // + // Inflated factory method when task is of generic type + // + if (builder_factory.DeclaringType.IsGeneric) { + var task_return_type = return_type.TypeArguments; + var bt = builder_factory.DeclaringType.MakeGenericType (Module, task_return_type); + builder_factory = MemberCache.GetMember (bt, builder_factory); + builder_start = MemberCache.GetMember (bt, builder_start); + } + + // + // stateMachine.$builder = AsyncTaskMethodBuilder<{task-type}>.Create(); + // + instance.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Call, builder_factory); + ec.Emit (OpCodes.Stfld, builder_field); + + // + // stateMachine.$builder.Start<{storey-type}>(ref stateMachine); + // + instance.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Ldflda, builder_field); + if (Task != null) + ec.Emit (OpCodes.Dup); + instance.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Call, builder_start.MakeGenericMethod (Module, instance.Type)); + + // + // Emits return stateMachine.$builder.Task; + // + if (Task != null) { + var task_get = Task.Get; + + if (MemberName.Arity > 0) { + task_get = MemberCache.GetMember (builder_field.MemberType, task_get); + } + + var pe_task = new PropertyExpr (Task, Location) { + InstanceExpression = EmptyExpression.Null, // Comes from the dup above + Getter = task_get + }; + + pe_task.Emit (ec); + } + } + + public void EmitSetException (EmitContext ec, LocalVariableReference exceptionVariable) + { + // + // $builder.SetException (Exception) + // + var mg = MethodGroupExpr.CreatePredefined (set_exception, set_exception.DeclaringType, Location); + mg.InstanceExpression = new FieldExpr (builder, Location) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location) + }; + + Arguments args = new Arguments (1); + args.Add (new Argument (exceptionVariable)); + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + mg.EmitCall (ec, args, true); + } + } + + public void EmitSetResult (EmitContext ec) + { + // + // $builder.SetResult (); + // $builder.SetResult (value); + // + var mg = MethodGroupExpr.CreatePredefined (set_result, set_result.DeclaringType, Location); + mg.InstanceExpression = new FieldExpr (builder, Location) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location) + }; + + Arguments args; + if (HoistedReturnValue == null) { + args = new Arguments (0); + } else { + args = new Arguments (1); + args.Add (new Argument (HoistedReturnValue)); + } + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + mg.EmitCall (ec, args, true); + } + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_type = Compiler.BuiltinTypes.ValueType; + base_class = null; + + var istate_machine = Module.PredefinedTypes.IAsyncStateMachine; + if (istate_machine.Define ()) { + return new[] { istate_machine.TypeSpec }; + } + + return null; + } + } + + public class StackFieldExpr : FieldExpr, IExpressionCleanup + { + public StackFieldExpr (Field field) + : base (field, Location.Null) + { + } + + public bool IsAvailableForReuse { + get { + var field = (Field) spec.MemberDefinition; + return field.IsAvailableForReuse; + } + set { + var field = (Field) spec.MemberDefinition; + field.IsAvailableForReuse = value; + } + } + + public override void AddressOf (EmitContext ec, AddressOp mode) + { + base.AddressOf (ec, mode); + + if (mode == AddressOp.Load) { + IsAvailableForReuse = true; + } + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + PrepareCleanup (ec); + } + + public void EmitLoad (EmitContext ec) + { + base.Emit (ec); + } + + public void PrepareCleanup (EmitContext ec) + { + IsAvailableForReuse = true; + + // + // Release any captured reference type stack variables + // to imitate real stack behavour and help GC stuff early + // + if (TypeSpec.IsReferenceType (type)) { + ec.AddStatementEpilog (this); + } + } + + void IExpressionCleanup.EmitCleanup (EmitContext ec) + { + EmitAssign (ec, new NullConstant (type, loc), false, false); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/attribute.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/attribute.cs new file mode 100644 index 000000000..9e0437492 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/attribute.cs @@ -0,0 +1,2140 @@ +// +// attribute.cs: Attributes handling +// +// Author: Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011-2013 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.IO; + +#if STATIC +using SecurityType = System.Collections.Generic.List; +using BadImageFormat = IKVM.Reflection.BadImageFormatException; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using SecurityType = System.Collections.Generic.Dictionary; +using BadImageFormat = System.BadImageFormatException; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// Base class for objects that can have Attributes applied to them. + /// + public abstract class Attributable { + // + // Holds all attributes attached to this element + // + protected Attributes attributes; + + public void AddAttributes (Attributes attrs, IMemberContext context) + { + if (attrs == null) + return; + + if (attributes == null) + attributes = attrs; + else + attributes.AddAttributes (attrs.Attrs); + attrs.AttachTo (this, context); + } + + public Attributes OptAttributes { + get { + return attributes; + } + set { + attributes = value; + } + } + + /// + /// Use member-specific procedure to apply attribute @a in @cb to the entity being built in @builder + /// + public abstract void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa); + + /// + /// Returns one AttributeTarget for this element. + /// + public abstract AttributeTargets AttributeTargets { get; } + + public abstract bool IsClsComplianceRequired (); + + /// + /// Gets list of valid attribute targets for explicit target declaration. + /// The first array item is default target. Don't break this rule. + /// + public abstract string[] ValidAttributeTargets { get; } + }; + + public class Attribute + { + public readonly string ExplicitTarget; + public AttributeTargets Target; + readonly ATypeNameExpression expression; + + Arguments pos_args, named_args; + + bool resolve_error; + bool arg_resolved; + readonly bool nameEscaped; + readonly Location loc; + public TypeSpec Type; + + // + // An attribute can be attached to multiple targets (e.g. multiple fields) + // + Attributable[] targets; + + // + // A member context for the attribute, it's much easier to hold it here + // than trying to pull it during resolve + // + IMemberContext context; + + public static readonly AttributeUsageAttribute DefaultUsageAttribute = new AttributeUsageAttribute (AttributeTargets.All); + public static readonly object[] EmptyObject = new object [0]; + + List> named_values; + + public Attribute (string target, ATypeNameExpression expr, Arguments[] args, Location loc, bool nameEscaped) + { + this.expression = expr; + if (args != null) { + pos_args = args[0]; + named_args = args[1]; + } + this.loc = loc; + ExplicitTarget = target; + this.nameEscaped = nameEscaped; + } + + public Location Location { + get { + return loc; + } + } + + public Arguments NamedArguments { + get { + return named_args; + } + } + + public Arguments PositionalArguments { + get { + return pos_args; + } + } + + public bool ResolveError { + get { + return resolve_error; + } + } + + public ATypeNameExpression TypeExpression { + get { + return expression; + } + } + + void AddModuleCharSet (ResolveContext rc) + { + const string dll_import_char_set = "CharSet"; + + // + // Only when not customized by user + // + if (HasField (dll_import_char_set)) + return; + + if (!rc.Module.PredefinedTypes.CharSet.Define ()) { + return; + } + + if (NamedArguments == null) + named_args = new Arguments (1); + + var value = Constant.CreateConstantFromValue (rc.Module.PredefinedTypes.CharSet.TypeSpec, rc.Module.DefaultCharSet, Location); + NamedArguments.Add (new NamedArgument (dll_import_char_set, loc, value)); + } + + public Attribute Clone () + { + Attribute a = new Attribute (ExplicitTarget, expression, null, loc, nameEscaped); + a.pos_args = pos_args; + a.named_args = NamedArguments; + return a; + } + + // + // When the same attribute is attached to multiple fiels + // we use @target field as a list of targets. The attribute + // has to be resolved only once but emitted for each target. + // + public void AttachTo (Attributable target, IMemberContext context) + { + if (this.targets == null) { + this.targets = new Attributable[] { target }; + this.context = context; + return; + } + + // When re-attaching global attributes + if (context is NamespaceContainer) { + this.targets[0] = target; + this.context = context; + return; + } + + // Resize target array + Attributable[] new_array = new Attributable [this.targets.Length + 1]; + targets.CopyTo (new_array, 0); + new_array [targets.Length] = target; + this.targets = new_array; + + // No need to update context, different targets cannot have + // different contexts, it's enough to remove same attributes + // from secondary members. + + target.OptAttributes = null; + } + + public ResolveContext CreateResolveContext () + { + return new ResolveContext (context, ResolveContext.Options.ConstantScope); + } + + static void Error_InvalidNamedArgument (ResolveContext rc, NamedArgument name) + { + rc.Report.Error (617, name.Location, "`{0}' is not a valid named attribute argument. Named attribute arguments " + + "must be fields which are not readonly, static, const or read-write properties which are " + + "public and not static", + name.Name); + } + + static void Error_InvalidNamedArgumentType (ResolveContext rc, NamedArgument name) + { + rc.Report.Error (655, name.Location, + "`{0}' is not a valid named attribute argument because it is not a valid attribute parameter type", + name.Name); + } + + public static void Error_AttributeArgumentIsDynamic (IMemberContext context, Location loc) + { + context.Module.Compiler.Report.Error (1982, loc, "An attribute argument cannot be dynamic expression"); + } + + public void Error_MissingGuidAttribute () + { + Report.Error (596, Location, "The Guid attribute must be specified with the ComImport attribute"); + } + + public void Error_MisusedExtensionAttribute () + { + Report.Error (1112, Location, "Do not use `{0}' directly. Use parameter modifier `this' instead", GetSignatureForError ()); + } + + public void Error_MisusedDynamicAttribute () + { + Report.Error (1970, loc, "Do not use `{0}' directly. Use `dynamic' keyword instead", GetSignatureForError ()); + } + + void Error_AttributeEmitError (string inner) + { + Report.Error (647, Location, "Error during emitting `{0}' attribute. The reason is `{1}'", + Type.GetSignatureForError (), inner); + } + + public void Error_InvalidArgumentValue (TypeSpec attributeType) + { + Report.Error (591, Location, "Invalid value for argument to `{0}' attribute", attributeType.GetSignatureForError ()); + } + + public void Error_InvalidSecurityParent () + { + Report.Error (7070, Location, + "Security attribute `{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations", + Type.GetSignatureForError ()); + } + + Attributable Owner { + get { + return targets [0]; + } + } + + /// + /// Tries to resolve the type of the attribute. Flags an error if it can't, and complain is true. + /// + void ResolveAttributeType (bool comparisonOnly) + { + var resolve_printer = new SessionReportPrinter (); + SessionReportPrinter secondary_printer = null; + ReportPrinter prev_recorder = Report.SetPrinter (resolve_printer); + + bool t1_is_attr = false; + bool t2_is_attr = false; + TypeSpec t1, t2; + ATypeNameExpression expanded = null; + + // TODO: Additional warnings such as CS0436 are swallowed because we don't + // print on success + + try { + t1 = expression.ResolveAsType (context); + resolve_printer.EndSession (); + + if (t1 != null && resolve_printer.ErrorsCount == 0) + t1_is_attr = t1.IsAttribute; + + if (nameEscaped) { + t2 = null; + } else { + expanded = (ATypeNameExpression) expression.Clone (null); + expanded.Name += "Attribute"; + + secondary_printer = new SessionReportPrinter (); + Report.SetPrinter (secondary_printer); + t2 = expanded.ResolveAsType (context); + secondary_printer.EndSession (); + if (t2 != null && secondary_printer.ErrorsCount == 0) + t2_is_attr = t2.IsAttribute; + + secondary_printer.EndSession (); + } + } finally { + context.Module.Compiler.Report.SetPrinter (prev_recorder); + } + + if (t1_is_attr && t2_is_attr && t1 != t2) { + if (!comparisonOnly) { + Report.Error (1614, Location, "`{0}' is ambiguous between `{1}' and `{2}'. Use either `@{0}' or `{0}Attribute'", + GetSignatureForError (), expression.GetSignatureForError (), expanded.GetSignatureForError ()); + resolve_error = true; + } + + return; + } + + if (t1_is_attr) { + Type = t1; + return; + } + + if (t2_is_attr) { + Type = t2; + return; + } + + if (comparisonOnly) + return; + + resolve_error = true; + + if (t1 != null) { + if (resolve_printer.IsEmpty) { + Report.SymbolRelatedToPreviousError (t1); + Report.Error (616, Location, "`{0}': is not an attribute class", t1.GetSignatureForError ()); + } else { + resolve_printer.Merge (prev_recorder); + } + + return; + } + + if (t2 != null) { + if (secondary_printer.IsEmpty) { + Report.SymbolRelatedToPreviousError (t2); + Report.Error (616, Location, "`{0}': is not an attribute class", t2.GetSignatureForError ()); + } else { + secondary_printer.Merge (prev_recorder); + } + + return; + } + + resolve_printer.Merge (prev_recorder); + } + + public TypeSpec ResolveTypeForComparison () + { + if (Type == null && !resolve_error) + ResolveAttributeType (true); + return Type; + } + + public string GetSignatureForError () + { + if (Type != null) + return Type.GetSignatureForError (); + + return expression.GetSignatureForError (); + } + + public bool HasSecurityAttribute { + get { + PredefinedAttribute pa = context.Module.PredefinedAttributes.Security; + return pa.IsDefined && TypeSpec.IsBaseClass (Type, pa.TypeSpec, false); + } + } + + public bool IsValidSecurityAttribute () + { + return HasSecurityAttribute && IsSecurityActionValid (); + } + + static bool IsValidMethodImplOption (int value) + { + // + // Allow to use AggressiveInlining on any runtime/corlib + // + MethodImplOptions all = (MethodImplOptions) 256; + foreach (MethodImplOptions v in System.Enum.GetValues (typeof (MethodImplOptions))) { + all |= v; + } + + return ((MethodImplOptions) value | all) == all; + } + + public static bool IsValidArgumentType (TypeSpec t) + { + if (t.IsArray) { + var ac = (ArrayContainer) t; + if (ac.Rank > 1) + return false; + + t = ac.Element; + } + + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.Double: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.Bool: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.UShort: + + case BuiltinTypeSpec.Type.String: + case BuiltinTypeSpec.Type.Object: + case BuiltinTypeSpec.Type.Dynamic: + case BuiltinTypeSpec.Type.Type: + return true; + } + + return t.IsEnum; + } + + // TODO: Don't use this ambiguous value + public string Name { + get { return expression.Name; } + } + + public ATypeNameExpression TypeNameExpression { + get { + return expression; + } + } + + public Report Report { + get { return context.Module.Compiler.Report; } + } + + public MethodSpec Resolve () + { + if (resolve_error) + return null; + + resolve_error = true; + arg_resolved = true; + + if (Type == null) { + ResolveAttributeType (false); + if (Type == null) + return null; + } + + if (Type.IsAbstract) { + Report.Error (653, Location, "Cannot apply attribute class `{0}' because it is abstract", GetSignatureForError ()); + return null; + } + + ObsoleteAttribute obsolete_attr = Type.GetAttributeObsolete (); + if (obsolete_attr != null) { + AttributeTester.Report_ObsoleteMessage (obsolete_attr, Type.GetSignatureForError (), Location, Report); + } + + ResolveContext rc = null; + + MethodSpec ctor; + // Try if the attribute is simple and has been resolved before + if (pos_args != null || !context.Module.AttributeConstructorCache.TryGetValue (Type, out ctor)) { + rc = CreateResolveContext (); + ctor = ResolveConstructor (rc); + if (ctor == null) { + return null; + } + + if (pos_args == null && ctor.Parameters.IsEmpty) + context.Module.AttributeConstructorCache.Add (Type, ctor); + } + + // + // Add [module: DefaultCharSet] to all DllImport import attributes + // + var module = context.Module; + if ((Type == module.PredefinedAttributes.DllImport || Type == module.PredefinedAttributes.UnmanagedFunctionPointer) && module.HasDefaultCharSet) { + if (rc == null) + rc = CreateResolveContext (); + + AddModuleCharSet (rc); + } + + if (NamedArguments != null) { + if (rc == null) + rc = CreateResolveContext (); + + if (!ResolveNamedArguments (rc)) + return null; + } + + resolve_error = false; + return ctor; + } + + MethodSpec ResolveConstructor (ResolveContext ec) + { + if (pos_args != null) { + bool dynamic; + pos_args.Resolve (ec, out dynamic); + if (dynamic) { + Error_AttributeArgumentIsDynamic (ec.MemberContext, loc); + return null; + } + } + + return Expression.ConstructorLookup (ec, Type, ref pos_args, loc); + } + + bool ResolveNamedArguments (ResolveContext ec) + { + int named_arg_count = NamedArguments.Count; + var seen_names = new List (named_arg_count); + + named_values = new List> (named_arg_count); + + foreach (NamedArgument a in NamedArguments) { + string name = a.Name; + if (seen_names.Contains (name)) { + ec.Report.Error (643, a.Location, "Duplicate named attribute `{0}' argument", name); + continue; + } + + seen_names.Add (name); + + a.Resolve (ec); + + Expression member = Expression.MemberLookup (ec, false, Type, name, 0, Expression.MemberLookupRestrictions.ExactArity, loc); + + if (member == null) { + member = Expression.MemberLookup (ec, true, Type, name, 0, Expression.MemberLookupRestrictions.ExactArity, loc); + + if (member != null) { + // TODO: ec.Report.SymbolRelatedToPreviousError (member); + Expression.ErrorIsInaccesible (ec, member.GetSignatureForError (), loc); + return false; + } + } + + if (member == null){ + Expression.Error_TypeDoesNotContainDefinition (ec, Location, Type, name); + return false; + } + + if (!(member is PropertyExpr || member is FieldExpr)) { + Error_InvalidNamedArgument (ec, a); + return false; + } + + ObsoleteAttribute obsolete_attr; + + if (member is PropertyExpr) { + var pi = ((PropertyExpr) member).PropertyInfo; + + if (!pi.HasSet || !pi.HasGet || pi.IsStatic || !pi.Get.IsPublic || !pi.Set.IsPublic) { + ec.Report.SymbolRelatedToPreviousError (pi); + Error_InvalidNamedArgument (ec, a); + return false; + } + + if (!IsValidArgumentType (member.Type)) { + ec.Report.SymbolRelatedToPreviousError (pi); + Error_InvalidNamedArgumentType (ec, a); + return false; + } + + obsolete_attr = pi.GetAttributeObsolete (); + pi.MemberDefinition.SetIsAssigned (); + } else { + var fi = ((FieldExpr) member).Spec; + + if (fi.IsReadOnly || fi.IsStatic || !fi.IsPublic) { + Error_InvalidNamedArgument (ec, a); + return false; + } + + if (!IsValidArgumentType (member.Type)) { + ec.Report.SymbolRelatedToPreviousError (fi); + Error_InvalidNamedArgumentType (ec, a); + return false; + } + + obsolete_attr = fi.GetAttributeObsolete (); + fi.MemberDefinition.SetIsAssigned (); + } + + if (obsolete_attr != null && !context.IsObsolete) + AttributeTester.Report_ObsoleteMessage (obsolete_attr, member.GetSignatureForError (), member.Location, Report); + + if (a.Type != member.Type) { + a.Expr = Convert.ImplicitConversionRequired (ec, a.Expr, member.Type, a.Expr.Location); + } + + if (a.Expr != null) + named_values.Add (new KeyValuePair ((MemberExpr) member, a)); + } + + return true; + } + + /// + /// Get a string containing a list of valid targets for the attribute 'attr' + /// + public string GetValidTargets () + { + StringBuilder sb = new StringBuilder (); + AttributeTargets targets = Type.GetAttributeUsage (context.Module.PredefinedAttributes.AttributeUsage).ValidOn; + + if ((targets & AttributeTargets.Assembly) != 0) + sb.Append ("assembly, "); + + if ((targets & AttributeTargets.Module) != 0) + sb.Append ("module, "); + + if ((targets & AttributeTargets.Class) != 0) + sb.Append ("class, "); + + if ((targets & AttributeTargets.Struct) != 0) + sb.Append ("struct, "); + + if ((targets & AttributeTargets.Enum) != 0) + sb.Append ("enum, "); + + if ((targets & AttributeTargets.Constructor) != 0) + sb.Append ("constructor, "); + + if ((targets & AttributeTargets.Method) != 0) + sb.Append ("method, "); + + if ((targets & AttributeTargets.Property) != 0) + sb.Append ("property, indexer, "); + + if ((targets & AttributeTargets.Field) != 0) + sb.Append ("field, "); + + if ((targets & AttributeTargets.Event) != 0) + sb.Append ("event, "); + + if ((targets & AttributeTargets.Interface) != 0) + sb.Append ("interface, "); + + if ((targets & AttributeTargets.Parameter) != 0) + sb.Append ("parameter, "); + + if ((targets & AttributeTargets.Delegate) != 0) + sb.Append ("delegate, "); + + if ((targets & AttributeTargets.ReturnValue) != 0) + sb.Append ("return, "); + + if ((targets & AttributeTargets.GenericParameter) != 0) + sb.Append ("type parameter, "); + + return sb.Remove (sb.Length - 2, 2).ToString (); + } + + public AttributeUsageAttribute GetAttributeUsageAttribute () + { + if (!arg_resolved) + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + + if (resolve_error) + return DefaultUsageAttribute; + + AttributeUsageAttribute usage_attribute = new AttributeUsageAttribute ((AttributeTargets) ((Constant) pos_args[0].Expr).GetValue ()); + + var field = GetNamedValue ("AllowMultiple") as BoolConstant; + if (field != null) + usage_attribute.AllowMultiple = field.Value; + + field = GetNamedValue ("Inherited") as BoolConstant; + if (field != null) + usage_attribute.Inherited = field.Value; + + return usage_attribute; + } + + /// + /// Returns custom name of indexer + /// + public string GetIndexerAttributeValue () + { + if (!arg_resolved) + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + + if (resolve_error || pos_args.Count != 1 || !(pos_args[0].Expr is Constant)) + return null; + + return ((Constant) pos_args[0].Expr).GetValue () as string; + } + + /// + /// Returns condition of ConditionalAttribute + /// + public string GetConditionalAttributeValue () + { + if (!arg_resolved) + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + + if (resolve_error) + return null; + + return ((Constant) pos_args[0].Expr).GetValue () as string; + } + + /// + /// Creates the instance of ObsoleteAttribute from this attribute instance + /// + public ObsoleteAttribute GetObsoleteAttribute () + { + if (!arg_resolved) { + // corlib only case when obsolete is used before is resolved + var c = Type.MemberDefinition as Class; + if (c != null && !c.HasMembersDefined) + c.Define (); + + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + } + + if (resolve_error) + return null; + + if (pos_args == null) + return new ObsoleteAttribute (); + + string msg = ((Constant) pos_args[0].Expr).GetValue () as string; + if (pos_args.Count == 1) + return new ObsoleteAttribute (msg); + + return new ObsoleteAttribute (msg, ((BoolConstant) pos_args[1].Expr).Value); + } + + /// + /// Returns value of CLSCompliantAttribute contructor parameter but because the method can be called + /// before ApplyAttribute. We need to resolve the arguments. + /// This situation occurs when class deps is differs from Emit order. + /// + public bool GetClsCompliantAttributeValue () + { + if (!arg_resolved) + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + + if (resolve_error) + return false; + + return ((BoolConstant) pos_args[0].Expr).Value; + } + + public TypeSpec GetCoClassAttributeValue () + { + if (!arg_resolved) + Resolve (); + + if (resolve_error) + return null; + + return GetArgumentType (); + } + + public bool CheckTarget () + { + string[] valid_targets = Owner.ValidAttributeTargets; + if (ExplicitTarget == null || ExplicitTarget == valid_targets [0]) { + Target = Owner.AttributeTargets; + return true; + } + + // TODO: we can skip the first item + if (Array.Exists (valid_targets, i => i == ExplicitTarget)) { + switch (ExplicitTarget) { + case "return": Target = AttributeTargets.ReturnValue; return true; + case "param": Target = AttributeTargets.Parameter; return true; + case "field": Target = AttributeTargets.Field; return true; + case "method": Target = AttributeTargets.Method; return true; + case "property": Target = AttributeTargets.Property; return true; + case "module": Target = AttributeTargets.Module; return true; + } + throw new InternalErrorException ("Unknown explicit target: " + ExplicitTarget); + } + + StringBuilder sb = new StringBuilder (); + foreach (string s in valid_targets) { + sb.Append (s); + sb.Append (", "); + } + sb.Remove (sb.Length - 2, 2); + Report.Warning (657, 1, Location, + "`{0}' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are `{1}'. All attributes in this section will be ignored", + ExplicitTarget, sb.ToString ()); + return false; + } + + /// + /// Tests permitted SecurityAction for assembly or other types + /// + bool IsSecurityActionValid () + { + SecurityAction action = GetSecurityActionValue (); + bool for_assembly = Target == AttributeTargets.Assembly || Target == AttributeTargets.Module; + var c = (Constant)pos_args [0].Expr; + + switch (action) { +#pragma warning disable 618 + case SecurityAction.Demand: + case SecurityAction.Assert: + case SecurityAction.Deny: + case SecurityAction.PermitOnly: + case SecurityAction.LinkDemand: + case SecurityAction.InheritanceDemand: + if (!for_assembly) + return true; + break; + + case SecurityAction.RequestMinimum: + case SecurityAction.RequestOptional: + case SecurityAction.RequestRefuse: + if (for_assembly) + return true; + break; +#pragma warning restore 618 + + default: + Report.Error (7049, c.Location, "Security attribute `{0}' has an invalid SecurityAction value `{1}'", + Type.GetSignatureForError (), c.GetValueAsLiteral()); + return false; + } + + switch (Target) { + case AttributeTargets.Assembly: + Report.Error (7050, c.Location, "SecurityAction value `{0}' is invalid for security attributes applied to an assembly", + c.GetSignatureForError ()); + break; + default: + Report.Error (7051, c.Location, "SecurityAction value `{0}' is invalid for security attributes applied to a type or a method", + c.GetSignatureForError ()); + break; + } + + return false; + } + + System.Security.Permissions.SecurityAction GetSecurityActionValue () + { + return (SecurityAction) ((Constant) pos_args[0].Expr).GetValue (); + } + + /// + /// Creates instance of SecurityAttribute class and add result of CreatePermission method to permission table. + /// + /// + public void ExtractSecurityPermissionSet (MethodSpec ctor, ref SecurityType permissions) + { +#if STATIC + object[] values = new object[pos_args.Count]; + for (int i = 0; i < values.Length; ++i) + values[i] = ((Constant) pos_args[i].Expr).GetValue (); + + PropertyInfo[] prop; + object[] prop_values; + if (named_values == null) { + prop = null; + prop_values = null; + } else { + prop = new PropertyInfo[named_values.Count]; + prop_values = new object [named_values.Count]; + for (int i = 0; i < prop.Length; ++i) { + prop [i] = ((PropertyExpr) named_values [i].Key).PropertyInfo.MetaInfo; + prop_values [i] = ((Constant) named_values [i].Value.Expr).GetValue (); + } + } + + if (permissions == null) + permissions = new SecurityType (); + + var cab = new CustomAttributeBuilder ((ConstructorInfo) ctor.GetMetaInfo (), values, prop, prop_values); + permissions.Add (cab); +#else + throw new NotSupportedException (); +#endif + } + + public Constant GetNamedValue (string name) + { + if (named_values == null) + return null; + + for (int i = 0; i < named_values.Count; ++i) { + if (named_values [i].Value.Name == name) + return named_values [i].Value.Expr as Constant; + } + + return null; + } + + public CharSet GetCharSetValue () + { + return (CharSet) System.Enum.Parse (typeof (CharSet), ((Constant) pos_args[0].Expr).GetValue ().ToString ()); + } + + public bool HasField (string fieldName) + { + if (named_values == null) + return false; + + foreach (var na in named_values) { + if (na.Value.Name == fieldName) + return true; + } + + return false; + } + + // + // Returns true for MethodImplAttribute with MethodImplOptions.InternalCall value + // + public bool IsInternalCall () + { + return (GetMethodImplOptions () & MethodImplOptions.InternalCall) != 0; + } + + public MethodImplOptions GetMethodImplOptions () + { + MethodImplOptions options = 0; + if (pos_args.Count == 1) { + options = (MethodImplOptions) System.Enum.Parse (typeof (MethodImplOptions), ((Constant) pos_args[0].Expr).GetValue ().ToString ()); + } else if (HasField ("Value")) { + var named = GetNamedValue ("Value"); + options = (MethodImplOptions) System.Enum.Parse (typeof (MethodImplOptions), named.GetValue ().ToString ()); + } + + return options; + } + + // + // Returns true for StructLayoutAttribute with LayoutKind.Explicit value + // + public bool IsExplicitLayoutKind () + { + if (pos_args == null || pos_args.Count != 1) + return false; + + var value = (LayoutKind) System.Enum.Parse (typeof (LayoutKind), ((Constant) pos_args[0].Expr).GetValue ().ToString ()); + return value == LayoutKind.Explicit; + } + + public Expression GetParameterDefaultValue () + { + if (pos_args == null) + return null; + + return pos_args[0].Expr; + } + + public override bool Equals (object obj) + { + Attribute a = obj as Attribute; + if (a == null) + return false; + + return Type == a.Type && Target == a.Target; + } + + public override int GetHashCode () + { + return Type.GetHashCode () ^ Target.GetHashCode (); + } + + /// + /// Emit attribute for Attributable symbol + /// + public void Emit (Dictionary> allEmitted) + { + var ctor = Resolve (); + if (ctor == null) + return; + + var predefined = context.Module.PredefinedAttributes; + + AttributeUsageAttribute usage_attr = Type.GetAttributeUsage (predefined.AttributeUsage); + if ((usage_attr.ValidOn & Target) == 0) { + Report.Error (592, Location, "The attribute `{0}' is not valid on this declaration type. " + + "It is valid on `{1}' declarations only", + GetSignatureForError (), GetValidTargets ()); + return; + } + + byte[] cdata; + if (pos_args == null && named_values == null) { + cdata = AttributeEncoder.Empty; + } else { + AttributeEncoder encoder = new AttributeEncoder (); + + if (pos_args != null) { + var param_types = ctor.Parameters.Types; + for (int j = 0; j < pos_args.Count; ++j) { + var pt = param_types[j]; + var arg_expr = pos_args[j].Expr; + if (j == 0) { + if ((Type == predefined.IndexerName || Type == predefined.Conditional) && arg_expr is Constant) { + string v = ((Constant) arg_expr).GetValue () as string; + if (!Tokenizer.IsValidIdentifier (v) || (Type == predefined.IndexerName && Tokenizer.IsKeyword (v))) { + context.Module.Compiler.Report.Error (633, arg_expr.Location, + "The argument to the `{0}' attribute must be a valid identifier", GetSignatureForError ()); + return; + } + } else if (Type == predefined.Guid) { + string v = ((StringConstant) arg_expr).Value; + try { + new Guid (v); + } catch { + Error_InvalidArgumentValue (Type); + return; + } + } else if (Type == predefined.AttributeUsage) { + int v = ((IntConstant) ((EnumConstant) arg_expr).Child).Value; + if (v == 0) + Error_InvalidArgumentValue (Type); + } else if (Type == predefined.MarshalAs) { + if (pos_args.Count == 1) { + var u_type = (UnmanagedType) System.Enum.Parse (typeof (UnmanagedType), ((Constant) pos_args[0].Expr).GetValue ().ToString ()); + if (u_type == UnmanagedType.ByValArray && !(Owner is FieldBase)) { + Report.Error (7055, pos_args [0].Expr.Location, "Unmanaged type `ByValArray' is only valid for fields"); + } + } + } else if (Type == predefined.DllImport) { + if (pos_args.Count == 1 && pos_args[0].Expr is Constant) { + var value = ((Constant) pos_args[0].Expr).GetValue () as string; + if (string.IsNullOrEmpty (value)) + Error_InvalidArgumentValue (Type); + } + } else if (Type == predefined.MethodImpl) { + if (pos_args.Count == 1) { + var value = (int) ((Constant) arg_expr).GetValueAsLong (); + + if (!IsValidMethodImplOption (value)) { + Error_InvalidArgumentValue (Type); + } + } + } + } + + arg_expr.EncodeAttributeValue (context, encoder, pt, pt); + } + } + + if (named_values != null) { + encoder.Encode ((ushort) named_values.Count); + foreach (var na in named_values) { + if (na.Key is FieldExpr) + encoder.Encode ((byte) 0x53); + else + encoder.Encode ((byte) 0x54); + + encoder.Encode (na.Key.Type); + encoder.Encode (na.Value.Name); + na.Value.Expr.EncodeAttributeValue (context, encoder, na.Key.Type, na.Key.Type); + } + } else { + encoder.EncodeEmptyNamedArguments (); + } + + cdata = encoder.ToArray (); + } + + if (!ctor.DeclaringType.IsConditionallyExcluded (context)) { + try { + foreach (Attributable target in targets) + target.ApplyAttributeBuilder (this, ctor, cdata, predefined); + } catch (Exception e) { + if (e is BadImageFormat && Report.Errors > 0) + return; + + Error_AttributeEmitError (e.Message); + return; + } + } + + if (!usage_attr.AllowMultiple && allEmitted != null) { + if (allEmitted.ContainsKey (this)) { + var a = allEmitted [this]; + if (a == null) { + a = new List (2); + allEmitted [this] = a; + } + a.Add (this); + } else { + allEmitted.Add (this, null); + } + } + + if (!context.Module.Compiler.Settings.VerifyClsCompliance) + return; + + // Here we are testing attribute arguments for array usage (error 3016) + if (Owner.IsClsComplianceRequired ()) { + if (pos_args != null) + pos_args.CheckArrayAsAttribute (context.Module.Compiler); + + if (NamedArguments == null) + return; + + NamedArguments.CheckArrayAsAttribute (context.Module.Compiler); + } + } + + private Expression GetValue () + { + if (pos_args == null || pos_args.Count < 1) + return null; + + return pos_args[0].Expr; + } + + public string GetString () + { + Expression e = GetValue (); + if (e is StringConstant) + return ((StringConstant)e).Value; + return null; + } + + public bool GetBoolean () + { + Expression e = GetValue (); + if (e is BoolConstant) + return ((BoolConstant)e).Value; + return false; + } + + public TypeSpec GetArgumentType () + { + TypeOf e = GetValue () as TypeOf; + if (e == null) + return null; + return e.TypeArgument; + } + } + + public class Attributes + { + public readonly List Attrs; +#if FULL_AST + public readonly List> Sections = new List> (); +#endif + + public Attributes (Attribute a) + { + Attrs = new List (); + Attrs.Add (a); + +#if FULL_AST + Sections.Add (Attrs); +#endif + } + + public Attributes (List attrs) + { + Attrs = attrs ?? new List (); +#if FULL_AST + Sections.Add (attrs); +#endif + } + + public void AddAttribute (Attribute attr) + { + Attrs.Add (attr); + } + + public void AddAttributes (List attrs) + { +#if FULL_AST + Sections.Add (attrs); +#else + Attrs.AddRange (attrs); +#endif + } + + public void AttachTo (Attributable attributable, IMemberContext context) + { + foreach (Attribute a in Attrs) + a.AttachTo (attributable, context); + } + + public Attributes Clone () + { + var al = new List (Attrs.Count); + foreach (Attribute a in Attrs) + al.Add (a.Clone ()); + + return new Attributes (al); + } + + /// + /// Checks whether attribute target is valid for the current element + /// + public bool CheckTargets () + { + for (int i = 0; i < Attrs.Count; ++i) { + if (!Attrs [i].CheckTarget ()) + Attrs.RemoveAt (i--); + } + + return true; + } + + public void ConvertGlobalAttributes (TypeContainer member, NamespaceContainer currentNamespace, bool isGlobal) + { + var member_explicit_targets = member.ValidAttributeTargets; + for (int i = 0; i < Attrs.Count; ++i) { + var attr = Attrs[0]; + if (attr.ExplicitTarget == null) + continue; + + int ii; + for (ii = 0; ii < member_explicit_targets.Length; ++ii) { + if (attr.ExplicitTarget == member_explicit_targets[ii]) { + ii = -1; + break; + } + } + + if (ii < 0 || !isGlobal) + continue; + + member.Module.AddAttribute (attr, currentNamespace); + Attrs.RemoveAt (i); + --i; + } + } + + public bool HasResolveError() + { + foreach (var a in Attrs) { + if (a.ResolveError) + return true; + } + + return false; + } + + public Attribute Search (PredefinedAttribute t) + { + return Search (null, t); + } + + public Attribute Search (string explicitTarget, PredefinedAttribute t) + { + foreach (Attribute a in Attrs) { + if (explicitTarget != null && a.ExplicitTarget != explicitTarget) + continue; + + if (a.ResolveTypeForComparison () == t) + return a; + } + return null; + } + + /// + /// Returns all attributes of type 't'. Use it when attribute is AllowMultiple = true + /// + public Attribute[] SearchMulti (PredefinedAttribute t) + { + List ar = null; + + foreach (Attribute a in Attrs) { + if (a.ResolveTypeForComparison () == t) { + if (ar == null) + ar = new List (Attrs.Count); + ar.Add (a); + } + } + + return ar == null ? null : ar.ToArray (); + } + + public void Emit () + { + CheckTargets (); + + Dictionary> ld = Attrs.Count > 1 ? new Dictionary> () : null; + + foreach (Attribute a in Attrs) + a.Emit (ld); + + if (ld == null || ld.Count == 0) + return; + + foreach (var d in ld) { + if (d.Value == null) + continue; + + Attribute a = d.Key; + + foreach (Attribute collision in d.Value) + a.Report.SymbolRelatedToPreviousError (collision.Location, ""); + + a.Report.Error (579, a.Location, "The attribute `{0}' cannot be applied multiple times", + a.GetSignatureForError ()); + } + } + + public bool Contains (PredefinedAttribute t) + { + return Search (t) != null; + } + } + + public sealed class AttributeEncoder + { + [Flags] + public enum EncodedTypeProperties + { + None = 0, + DynamicType = 1, + TypeParameter = 1 << 1 + } + + public static readonly byte[] Empty; + + byte[] buffer; + int pos; + const ushort Version = 1; + + static AttributeEncoder () + { + Empty = new byte[4]; + Empty[0] = (byte) Version; + } + + public AttributeEncoder () + { + buffer = new byte[32]; + Encode (Version); + } + + public void Encode (bool value) + { + Encode (value ? (byte) 1 : (byte) 0); + } + + public void Encode (byte value) + { + if (pos == buffer.Length) + Grow (1); + + buffer [pos++] = value; + } + + public void Encode (sbyte value) + { + Encode ((byte) value); + } + + public void Encode (short value) + { + if (pos + 2 > buffer.Length) + Grow (2); + + buffer[pos++] = (byte) value; + buffer[pos++] = (byte) (value >> 8); + } + + public void Encode (ushort value) + { + Encode ((short) value); + } + + public void Encode (int value) + { + if (pos + 4 > buffer.Length) + Grow (4); + + buffer[pos++] = (byte) value; + buffer[pos++] = (byte) (value >> 8); + buffer[pos++] = (byte) (value >> 16); + buffer[pos++] = (byte) (value >> 24); + } + + public void Encode (uint value) + { + Encode ((int) value); + } + + public void Encode (long value) + { + if (pos + 8 > buffer.Length) + Grow (8); + + buffer[pos++] = (byte) value; + buffer[pos++] = (byte) (value >> 8); + buffer[pos++] = (byte) (value >> 16); + buffer[pos++] = (byte) (value >> 24); + buffer[pos++] = (byte) (value >> 32); + buffer[pos++] = (byte) (value >> 40); + buffer[pos++] = (byte) (value >> 48); + buffer[pos++] = (byte) (value >> 56); + } + + public void Encode (ulong value) + { + Encode ((long) value); + } + + public void Encode (float value) + { + Encode (SingleConverter.SingleToInt32Bits (value)); + } + + public void Encode (double value) + { + Encode (BitConverter.DoubleToInt64Bits (value)); + } + + public void Encode (string value) + { + if (value == null) { + Encode ((byte) 0xFF); + return; + } + + var buf = Encoding.UTF8.GetBytes(value); + WriteCompressedValue (buf.Length); + + if (pos + buf.Length > buffer.Length) + Grow (buf.Length); + + Buffer.BlockCopy (buf, 0, buffer, pos, buf.Length); + pos += buf.Length; + } + + public EncodedTypeProperties Encode (TypeSpec type) + { + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Bool: + Encode ((byte) 0x02); + break; + case BuiltinTypeSpec.Type.Char: + Encode ((byte) 0x03); + break; + case BuiltinTypeSpec.Type.SByte: + Encode ((byte) 0x04); + break; + case BuiltinTypeSpec.Type.Byte: + Encode ((byte) 0x05); + break; + case BuiltinTypeSpec.Type.Short: + Encode ((byte) 0x06); + break; + case BuiltinTypeSpec.Type.UShort: + Encode ((byte) 0x07); + break; + case BuiltinTypeSpec.Type.Int: + Encode ((byte) 0x08); + break; + case BuiltinTypeSpec.Type.UInt: + Encode ((byte) 0x09); + break; + case BuiltinTypeSpec.Type.Long: + Encode ((byte) 0x0A); + break; + case BuiltinTypeSpec.Type.ULong: + Encode ((byte) 0x0B); + break; + case BuiltinTypeSpec.Type.Float: + Encode ((byte) 0x0C); + break; + case BuiltinTypeSpec.Type.Double: + Encode ((byte) 0x0D); + break; + case BuiltinTypeSpec.Type.String: + Encode ((byte) 0x0E); + break; + case BuiltinTypeSpec.Type.Type: + Encode ((byte) 0x50); + break; + case BuiltinTypeSpec.Type.Object: + Encode ((byte) 0x51); + break; + case BuiltinTypeSpec.Type.Dynamic: + Encode ((byte) 0x51); + return EncodedTypeProperties.DynamicType; + default: + if (type.IsArray) { + Encode ((byte) 0x1D); + return Encode (TypeManager.GetElementType (type)); + } + + if (type.Kind == MemberKind.Enum) { + Encode ((byte) 0x55); + EncodeTypeName (type); + } + + break; + } + + return EncodedTypeProperties.None; + } + + public void EncodeTypeName (TypeSpec type) + { + var old_type = type.GetMetaInfo (); + Encode (type.MemberDefinition.IsImported ? old_type.AssemblyQualifiedName : old_type.FullName); + } + + public void EncodeTypeName (TypeContainer type) + { + Encode (type.GetSignatureForMetadata ()); + } + + + // + // Encodes single property named argument per call + // + public void EncodeNamedPropertyArgument (PropertySpec property, Constant value) + { + Encode ((ushort) 1); // length + Encode ((byte) 0x54); // property + Encode (property.MemberType); + Encode (property.Name); + value.EncodeAttributeValue (null, this, property.MemberType, property.MemberType); + } + + // + // Encodes single field named argument per call + // + public void EncodeNamedFieldArgument (FieldSpec field, Constant value) + { + Encode ((ushort) 1); // length + Encode ((byte) 0x53); // field + Encode (field.MemberType); + Encode (field.Name); + value.EncodeAttributeValue (null, this, field.MemberType, field.MemberType); + } + + public void EncodeNamedArguments (T[] members, Constant[] values) where T : MemberSpec, IInterfaceMemberSpec + { + Encode ((ushort) members.Length); + + for (int i = 0; i < members.Length; ++i) + { + var member = members[i]; + + if (member.Kind == MemberKind.Field) + Encode ((byte) 0x53); + else if (member.Kind == MemberKind.Property) + Encode ((byte) 0x54); + else + throw new NotImplementedException (member.Kind.ToString ()); + + Encode (member.MemberType); + Encode (member.Name); + values [i].EncodeAttributeValue (null, this, member.MemberType, member.MemberType); + } + } + + public void EncodeEmptyNamedArguments () + { + Encode ((ushort) 0); + } + + void Grow (int inc) + { + int size = System.Math.Max (pos * 4, pos + inc + 2); + Array.Resize (ref buffer, size); + } + + void WriteCompressedValue (int value) + { + if (value < 0x80) { + Encode ((byte) value); + return; + } + + if (value < 0x4000) { + Encode ((byte) (0x80 | (value >> 8))); + Encode ((byte) value); + return; + } + + Encode (value); + } + + public byte[] ToArray () + { + byte[] buf = new byte[pos]; + Array.Copy (buffer, buf, pos); + return buf; + } + } + + + /// + /// Helper class for attribute verification routine. + /// + static class AttributeTester + { + /// + /// Common method for Obsolete error/warning reporting. + /// + public static void Report_ObsoleteMessage (ObsoleteAttribute oa, string member, Location loc, Report Report) + { + if (oa.IsError) { + Report.Error (619, loc, "`{0}' is obsolete: `{1}'", member, oa.Message); + return; + } + + if (oa.Message == null || oa.Message.Length == 0) { + Report.Warning (612, 1, loc, "`{0}' is obsolete", member); + return; + } + Report.Warning (618, 2, loc, "`{0}' is obsolete: `{1}'", member, oa.Message); + } + } + + // + // Predefined attribute types + // + public class PredefinedAttributes + { + // Build-in attributes + public readonly PredefinedAttribute ParamArray; + public readonly PredefinedAttribute Out; + + // Optional attributes + public readonly PredefinedAttribute Obsolete; + public readonly PredefinedAttribute DllImport; + public readonly PredefinedAttribute MethodImpl; + public readonly PredefinedAttribute MarshalAs; + public readonly PredefinedAttribute In; + public readonly PredefinedAttribute IndexerName; + public readonly PredefinedAttribute Conditional; + public readonly PredefinedAttribute CLSCompliant; + public readonly PredefinedAttribute Security; + public readonly PredefinedAttribute Required; + public readonly PredefinedAttribute Guid; + public readonly PredefinedAttribute AssemblyCulture; + public readonly PredefinedAttribute AssemblyVersion; + public readonly PredefinedAttribute AssemblyAlgorithmId; + public readonly PredefinedAttribute AssemblyFlags; + public readonly PredefinedAttribute AssemblyFileVersion; + public readonly PredefinedAttribute ComImport; + public readonly PredefinedAttribute CoClass; + public readonly PredefinedAttribute AttributeUsage; + public readonly PredefinedAttribute DefaultParameterValue; + public readonly PredefinedAttribute OptionalParameter; + public readonly PredefinedAttribute UnverifiableCode; + public readonly PredefinedAttribute DefaultCharset; + public readonly PredefinedAttribute TypeForwarder; + public readonly PredefinedAttribute FixedBuffer; + public readonly PredefinedAttribute CompilerGenerated; + public readonly PredefinedAttribute InternalsVisibleTo; + public readonly PredefinedAttribute RuntimeCompatibility; + public readonly PredefinedAttribute DebuggerHidden; + public readonly PredefinedAttribute UnsafeValueType; + public readonly PredefinedAttribute UnmanagedFunctionPointer; + public readonly PredefinedDebuggerBrowsableAttribute DebuggerBrowsable; + public readonly PredefinedAttribute DebuggerStepThrough; + public readonly PredefinedDebuggableAttribute Debuggable; + + // New in .NET 3.5 + public readonly PredefinedAttribute Extension; + + // New in .NET 4.0 + public readonly PredefinedDynamicAttribute Dynamic; + + // New in .NET 4.5 + public readonly PredefinedStateMachineAttribute AsyncStateMachine; + + // + // Optional types which are used as types and for member lookup + // + public readonly PredefinedAttribute DefaultMember; + public readonly PredefinedDecimalAttribute DecimalConstant; + public readonly PredefinedAttribute StructLayout; + public readonly PredefinedAttribute FieldOffset; + public readonly PredefinedAttribute AssemblyProduct; + public readonly PredefinedAttribute AssemblyCompany; + public readonly PredefinedAttribute AssemblyDescription; + public readonly PredefinedAttribute AssemblyCopyright; + public readonly PredefinedAttribute AssemblyTrademark; + public readonly PredefinedAttribute CallerMemberNameAttribute; + public readonly PredefinedAttribute CallerLineNumberAttribute; + public readonly PredefinedAttribute CallerFilePathAttribute; + + public PredefinedAttributes (ModuleContainer module) + { + ParamArray = new PredefinedAttribute (module, "System", "ParamArrayAttribute"); + Out = new PredefinedAttribute (module, "System.Runtime.InteropServices", "OutAttribute"); + ParamArray.Resolve (); + Out.Resolve (); + + Obsolete = new PredefinedAttribute (module, "System", "ObsoleteAttribute"); + DllImport = new PredefinedAttribute (module, "System.Runtime.InteropServices", "DllImportAttribute"); + MethodImpl = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "MethodImplAttribute"); + MarshalAs = new PredefinedAttribute (module, "System.Runtime.InteropServices", "MarshalAsAttribute"); + In = new PredefinedAttribute (module, "System.Runtime.InteropServices", "InAttribute"); + IndexerName = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "IndexerNameAttribute"); + Conditional = new PredefinedAttribute (module, "System.Diagnostics", "ConditionalAttribute"); + CLSCompliant = new PredefinedAttribute (module, "System", "CLSCompliantAttribute"); + Security = new PredefinedAttribute (module, "System.Security.Permissions", "SecurityAttribute"); + Required = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "RequiredAttributeAttribute"); + Guid = new PredefinedAttribute (module, "System.Runtime.InteropServices", "GuidAttribute"); + AssemblyCulture = new PredefinedAttribute (module, "System.Reflection", "AssemblyCultureAttribute"); + AssemblyVersion = new PredefinedAttribute (module, "System.Reflection", "AssemblyVersionAttribute"); + AssemblyAlgorithmId = new PredefinedAttribute (module, "System.Reflection", "AssemblyAlgorithmIdAttribute"); + AssemblyFlags = new PredefinedAttribute (module, "System.Reflection", "AssemblyFlagsAttribute"); + AssemblyFileVersion = new PredefinedAttribute (module, "System.Reflection", "AssemblyFileVersionAttribute"); + ComImport = new PredefinedAttribute (module, "System.Runtime.InteropServices", "ComImportAttribute"); + CoClass = new PredefinedAttribute (module, "System.Runtime.InteropServices", "CoClassAttribute"); + AttributeUsage = new PredefinedAttribute (module, "System", "AttributeUsageAttribute"); + DefaultParameterValue = new PredefinedAttribute (module, "System.Runtime.InteropServices", "DefaultParameterValueAttribute"); + OptionalParameter = new PredefinedAttribute (module, "System.Runtime.InteropServices", "OptionalAttribute"); + UnverifiableCode = new PredefinedAttribute (module, "System.Security", "UnverifiableCodeAttribute"); + + DefaultCharset = new PredefinedAttribute (module, "System.Runtime.InteropServices", "DefaultCharSetAttribute"); + TypeForwarder = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "TypeForwardedToAttribute"); + FixedBuffer = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "FixedBufferAttribute"); + CompilerGenerated = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"); + InternalsVisibleTo = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "InternalsVisibleToAttribute"); + RuntimeCompatibility = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "RuntimeCompatibilityAttribute"); + DebuggerHidden = new PredefinedAttribute (module, "System.Diagnostics", "DebuggerHiddenAttribute"); + UnsafeValueType = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "UnsafeValueTypeAttribute"); + UnmanagedFunctionPointer = new PredefinedAttribute (module, "System.Runtime.InteropServices", "UnmanagedFunctionPointerAttribute"); + DebuggerBrowsable = new PredefinedDebuggerBrowsableAttribute (module, "System.Diagnostics", "DebuggerBrowsableAttribute"); + DebuggerStepThrough = new PredefinedAttribute (module, "System.Diagnostics", "DebuggerStepThroughAttribute"); + Debuggable = new PredefinedDebuggableAttribute (module, "System.Diagnostics", "DebuggableAttribute"); + + Extension = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "ExtensionAttribute"); + + Dynamic = new PredefinedDynamicAttribute (module, "System.Runtime.CompilerServices", "DynamicAttribute"); + + DefaultMember = new PredefinedAttribute (module, "System.Reflection", "DefaultMemberAttribute"); + DecimalConstant = new PredefinedDecimalAttribute (module, "System.Runtime.CompilerServices", "DecimalConstantAttribute"); + StructLayout = new PredefinedAttribute (module, "System.Runtime.InteropServices", "StructLayoutAttribute"); + FieldOffset = new PredefinedAttribute (module, "System.Runtime.InteropServices", "FieldOffsetAttribute"); + AssemblyProduct = new PredefinedAttribute (module, "System.Reflection", "AssemblyProductAttribute"); + AssemblyCompany = new PredefinedAttribute (module, "System.Reflection", "AssemblyCompanyAttribute"); + AssemblyDescription = new PredefinedAttribute (module, "System.Reflection", "AssemblyDescriptionAttribute"); + AssemblyCopyright = new PredefinedAttribute (module, "System.Reflection", "AssemblyCopyrightAttribute"); + AssemblyTrademark = new PredefinedAttribute (module, "System.Reflection", "AssemblyTrademarkAttribute"); + + AsyncStateMachine = new PredefinedStateMachineAttribute (module, "System.Runtime.CompilerServices", "AsyncStateMachineAttribute"); + + CallerMemberNameAttribute = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "CallerMemberNameAttribute"); + CallerLineNumberAttribute = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "CallerLineNumberAttribute"); + CallerFilePathAttribute = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "CallerFilePathAttribute"); + + // TODO: Should define only attributes which are used for comparison + const System.Reflection.BindingFlags all_fields = System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly; + + foreach (var fi in GetType ().GetFields (all_fields)) { + ((PredefinedAttribute) fi.GetValue (this)).Define (); + } + } + } + + public class PredefinedAttribute : PredefinedType + { + protected MethodSpec ctor; + + public PredefinedAttribute (ModuleContainer module, string ns, string name) + : base (module, MemberKind.Class, ns, name) + { + } + + #region Properties + + public MethodSpec Constructor { + get { + return ctor; + } + } + + #endregion + + public static bool operator == (TypeSpec type, PredefinedAttribute pa) + { + return type == pa.type && pa.type != null; + } + + public static bool operator != (TypeSpec type, PredefinedAttribute pa) + { + return type != pa.type; + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + + public override bool Equals (object obj) + { + throw new NotSupportedException (); + } + + public void EmitAttribute (ConstructorBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (MethodBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (PropertyBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (FieldBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (TypeBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (AssemblyBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (ModuleBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (ParameterBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + ConstructorInfo GetCtorMetaInfo () + { + return (ConstructorInfo) ctor.GetMetaInfo (); + } + + public bool ResolveBuilder () + { + if (ctor != null) + return true; + + // + // Handle all parameter-less attributes as optional + // + if (!Define ()) + return false; + + ctor = (MethodSpec) MemberCache.FindMember (type, MemberFilter.Constructor (ParametersCompiled.EmptyReadOnlyParameters), BindingRestriction.DeclaredOnly); + return ctor != null; + } + } + + public class PredefinedDebuggerBrowsableAttribute : PredefinedAttribute + { + public PredefinedDebuggerBrowsableAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (FieldBuilder builder, System.Diagnostics.DebuggerBrowsableState state) + { + var ctor = module.PredefinedMembers.DebuggerBrowsableAttributeCtor.Get (); + if (ctor == null) + return; + + AttributeEncoder encoder = new AttributeEncoder (); + encoder.Encode ((int) state); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + public class PredefinedDebuggableAttribute : PredefinedAttribute + { + public PredefinedDebuggableAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (AssemblyBuilder builder, System.Diagnostics.DebuggableAttribute.DebuggingModes modes) + { + var atype = module.PredefinedAttributes.Debuggable; + if (!atype.Define ()) + return; + + MethodSpec ctor = null; + foreach (MethodSpec m in MemberCache.FindMembers (atype.TypeSpec, CSharp.Constructor.ConstructorName, true)) { + if (m.Parameters.Count != 1) + continue; + + if (m.Parameters.Types[0].Kind == MemberKind.Enum) { + ctor = m; + } + } + + if (ctor == null) + return; + + AttributeEncoder encoder = new AttributeEncoder (); + encoder.Encode ((int) modes); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + public class PredefinedDecimalAttribute : PredefinedAttribute + { + public PredefinedDecimalAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (ParameterBuilder builder, decimal value, Location loc) + { + var ctor = module.PredefinedMembers.DecimalConstantAttributeCtor.Resolve (loc); + if (ctor == null) + return; + + int[] bits = decimal.GetBits (value); + AttributeEncoder encoder = new AttributeEncoder (); + encoder.Encode ((byte) (bits[3] >> 16)); + encoder.Encode ((byte) (bits[3] >> 31)); + encoder.Encode ((uint) bits[2]); + encoder.Encode ((uint) bits[1]); + encoder.Encode ((uint) bits[0]); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + + public void EmitAttribute (FieldBuilder builder, decimal value, Location loc) + { + var ctor = module.PredefinedMembers.DecimalConstantAttributeCtor.Resolve (loc); + if (ctor == null) + return; + + int[] bits = decimal.GetBits (value); + AttributeEncoder encoder = new AttributeEncoder (); + encoder.Encode ((byte) (bits[3] >> 16)); + encoder.Encode ((byte) (bits[3] >> 31)); + encoder.Encode ((uint) bits[2]); + encoder.Encode ((uint) bits[1]); + encoder.Encode ((uint) bits[0]); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + public class PredefinedStateMachineAttribute : PredefinedAttribute + { + public PredefinedStateMachineAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (MethodBuilder builder, StateMachine type) + { + var predefined_ctor = module.PredefinedMembers.AsyncStateMachineAttributeCtor; + + var ctor = predefined_ctor.Get (); + + if (ctor == null) + return; + + AttributeEncoder encoder = new AttributeEncoder (); + encoder.EncodeTypeName (type); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + public class PredefinedDynamicAttribute : PredefinedAttribute + { + MethodSpec tctor; + + public PredefinedDynamicAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (FieldBuilder builder, TypeSpec type, Location loc) + { + if (ResolveTransformationCtor (loc)) { + var cab = new CustomAttributeBuilder ((ConstructorInfo) tctor.GetMetaInfo (), new object[] { GetTransformationFlags (type) }); + builder.SetCustomAttribute (cab); + } + } + + public void EmitAttribute (ParameterBuilder builder, TypeSpec type, Location loc) + { + if (ResolveTransformationCtor (loc)) { + var cab = new CustomAttributeBuilder ((ConstructorInfo) tctor.GetMetaInfo (), new object[] { GetTransformationFlags (type) }); + builder.SetCustomAttribute (cab); + } + } + + public void EmitAttribute (PropertyBuilder builder, TypeSpec type, Location loc) + { + if (ResolveTransformationCtor (loc)) { + var cab = new CustomAttributeBuilder ((ConstructorInfo) tctor.GetMetaInfo (), new object[] { GetTransformationFlags (type) }); + builder.SetCustomAttribute (cab); + } + } + + public void EmitAttribute (TypeBuilder builder, TypeSpec type, Location loc) + { + if (ResolveTransformationCtor (loc)) { + var cab = new CustomAttributeBuilder ((ConstructorInfo) tctor.GetMetaInfo (), new object[] { GetTransformationFlags (type) }); + builder.SetCustomAttribute (cab); + } + } + + // + // When any element of the type is a dynamic type + // + // This method builds a transformation array for dynamic types + // used in places where DynamicAttribute cannot be applied to. + // It uses bool flag when type is of dynamic type and each + // section always starts with "false" for some reason. + // + // LAMESPEC: This should be part of C# specification + // + // Example: Func + // Transformation: { false, true, false, false, true } + // + static bool[] GetTransformationFlags (TypeSpec t) + { + bool[] element; + var ac = t as ArrayContainer; + if (ac != null) { + element = GetTransformationFlags (ac.Element); + if (element == null) + return new bool[] { false, false }; + + bool[] res = new bool[element.Length + 1]; + res[0] = false; + Array.Copy (element, 0, res, 1, element.Length); + return res; + } + + if (t == null) + return null; + + if (t.IsGeneric) { + List transform = null; + var targs = t.TypeArguments; + for (int i = 0; i < targs.Length; ++i) { + element = GetTransformationFlags (targs[i]); + if (element != null) { + if (transform == null) { + transform = new List (); + for (int ii = 0; ii <= i; ++ii) + transform.Add (false); + } + + transform.AddRange (element); + } else if (transform != null) { + transform.Add (false); + } + } + + if (transform != null) + return transform.ToArray (); + } + + if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return new bool[] { true }; + + return null; + } + + bool ResolveTransformationCtor (Location loc) + { + if (tctor != null) + return true; + + tctor = module.PredefinedMembers.DynamicAttributeCtor.Resolve (loc); + return tctor != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cfold.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cfold.cs new file mode 100644 index 000000000..da85bf386 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cfold.cs @@ -0,0 +1,1192 @@ +// +// cfold.cs: Constant Folding +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Copyright 2002, 2003 Ximian, Inc. +// Copyright 2003-2011, Novell, Inc. +// +using System; + +namespace Mono.CSharp { + + public static class ConstantFold + { + public static TypeSpec[] CreateBinaryPromotionsTypes (BuiltinTypes types) + { + return new TypeSpec[] { + types.Decimal, types.Double, types.Float, + types.ULong, types.Long, types.UInt + }; + } + + // + // Performs the numeric promotions on the left and right expresions + // and deposits the results on `lc' and `rc'. + // + // On success, the types of `lc' and `rc' on output will always match, + // and the pair will be one of: + // + // TODO: BinaryFold should be called as an optimization step only, + // error checking here is weak + // + static bool DoBinaryNumericPromotions (ResolveContext rc, ref Constant left, ref Constant right) + { + TypeSpec ltype = left.Type; + TypeSpec rtype = right.Type; + + foreach (TypeSpec t in rc.BuiltinTypes.BinaryPromotionsTypes) { + if (t == ltype) + return t == rtype || ConvertPromotion (rc, ref right, ref left, t); + + if (t == rtype) + return t == ltype || ConvertPromotion (rc, ref left, ref right, t); + } + + left = left.ConvertImplicitly (rc.BuiltinTypes.Int); + right = right.ConvertImplicitly (rc.BuiltinTypes.Int); + return left != null && right != null; + } + + static bool ConvertPromotion (ResolveContext rc, ref Constant prim, ref Constant second, TypeSpec type) + { + Constant c = prim.ConvertImplicitly (type); + if (c != null) { + prim = c; + return true; + } + + if (type.BuiltinType == BuiltinTypeSpec.Type.UInt) { + type = rc.BuiltinTypes.Long; + prim = prim.ConvertImplicitly (type); + second = second.ConvertImplicitly (type); + return prim != null && second != null; + } + + return false; + } + + internal static void Error_CompileTimeOverflow (ResolveContext rc, Location loc) + { + rc.Report.Error (220, loc, "The operation overflows at compile time in checked mode"); + } + + /// + /// Constant expression folder for binary operations. + /// + /// Returns null if the expression can not be folded. + /// + static public Constant BinaryFold (ResolveContext ec, Binary.Operator oper, + Constant left, Constant right, Location loc) + { + Constant result = null; + + if (left is EmptyConstantCast) + return BinaryFold (ec, oper, ((EmptyConstantCast)left).child, right, loc); + + if (left is SideEffectConstant) { + result = BinaryFold (ec, oper, ((SideEffectConstant) left).value, right, loc); + if (result == null) + return null; + return new SideEffectConstant (result, left, loc); + } + + if (right is EmptyConstantCast) + return BinaryFold (ec, oper, left, ((EmptyConstantCast)right).child, loc); + + if (right is SideEffectConstant) { + result = BinaryFold (ec, oper, left, ((SideEffectConstant) right).value, loc); + if (result == null) + return null; + return new SideEffectConstant (result, right, loc); + } + + TypeSpec lt = left.Type; + TypeSpec rt = right.Type; + bool bool_res; + + if (lt.BuiltinType == BuiltinTypeSpec.Type.Bool && lt == rt) { + bool lv = (bool) left.GetValue (); + bool rv = (bool) right.GetValue (); + switch (oper) { + case Binary.Operator.BitwiseAnd: + case Binary.Operator.LogicalAnd: + return new BoolConstant (ec.BuiltinTypes, lv && rv, left.Location); + case Binary.Operator.BitwiseOr: + case Binary.Operator.LogicalOr: + return new BoolConstant (ec.BuiltinTypes, lv || rv, left.Location); + case Binary.Operator.ExclusiveOr: + return new BoolConstant (ec.BuiltinTypes, lv ^ rv, left.Location); + case Binary.Operator.Equality: + return new BoolConstant (ec.BuiltinTypes, lv == rv, left.Location); + case Binary.Operator.Inequality: + return new BoolConstant (ec.BuiltinTypes, lv != rv, left.Location); + } + return null; + } + + // + // During an enum evaluation, none of the rules are valid + // Not sure whether it is bug in csc or in documentation + // + if (ec.HasSet (ResolveContext.Options.EnumScope)){ + if (left is EnumConstant) + left = ((EnumConstant) left).Child; + + if (right is EnumConstant) + right = ((EnumConstant) right).Child; + } else if (left is EnumConstant && rt == lt) { + switch (oper){ + /// + /// E operator |(E x, E y); + /// E operator &(E x, E y); + /// E operator ^(E x, E y); + /// + case Binary.Operator.BitwiseOr: + case Binary.Operator.BitwiseAnd: + case Binary.Operator.ExclusiveOr: + result = BinaryFold (ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc); + if (result != null) + result = result.Reduce (ec, lt); + return result; + + /// + /// U operator -(E x, E y); + /// + case Binary.Operator.Subtraction: + result = BinaryFold (ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc); + if (result != null) + result = result.Reduce (ec, EnumSpec.GetUnderlyingType (lt)); + return result; + + /// + /// bool operator ==(E x, E y); + /// bool operator !=(E x, E y); + /// bool operator <(E x, E y); + /// bool operator >(E x, E y); + /// bool operator <=(E x, E y); + /// bool operator >=(E x, E y); + /// + case Binary.Operator.Equality: + case Binary.Operator.Inequality: + case Binary.Operator.LessThan: + case Binary.Operator.GreaterThan: + case Binary.Operator.LessThanOrEqual: + case Binary.Operator.GreaterThanOrEqual: + return BinaryFold(ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc); + } + return null; + } + + switch (oper){ + case Binary.Operator.BitwiseOr: + // + // bool? operator |(bool? x, bool? y); + // + if ((lt.BuiltinType == BuiltinTypeSpec.Type.Bool && right is NullLiteral) || + (rt.BuiltinType == BuiltinTypeSpec.Type.Bool && left is NullLiteral)) { + var b = new Binary (oper, left, right).ResolveOperator (ec); + + // false | null => null + // null | false => null + if ((right is NullLiteral && left.IsDefaultValue) || (left is NullLiteral && right.IsDefaultValue)) + return Nullable.LiftedNull.CreateFromExpression (ec, b); + + // true | null => true + // null | true => true + return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, true, loc), b); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + if (left is IntConstant){ + int res = ((IntConstant) left).Value | ((IntConstant) right).Value; + + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is UIntConstant){ + uint res = ((UIntConstant)left).Value | ((UIntConstant)right).Value; + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is LongConstant){ + long res = ((LongConstant)left).Value | ((LongConstant)right).Value; + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is ULongConstant){ + ulong res = ((ULongConstant)left).Value | + ((ULongConstant)right).Value; + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } + break; + + case Binary.Operator.BitwiseAnd: + // + // bool? operator &(bool? x, bool? y); + // + if ((lt.BuiltinType == BuiltinTypeSpec.Type.Bool && right is NullLiteral) || + (rt.BuiltinType == BuiltinTypeSpec.Type.Bool && left is NullLiteral)) { + var b = new Binary (oper, left, right).ResolveOperator (ec); + + // false & null => false + // null & false => false + if ((right is NullLiteral && left.IsDefaultValue) || (left is NullLiteral && right.IsDefaultValue)) + return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, false, loc), b); + + // true & null => null + // null & true => null + return Nullable.LiftedNull.CreateFromExpression (ec, b); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + /// + /// int operator &(int x, int y); + /// uint operator &(uint x, uint y); + /// long operator &(long x, long y); + /// ulong operator &(ulong x, ulong y); + /// + if (left is IntConstant){ + int res = ((IntConstant) left).Value & ((IntConstant) right).Value; + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is UIntConstant){ + uint res = ((UIntConstant)left).Value & ((UIntConstant)right).Value; + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is LongConstant){ + long res = ((LongConstant)left).Value & ((LongConstant)right).Value; + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is ULongConstant){ + ulong res = ((ULongConstant)left).Value & + ((ULongConstant)right).Value; + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } + break; + + case Binary.Operator.ExclusiveOr: + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + if (left is IntConstant){ + int res = ((IntConstant) left).Value ^ ((IntConstant) right).Value; + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is UIntConstant){ + uint res = ((UIntConstant)left).Value ^ ((UIntConstant)right).Value; + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is LongConstant){ + long res = ((LongConstant)left).Value ^ ((LongConstant)right).Value; + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is ULongConstant){ + ulong res = ((ULongConstant)left).Value ^ + ((ULongConstant)right).Value; + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } + break; + + case Binary.Operator.Addition: + // + // If both sides are strings, then concatenate + // + // string operator + (string x, string y) + // + if (lt.BuiltinType == BuiltinTypeSpec.Type.String || rt.BuiltinType == BuiltinTypeSpec.Type.String){ + if (lt == rt) + return new StringConstant (ec.BuiltinTypes, (string)left.GetValue () + (string)right.GetValue (), + left.Location); + + if (lt == InternalType.NullLiteral || left.IsNull) + return new StringConstant (ec.BuiltinTypes, "" + right.GetValue (), left.Location); + + if (rt == InternalType.NullLiteral || right.IsNull) + return new StringConstant (ec.BuiltinTypes, left.GetValue () + "", left.Location); + + return null; + } + + // + // string operator + (string x, object y) + // + if (lt == InternalType.NullLiteral) { + if (rt.BuiltinType == BuiltinTypeSpec.Type.Object) + return new StringConstant (ec.BuiltinTypes, "" + right.GetValue (), left.Location); + + if (lt == rt) { + ec.Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'", + "+", lt.GetSignatureForError (), rt.GetSignatureForError ()); + return null; + } + + return right; + } + + // + // string operator + (object x, string y) + // + if (rt == InternalType.NullLiteral) { + if (lt.BuiltinType == BuiltinTypeSpec.Type.Object) + return new StringConstant (ec.BuiltinTypes, right.GetValue () + "", left.Location); + + return left; + } + + // + // handle "E operator + (E x, U y)" + // handle "E operator + (Y y, E x)" + // + EnumConstant lc = left as EnumConstant; + EnumConstant rc = right as EnumConstant; + if (lc != null || rc != null){ + if (lc == null) { + lc = rc; + lt = lc.Type; + right = left; + } + + // U has to be implicitly convetible to E.base + right = right.ConvertImplicitly (lc.Child.Type); + if (right == null) + return null; + + result = BinaryFold (ec, oper, lc.Child, right, loc); + if (result == null) + return null; + + result = result.Reduce (ec, lt); + if (result == null || lt.IsEnum) + return result; + + return new EnumConstant (result, lt); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value + + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value + + ((DoubleConstant) right).Value); + + return new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a + b); + else + res = unchecked (a + b); + + result = new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value + + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value + + ((ULongConstant) right).Value); + + result = new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value + + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value + + ((LongConstant) right).Value); + + result = new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value + + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value + + ((UIntConstant) right).Value); + + result = new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value + + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value + + ((IntConstant) right).Value); + + result = new IntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value + + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value + + ((DecimalConstant) right).Value); + + result = new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + } + + return result; + + case Binary.Operator.Subtraction: + // + // handle "E operator - (E x, U y)" + // handle "E operator - (Y y, E x)" + // + lc = left as EnumConstant; + rc = right as EnumConstant; + if (lc != null || rc != null){ + if (lc == null) { + lc = rc; + lt = lc.Type; + right = left; + } + + // U has to be implicitly convetible to E.base + right = right.ConvertImplicitly (lc.Child.Type); + if (right == null) + return null; + + result = BinaryFold (ec, oper, lc.Child, right, loc); + if (result == null) + return null; + + result = result.Reduce (ec, lt); + if (result == null) + return null; + + return new EnumConstant (result, lt); + } + + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value - + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value - + ((DoubleConstant) right).Value); + + result = new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a - b); + else + res = unchecked (a - b); + + result = new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value - + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value - + ((ULongConstant) right).Value); + + result = new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value - + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value - + ((LongConstant) right).Value); + + result = new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value - + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value - + ((UIntConstant) right).Value); + + result = new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value - + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value - + ((IntConstant) right).Value); + + result = new IntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value - + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value - + ((DecimalConstant) right).Value); + + return new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } else { + throw new Exception ( "Unexepected subtraction input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + } + + return result; + + case Binary.Operator.Multiply: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value * + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value * + ((DoubleConstant) right).Value); + + return new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a * b); + else + res = unchecked (a * b); + + return new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value * + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value * + ((ULongConstant) right).Value); + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value * + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value * + ((LongConstant) right).Value); + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value * + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value * + ((UIntConstant) right).Value); + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value * + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value * + ((IntConstant) right).Value); + + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value * + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value * + ((DecimalConstant) right).Value); + + return new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } else { + throw new Exception ( "Unexepected multiply input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + } + break; + + case Binary.Operator.Division: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value / + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value / + ((DoubleConstant) right).Value); + + return new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a / b); + else + res = unchecked (a / b); + + return new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value / + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value / + ((ULongConstant) right).Value); + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value / + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value / + ((LongConstant) right).Value); + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value / + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value / + ((UIntConstant) right).Value); + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value / + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value / + ((IntConstant) right).Value); + + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value / + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value / + ((DecimalConstant) right).Value); + + return new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } else { + throw new Exception ( "Unexepected division input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + + } catch (DivideByZeroException) { + ec.Report.Error (20, loc, "Division by constant zero"); + } + + break; + + case Binary.Operator.Modulus: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value % + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value % + ((DoubleConstant) right).Value); + + return new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a % b); + else + res = unchecked (a % b); + + return new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value % + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value % + ((ULongConstant) right).Value); + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value % + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value % + ((LongConstant) right).Value); + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value % + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value % + ((UIntConstant) right).Value); + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value % + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value % + ((IntConstant) right).Value); + + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } + + if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value % + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value % + ((DecimalConstant) right).Value); + + return new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } + + throw new Exception ( "Unexepected modulus input: " + left); + } catch (DivideByZeroException){ + ec.Report.Error (20, loc, "Division by constant zero"); + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + } + break; + + // + // There is no overflow checking on left shift + // + case Binary.Operator.LeftShift: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + IntConstant ic = right.ConvertImplicitly (ec.BuiltinTypes.Int) as IntConstant; + if (ic == null){ + return null; + } + + int lshift_val = ic.Value; + switch (left.Type.BuiltinType) { + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (ec.BuiltinTypes, ((ULongConstant) left).Value << lshift_val, left.Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (ec.BuiltinTypes, ((LongConstant) left).Value << lshift_val, left.Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (ec.BuiltinTypes, ((UIntConstant) left).Value << lshift_val, left.Location); + } + + // null << value => null + if (left is NullLiteral) + return (Constant) new Binary (oper, left, right).ResolveOperator (ec); + + left = left.ConvertImplicitly (ec.BuiltinTypes.Int); + if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Int) + return new IntConstant (ec.BuiltinTypes, ((IntConstant) left).Value << lshift_val, left.Location); + + return null; + + // + // There is no overflow checking on right shift + // + case Binary.Operator.RightShift: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + IntConstant sic = right.ConvertImplicitly (ec.BuiltinTypes.Int) as IntConstant; + if (sic == null){ + return null; + } + int rshift_val = sic.Value; + switch (left.Type.BuiltinType) { + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (ec.BuiltinTypes, ((ULongConstant) left).Value >> rshift_val, left.Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (ec.BuiltinTypes, ((LongConstant) left).Value >> rshift_val, left.Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (ec.BuiltinTypes, ((UIntConstant) left).Value >> rshift_val, left.Location); + } + + // null >> value => null + if (left is NullLiteral) + return (Constant) new Binary (oper, left, right).ResolveOperator (ec); + + left = left.ConvertImplicitly (ec.BuiltinTypes.Int); + if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Int) + return new IntConstant (ec.BuiltinTypes, ((IntConstant) left).Value >> rshift_val, left.Location); + + return null; + + case Binary.Operator.Equality: + if (TypeSpec.IsReferenceType (lt) && TypeSpec.IsReferenceType (rt) || + (left is Nullable.LiftedNull && right.IsNull) || + (right is Nullable.LiftedNull && left.IsNull)) { + if (left.IsNull || right.IsNull) { + return ReducedExpression.Create ( + new BoolConstant (ec.BuiltinTypes, left.IsNull == right.IsNull, left.Location), + new Binary (oper, left, right)); + } + + if (left is StringConstant && right is StringConstant) + return new BoolConstant (ec.BuiltinTypes, + ((StringConstant) left).Value == ((StringConstant) right).Value, left.Location); + + return null; + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value == + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue == + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value == + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value == + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value == + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value == + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.Inequality: + if (TypeSpec.IsReferenceType (lt) && TypeSpec.IsReferenceType (rt) || + (left is Nullable.LiftedNull && right.IsNull) || + (right is Nullable.LiftedNull && left.IsNull)) { + if (left.IsNull || right.IsNull) { + return ReducedExpression.Create ( + new BoolConstant (ec.BuiltinTypes, left.IsNull != right.IsNull, left.Location), + new Binary (oper, left, right)); + } + + if (left is StringConstant && right is StringConstant) + return new BoolConstant (ec.BuiltinTypes, + ((StringConstant) left).Value != ((StringConstant) right).Value, left.Location); + + return null; + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value != + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue != + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value != + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value != + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value != + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value != + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.LessThan: + if (right is NullLiteral) { + if (left is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value < + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue < + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value < + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value < + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value < + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value < + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.GreaterThan: + if (right is NullLiteral) { + if (left is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value > + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue > + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value > + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value > + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value > + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value > + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.GreaterThanOrEqual: + if (right is NullLiteral) { + if (left is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value >= + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue >= + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value >= + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value >= + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value >= + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value >= + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.LessThanOrEqual: + if (right is NullLiteral) { + if (left is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value <= + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue <= + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value <= + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value <= + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value <= + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value <= + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + } + + return null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/class.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/class.cs new file mode 100644 index 000000000..ae4c02766 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/class.cs @@ -0,0 +1,3895 @@ +// +// class.cs: Class and Struct handlers +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2011 Novell, Inc +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// + +using System; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.Diagnostics; +using Mono.CompilerServices.SymbolWriter; + +#if NET_2_1 +using XmlElement = System.Object; +#endif + +#if STATIC +using SecurityType = System.Collections.Generic.List; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using SecurityType = System.Collections.Generic.Dictionary; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // + // General types container, used as a base class for all constructs which can hold types + // + public abstract class TypeContainer : MemberCore + { + public readonly MemberKind Kind; + public readonly string Basename; + + protected List containers; + + TypeDefinition main_container; + + protected Dictionary defined_names; + + protected bool is_defined; + + public int CounterAnonymousMethods { get; set; } + public int CounterAnonymousContainers { get; set; } + public int CounterSwitchTypes { get; set; } + + protected TypeContainer (TypeContainer parent, MemberName name, Attributes attrs, MemberKind kind) + : base (parent, name, attrs) + { + this.Kind = kind; + if (name != null) + this.Basename = name.Basename; + + defined_names = new Dictionary (); + } + + public override TypeSpec CurrentType { + get { + return null; + } + } + + public Dictionary DefinedNames { + get { + return defined_names; + } + } + + public TypeDefinition PartialContainer { + get { + return main_container; + } + protected set { + main_container = value; + } + } + + public IList Containers { + get { + return containers; + } + } + + // + // Any unattached attributes during parsing get added here. User + // by FULL_AST mode + // + public Attributes UnattachedAttributes { + get; set; + } + + public void AddCompilerGeneratedClass (CompilerGeneratedContainer c) + { + AddTypeContainerMember (c); + } + + public virtual void AddPartial (TypeDefinition next_part) + { + MemberCore mc; + (PartialContainer ?? this).defined_names.TryGetValue (next_part.Basename, out mc); + + AddPartial (next_part, mc as TypeDefinition); + } + + protected void AddPartial (TypeDefinition next_part, TypeDefinition existing) + { + next_part.ModFlags |= Modifiers.PARTIAL; + + if (existing == null) { + AddTypeContainer (next_part); + return; + } + + if ((existing.ModFlags & Modifiers.PARTIAL) == 0) { + if (existing.Kind != next_part.Kind) { + AddTypeContainer (next_part); + } else { + Report.SymbolRelatedToPreviousError (next_part); + Error_MissingPartialModifier (existing); + } + + return; + } + + if (existing.Kind != next_part.Kind) { + Report.SymbolRelatedToPreviousError (existing); + Report.Error (261, next_part.Location, + "Partial declarations of `{0}' must be all classes, all structs or all interfaces", + next_part.GetSignatureForError ()); + } + + if ((existing.ModFlags & Modifiers.AccessibilityMask) != (next_part.ModFlags & Modifiers.AccessibilityMask) && + ((existing.ModFlags & Modifiers.DEFAULT_ACCESS_MODIFIER) == 0 && + (next_part.ModFlags & Modifiers.DEFAULT_ACCESS_MODIFIER) == 0)) { + Report.SymbolRelatedToPreviousError (existing); + Report.Error (262, next_part.Location, + "Partial declarations of `{0}' have conflicting accessibility modifiers", + next_part.GetSignatureForError ()); + } + + var tc_names = existing.CurrentTypeParameters; + if (tc_names != null) { + for (int i = 0; i < tc_names.Count; ++i) { + var tp = next_part.MemberName.TypeParameters[i]; + if (tc_names[i].MemberName.Name != tp.MemberName.Name) { + Report.SymbolRelatedToPreviousError (existing.Location, ""); + Report.Error (264, next_part.Location, "Partial declarations of `{0}' must have the same type parameter names in the same order", + next_part.GetSignatureForError ()); + break; + } + + if (tc_names[i].Variance != tp.Variance) { + Report.SymbolRelatedToPreviousError (existing.Location, ""); + Report.Error (1067, next_part.Location, "Partial declarations of `{0}' must have the same type parameter variance modifiers", + next_part.GetSignatureForError ()); + break; + } + } + } + + if ((next_part.ModFlags & Modifiers.DEFAULT_ACCESS_MODIFIER) != 0) { + existing.ModFlags |= next_part.ModFlags & ~(Modifiers.DEFAULT_ACCESS_MODIFIER | Modifiers.AccessibilityMask); + } else if ((existing.ModFlags & Modifiers.DEFAULT_ACCESS_MODIFIER) != 0) { + existing.ModFlags &= ~(Modifiers.DEFAULT_ACCESS_MODIFIER | Modifiers.AccessibilityMask); + existing.ModFlags |= next_part.ModFlags; + } else { + existing.ModFlags |= next_part.ModFlags; + } + + existing.Definition.Modifiers = existing.ModFlags; + + if (next_part.attributes != null) { + if (existing.attributes == null) + existing.attributes = next_part.attributes; + else + existing.attributes.AddAttributes (next_part.attributes.Attrs); + } + + next_part.PartialContainer = existing; + + existing.AddPartialPart (next_part); + + AddTypeContainerMember (next_part); + } + + public virtual void AddTypeContainer (TypeContainer tc) + { + AddTypeContainerMember (tc); + + var tparams = tc.MemberName.TypeParameters; + if (tparams != null && tc.PartialContainer != null) { + var td = (TypeDefinition) tc; + for (int i = 0; i < tparams.Count; ++i) { + var tp = tparams[i]; + if (tp.MemberName == null) + continue; + + td.AddNameToContainer (tp, tp.Name); + } + } + } + + protected virtual void AddTypeContainerMember (TypeContainer tc) + { + containers.Add (tc); + } + + public virtual void CloseContainer () + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.CloseContainer (); + } + } + } + + public virtual void CreateMetadataName (StringBuilder sb) + { + if (Parent != null && Parent.MemberName != null) + Parent.CreateMetadataName (sb); + + MemberName.CreateMetadataName (sb); + } + + public virtual bool CreateContainer () + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.CreateContainer (); + } + } + + return true; + } + + public override bool Define () + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.Define (); + } + } + + // Release cache used by parser only + if (Module.Evaluator == null) { + defined_names = null; + } else { + defined_names.Clear (); + } + + return true; + } + + public virtual void PrepareEmit () + { + if (containers != null) { + foreach (var t in containers) { + try { + t.PrepareEmit (); + } catch (Exception e) { + if (MemberName == MemberName.Null) + throw; + + throw new InternalErrorException (t, e); + } + } + } + } + + public virtual bool DefineContainer () + { + if (is_defined) + return true; + + is_defined = true; + + DoDefineContainer (); + + if (containers != null) { + foreach (TypeContainer tc in containers) { + try { + tc.DefineContainer (); + } catch (Exception e) { + if (MemberName == MemberName.Null) + throw; + + throw new InternalErrorException (tc, e); + } + } + } + + return true; + } + + public virtual void ExpandBaseInterfaces () + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.ExpandBaseInterfaces (); + } + } + } + + protected virtual void DefineNamespace () + { + if (containers != null) { + foreach (var tc in containers) { + try { + tc.DefineNamespace (); + } catch (Exception e) { + throw new InternalErrorException (tc, e); + } + } + } + } + + protected virtual void DoDefineContainer () + { + } + + public virtual void EmitContainer () + { + if (containers != null) { + for (int i = 0; i < containers.Count; ++i) + containers[i].EmitContainer (); + } + } + + protected void Error_MissingPartialModifier (MemberCore type) + { + Report.Error (260, type.Location, + "Missing partial modifier on declaration of type `{0}'. Another partial declaration of this type exists", + type.GetSignatureForError ()); + } + + public override string GetSignatureForDocumentation () + { + if (Parent != null && Parent.MemberName != null) + return Parent.GetSignatureForDocumentation () + "." + MemberName.GetSignatureForDocumentation (); + + return MemberName.GetSignatureForDocumentation (); + } + + public override string GetSignatureForError () + { + if (Parent != null && Parent.MemberName != null) + return Parent.GetSignatureForError () + "." + MemberName.GetSignatureForError (); + + return MemberName.GetSignatureForError (); + } + + public string GetSignatureForMetadata () + { + if (Parent is TypeDefinition) { + return Parent.GetSignatureForMetadata () + "+" + TypeNameParser.Escape (MemberName.Basename); + } + + var sb = new StringBuilder (); + CreateMetadataName (sb); + return sb.ToString (); + } + + public virtual void RemoveContainer (TypeContainer cont) + { + if (containers != null) + containers.Remove (cont); + + var tc = Parent == Module ? Module : this; + tc.defined_names.Remove (cont.Basename); + } + + public virtual void VerifyMembers () + { + if (containers != null) { + foreach (TypeContainer tc in containers) + tc.VerifyMembers (); + } + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.WriteDebugSymbol (file); + } + } + } + } + + public abstract class TypeDefinition : TypeContainer, ITypeDefinition + { + // + // Different context is needed when resolving type container base + // types. Type names come from the parent scope but type parameter + // names from the container scope. + // + public struct BaseContext : IMemberContext + { + TypeContainer tc; + + public BaseContext (TypeContainer tc) + { + this.tc = tc; + } + + #region IMemberContext Members + + public CompilerContext Compiler { + get { return tc.Compiler; } + } + + public TypeSpec CurrentType { + get { return tc.PartialContainer.CurrentType; } + } + + public TypeParameters CurrentTypeParameters { + get { return tc.PartialContainer.CurrentTypeParameters; } + } + + public MemberCore CurrentMemberDefinition { + get { return tc; } + } + + public bool IsObsolete { + get { return tc.IsObsolete; } + } + + public bool IsUnsafe { + get { return tc.IsUnsafe; } + } + + public bool IsStatic { + get { return tc.IsStatic; } + } + + public ModuleContainer Module { + get { return tc.Module; } + } + + public string GetSignatureForError () + { + return tc.GetSignatureForError (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + return null; + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + return tc.Parent.LookupNamespaceAlias (name); + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + if (arity == 0) { + var tp = CurrentTypeParameters; + if (tp != null) { + TypeParameter t = tp.Find (name); + if (t != null) + return new TypeParameterExpr (t, loc); + } + } + + return tc.Parent.LookupNamespaceOrType (name, arity, mode, loc); + } + + #endregion + } + + [Flags] + enum CachedMethods + { + Equals = 1, + GetHashCode = 1 << 1, + HasStaticFieldInitializer = 1 << 2 + } + + readonly List members; + + // Holds a list of fields that have initializers + protected List initialized_fields; + + // Holds a list of static fields that have initializers + protected List initialized_static_fields; + + Dictionary hoisted_base_call_proxies; + + Dictionary Cache = new Dictionary (); + + // + // Points to the first non-static field added to the container. + // + // This is an arbitrary choice. We are interested in looking at _some_ non-static field, + // and the first one's as good as any. + // + protected FieldBase first_nonstatic_field; + + // + // This one is computed after we can distinguish interfaces + // from classes from the arraylist `type_bases' + // + protected TypeSpec base_type; + FullNamedExpression base_type_expr; // TODO: It's temporary variable + protected TypeSpec[] iface_exprs; + + protected List type_bases; + + // Partial parts for classes only + List class_partial_parts; + + TypeDefinition InTransit; + + public TypeBuilder TypeBuilder; + GenericTypeParameterBuilder[] all_tp_builders; + // + // All recursive type parameters put together sharing same + // TypeParameter instances + // + TypeParameters all_type_parameters; + + public const string DefaultIndexerName = "Item"; + + bool has_normal_indexers; + string indexer_name; + protected bool requires_delayed_unmanagedtype_check; + bool error; + bool members_defined; + bool members_defined_ok; + protected bool has_static_constructor; + + private CachedMethods cached_method; + + protected TypeSpec spec; + TypeSpec current_type; + + public int DynamicSitesCounter; + public int AnonymousMethodsCounter; + public int MethodGroupsCounter; + + static readonly string[] attribute_targets = new [] { "type" }; + static readonly string[] attribute_targets_primary = new [] { "type", "method" }; + + /// + /// The pending methods that need to be implemented + // (interfaces or abstract methods) + /// + PendingImplementation pending; + + protected TypeDefinition (TypeContainer parent, MemberName name, Attributes attrs, MemberKind kind) + : base (parent, name, attrs, kind) + { + PartialContainer = this; + members = new List (); + } + + #region Properties + + public List BaseTypeExpressions { + get { + return type_bases; + } + } + + public override TypeSpec CurrentType { + get { + if (current_type == null) { + if (IsGenericOrParentIsGeneric) { + // + // Switch to inflated version as it's used by all expressions + // + var targs = CurrentTypeParameters == null ? TypeSpec.EmptyTypes : CurrentTypeParameters.Types; + current_type = spec.MakeGenericType (this, targs); + } else { + current_type = spec; + } + } + + return current_type; + } + } + + public override TypeParameters CurrentTypeParameters { + get { + return PartialContainer.MemberName.TypeParameters; + } + } + + int CurrentTypeParametersStartIndex { + get { + int total = all_tp_builders.Length; + if (CurrentTypeParameters != null) { + return total - CurrentTypeParameters.Count; + } + return total; + } + } + + public virtual AssemblyDefinition DeclaringAssembly { + get { + return Module.DeclaringAssembly; + } + } + + IAssemblyDefinition ITypeDefinition.DeclaringAssembly { + get { + return Module.DeclaringAssembly; + } + } + + public TypeSpec Definition { + get { + return spec; + } + } + + public bool HasMembersDefined { + get { + return members_defined; + } + } + + public List TypeBaseExpressions { + get { + return type_bases; + } + } + + public bool HasInstanceConstructor { + get { + return (caching_flags & Flags.HasInstanceConstructor) != 0; + } + set { + caching_flags |= Flags.HasInstanceConstructor; + } + } + + // Indicated whether container has StructLayout attribute set Explicit + public bool HasExplicitLayout { + get { return (caching_flags & Flags.HasExplicitLayout) != 0; } + set { caching_flags |= Flags.HasExplicitLayout; } + } + + public bool HasOperators { + get { + return (caching_flags & Flags.HasUserOperators) != 0; + } + set { + caching_flags |= Flags.HasUserOperators; + } + } + + public bool HasStructLayout { + get { return (caching_flags & Flags.HasStructLayout) != 0; } + set { caching_flags |= Flags.HasStructLayout; } + } + + public TypeSpec[] Interfaces { + get { + return iface_exprs; + } + } + + public bool IsGenericOrParentIsGeneric { + get { + return all_type_parameters != null; + } + } + + public bool IsTopLevel { + get { + return !(Parent is TypeDefinition); + } + } + + public bool IsPartial { + get { + return (ModFlags & Modifiers.PARTIAL) != 0; + } + } + + bool ITypeDefinition.IsTypeForwarder { + get { + return false; + } + } + + bool ITypeDefinition.IsCyclicTypeForwarder { + get { + return false; + } + } + + // + // Returns true for secondary partial containers + // + bool IsPartialPart { + get { + return PartialContainer != this; + } + } + + public MemberCache MemberCache { + get { + return spec.MemberCache; + } + } + + public List Members { + get { + return members; + } + } + + string ITypeDefinition.Namespace { + get { + var p = Parent; + while (p.Kind != MemberKind.Namespace) + p = p.Parent; + + return p.MemberName == null ? null : p.GetSignatureForError (); + } + } + + public ParametersCompiled PrimaryConstructorParameters { get; set; } + + public Arguments PrimaryConstructorBaseArguments { get; set; } + + public Location PrimaryConstructorBaseArgumentsStart { get; set; } + + public TypeParameters TypeParametersAll { + get { + return all_type_parameters; + } + } + + public override string[] ValidAttributeTargets { + get { + return PrimaryConstructorParameters != null ? attribute_targets_primary : attribute_targets; + } + } + +#if FULL_AST + public bool HasOptionalSemicolon { + get; + private set; + } + Location optionalSemicolon; + public Location OptionalSemicolon { + get { + return optionalSemicolon; + } + set { + optionalSemicolon = value; + HasOptionalSemicolon = true; + } + } +#endif + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public void AddMember (MemberCore symbol) + { + if (symbol.MemberName.ExplicitInterface != null) { + if (!(Kind == MemberKind.Class || Kind == MemberKind.Struct)) { + Report.Error (541, symbol.Location, + "`{0}': explicit interface declaration can only be declared in a class or struct", + symbol.GetSignatureForError ()); + } + } + + AddNameToContainer (symbol, symbol.MemberName.Name); + members.Add (symbol); + } + + public override void AddTypeContainer (TypeContainer tc) + { + AddNameToContainer (tc, tc.Basename); + + base.AddTypeContainer (tc); + } + + protected override void AddTypeContainerMember (TypeContainer tc) + { + members.Add (tc); + + if (containers == null) + containers = new List (); + + base.AddTypeContainerMember (tc); + } + + // + // Adds the member to defined_names table. It tests for duplications and enclosing name conflicts + // + public virtual void AddNameToContainer (MemberCore symbol, string name) + { + if (((ModFlags | symbol.ModFlags) & Modifiers.COMPILER_GENERATED) != 0) + return; + + MemberCore mc; + if (!PartialContainer.defined_names.TryGetValue (name, out mc)) { + PartialContainer.defined_names.Add (name, symbol); + return; + } + + if (symbol.EnableOverloadChecks (mc)) + return; + + InterfaceMemberBase im = mc as InterfaceMemberBase; + if (im != null && im.IsExplicitImpl) + return; + + Report.SymbolRelatedToPreviousError (mc); + if ((mc.ModFlags & Modifiers.PARTIAL) != 0 && (symbol is ClassOrStruct || symbol is Interface)) { + Error_MissingPartialModifier (symbol); + return; + } + + if (symbol is TypeParameter) { + Report.Error (692, symbol.Location, + "Duplicate type parameter `{0}'", symbol.GetSignatureForError ()); + } else { + Report.Error (102, symbol.Location, + "The type `{0}' already contains a definition for `{1}'", + GetSignatureForError (), name); + } + + return; + } + + public void AddConstructor (Constructor c) + { + AddConstructor (c, false); + } + + public void AddConstructor (Constructor c, bool isDefault) + { + bool is_static = (c.ModFlags & Modifiers.STATIC) != 0; + if (!isDefault) + AddNameToContainer (c, is_static ? Constructor.TypeConstructorName : Constructor.ConstructorName); + + if (is_static && c.ParameterInfo.IsEmpty) { + PartialContainer.has_static_constructor = true; + } else { + PartialContainer.HasInstanceConstructor = true; + } + + members.Add (c); + } + + public bool AddField (FieldBase field) + { + AddMember (field); + + if ((field.ModFlags & Modifiers.STATIC) != 0) + return true; + + var first_field = PartialContainer.first_nonstatic_field; + if (first_field == null) { + PartialContainer.first_nonstatic_field = field; + return true; + } + + if (Kind == MemberKind.Struct && first_field.Parent != field.Parent) { + Report.SymbolRelatedToPreviousError (first_field.Parent); + Report.Warning (282, 3, field.Location, + "struct instance field `{0}' found in different declaration from instance field `{1}'", + field.GetSignatureForError (), first_field.GetSignatureForError ()); + } + return true; + } + + /// + /// Indexer has special handling in constrast to other AddXXX because the name can be driven by IndexerNameAttribute + /// + public void AddIndexer (Indexer i) + { + members.Add (i); + } + + public void AddOperator (Operator op) + { + PartialContainer.HasOperators = true; + AddMember (op); + } + + public void AddPartialPart (TypeDefinition part) + { + if (Kind != MemberKind.Class) + return; + + if (class_partial_parts == null) + class_partial_parts = new List (); + + class_partial_parts.Add (part); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Method) { + foreach (var m in members) { + var c = m as Constructor; + if (c == null) + continue; + + if (c.IsPrimaryConstructor) { + c.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + } + + throw new InternalErrorException (); + } + + if (has_normal_indexers && a.Type == pa.DefaultMember) { + Report.Error (646, a.Location, "Cannot specify the `DefaultMember' attribute on type containing an indexer"); + return; + } + + if (a.Type == pa.Required) { + Report.Error (1608, a.Location, "The RequiredAttribute attribute is not permitted on C# types"); + return; + } + + TypeBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public override AttributeTargets AttributeTargets { + get { + throw new NotSupportedException (); + } + } + + public TypeSpec BaseType { + get { + return spec.BaseType; + } + } + + protected virtual TypeAttributes TypeAttr { + get { + return ModifiersExtensions.TypeAttr (ModFlags, IsTopLevel); + } + } + + public int TypeParametersCount { + get { + return MemberName.Arity; + } + } + + TypeParameterSpec[] ITypeDefinition.TypeParameters { + get { + return PartialContainer.CurrentTypeParameters.Types; + } + } + + public string GetAttributeDefaultMember () + { + return indexer_name ?? DefaultIndexerName; + } + + public bool IsComImport { + get { + if (OptAttributes == null) + return false; + + return OptAttributes.Contains (Module.PredefinedAttributes.ComImport); + } + } + + public virtual void RegisterFieldForInitialization (MemberCore field, FieldInitializer expression) + { + if (IsPartialPart) + PartialContainer.RegisterFieldForInitialization (field, expression); + + if ((field.ModFlags & Modifiers.STATIC) != 0){ + if (initialized_static_fields == null) { + HasStaticFieldInitializer = true; + initialized_static_fields = new List (4); + } + + initialized_static_fields.Add (expression); + } else { + if (initialized_fields == null) + initialized_fields = new List (4); + + initialized_fields.Add (expression); + } + } + + public void ResolveFieldInitializers (BlockContext ec) + { + Debug.Assert (!IsPartialPart); + + if (ec.IsStatic) { + if (initialized_static_fields == null) + return; + + bool has_complex_initializer = !ec.Module.Compiler.Settings.Optimize; + int i; + ExpressionStatement [] init = new ExpressionStatement [initialized_static_fields.Count]; + for (i = 0; i < initialized_static_fields.Count; ++i) { + FieldInitializer fi = initialized_static_fields [i]; + ExpressionStatement s = fi.ResolveStatement (ec); + if (s == null) { + s = EmptyExpressionStatement.Instance; + } else if (!fi.IsSideEffectFree) { + has_complex_initializer = true; + } + + init [i] = s; + } + + for (i = 0; i < initialized_static_fields.Count; ++i) { + FieldInitializer fi = initialized_static_fields [i]; + // + // Need special check to not optimize code like this + // static int a = b = 5; + // static int b = 0; + // + if (!has_complex_initializer && fi.IsDefaultInitializer) + continue; + + ec.AssignmentInfoOffset += fi.AssignmentOffset; + ec.CurrentBlock.AddScopeStatement (new StatementExpression (init [i])); + } + + return; + } + + if (initialized_fields == null) + return; + + for (int i = 0; i < initialized_fields.Count; ++i) { + FieldInitializer fi = initialized_fields [i]; + + // + // Clone before resolving otherwise when field initializer is needed + // in more than 1 constructor any resolve after the initial one would + // only took the resolved expression which is problem for expressions + // that generate extra expressions or code during Resolve phase + // + var cloned = fi.Clone (new CloneContext ()); + + ExpressionStatement s = fi.ResolveStatement (ec); + if (s == null) { + initialized_fields [i] = new FieldInitializer (fi.Field, ErrorExpression.Instance, Location.Null); + continue; + } + + // + // Field is re-initialized to its default value => removed + // + if (fi.IsDefaultInitializer && ec.Module.Compiler.Settings.Optimize) + continue; + + ec.AssignmentInfoOffset += fi.AssignmentOffset; + ec.CurrentBlock.AddScopeStatement (new StatementExpression (s)); + initialized_fields [i] = (FieldInitializer) cloned; + } + } + + public override string DocComment { + get { + return comment; + } + set { + if (value == null) + return; + + comment += value; + } + } + + public PendingImplementation PendingImplementations { + get { return pending; } + } + + internal override void GenerateDocComment (DocumentationBuilder builder) + { + if (IsPartialPart) + return; + + base.GenerateDocComment (builder); + + foreach (var member in members) + member.GenerateDocComment (builder); + } + + public TypeSpec GetAttributeCoClass () + { + if (OptAttributes == null) + return null; + + Attribute a = OptAttributes.Search (Module.PredefinedAttributes.CoClass); + if (a == null) + return null; + + return a.GetCoClassAttributeValue (); + } + + public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) + { + Attribute a = null; + if (OptAttributes != null) { + a = OptAttributes.Search (pa); + } + + if (a == null) + return null; + + return a.GetAttributeUsageAttribute (); + } + + public virtual CompilationSourceFile GetCompilationSourceFile () + { + TypeContainer ns = Parent; + while (true) { + var sf = ns as CompilationSourceFile; + if (sf != null) + return sf; + + ns = ns.Parent; + } + } + + public virtual void SetBaseTypes (List baseTypes) + { + type_bases = baseTypes; + } + + /// + /// This function computes the Base class and also the + /// list of interfaces that the class or struct @c implements. + /// + /// The return value is an array (might be null) of + /// interfaces implemented (as Types). + /// + /// The @base_class argument is set to the base object or null + /// if this is `System.Object'. + /// + protected virtual TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_class = null; + if (type_bases == null) + return null; + + int count = type_bases.Count; + TypeSpec[] ifaces = null; + var base_context = new BaseContext (this); + for (int i = 0, j = 0; i < count; i++){ + FullNamedExpression fne = type_bases [i]; + + var fne_resolved = fne.ResolveAsType (base_context); + if (fne_resolved == null) + continue; + + if (i == 0 && Kind == MemberKind.Class && !fne_resolved.IsInterface) { + if (fne_resolved.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Report.Error (1965, Location, "Class `{0}' cannot derive from the dynamic type", + GetSignatureForError ()); + + continue; + } + + base_type = fne_resolved; + base_class = fne; + continue; + } + + if (ifaces == null) + ifaces = new TypeSpec [count - i]; + + if (fne_resolved.IsInterface) { + for (int ii = 0; ii < j; ++ii) { + if (fne_resolved == ifaces [ii]) { + Report.Error (528, Location, "`{0}' is already listed in interface list", + fne_resolved.GetSignatureForError ()); + break; + } + } + + if (Kind == MemberKind.Interface && !IsAccessibleAs (fne_resolved)) { + Report.Error (61, fne.Location, + "Inconsistent accessibility: base interface `{0}' is less accessible than interface `{1}'", + fne_resolved.GetSignatureForError (), GetSignatureForError ()); + } + } else { + Report.SymbolRelatedToPreviousError (fne_resolved); + if (Kind != MemberKind.Class) { + Report.Error (527, fne.Location, "Type `{0}' in interface list is not an interface", fne_resolved.GetSignatureForError ()); + } else if (base_class != null) + Report.Error (1721, fne.Location, "`{0}': Classes cannot have multiple base classes (`{1}' and `{2}')", + GetSignatureForError (), base_class.GetSignatureForError (), fne_resolved.GetSignatureForError ()); + else { + Report.Error (1722, fne.Location, "`{0}': Base class `{1}' must be specified as first", + GetSignatureForError (), fne_resolved.GetSignatureForError ()); + } + } + + ifaces [j++] = fne_resolved; + } + + return ifaces; + } + + // + // Checks that some operators come in pairs: + // == and != + // > and < + // >= and <= + // true and false + // + // They are matched based on the return type and the argument types + // + void CheckPairedOperators () + { + bool has_equality_or_inequality = false; + List found_matched = new List (); + + for (int i = 0; i < members.Count; ++i) { + var o_a = members[i] as Operator; + if (o_a == null) + continue; + + var o_type = o_a.OperatorType; + if (o_type == Operator.OpType.Equality || o_type == Operator.OpType.Inequality) + has_equality_or_inequality = true; + + if (found_matched.Contains (o_type)) + continue; + + var matching_type = o_a.GetMatchingOperator (); + if (matching_type == Operator.OpType.TOP) { + continue; + } + + bool pair_found = false; + for (int ii = 0; ii < members.Count; ++ii) { + var o_b = members[ii] as Operator; + if (o_b == null || o_b.OperatorType != matching_type) + continue; + + if (!TypeSpecComparer.IsEqual (o_a.ReturnType, o_b.ReturnType)) + continue; + + if (!TypeSpecComparer.Equals (o_a.ParameterTypes, o_b.ParameterTypes)) + continue; + + found_matched.Add (matching_type); + pair_found = true; + break; + } + + if (!pair_found) { + Report.Error (216, o_a.Location, + "The operator `{0}' requires a matching operator `{1}' to also be defined", + o_a.GetSignatureForError (), Operator.GetName (matching_type)); + } + } + + if (has_equality_or_inequality) { + if (!HasEquals) + Report.Warning (660, 2, Location, "`{0}' defines operator == or operator != but does not override Object.Equals(object o)", + GetSignatureForError ()); + + if (!HasGetHashCode) + Report.Warning (661, 2, Location, "`{0}' defines operator == or operator != but does not override Object.GetHashCode()", + GetSignatureForError ()); + } + } + + public override void CreateMetadataName (StringBuilder sb) + { + if (Parent.MemberName != null) { + Parent.CreateMetadataName (sb); + + if (sb.Length != 0) { + sb.Append ("."); + } + } + + sb.Append (MemberName.Basename); + } + + bool CreateTypeBuilder () + { + // + // Sets .size to 1 for structs with no instance fields + // + int type_size = Kind == MemberKind.Struct && first_nonstatic_field == null && !(this is StateMachine) ? 1 : 0; + + var parent_def = Parent as TypeDefinition; + if (parent_def == null) { + var sb = new StringBuilder (); + CreateMetadataName (sb); + TypeBuilder = Module.CreateBuilder (sb.ToString (), TypeAttr, type_size); + } else { + TypeBuilder = parent_def.TypeBuilder.DefineNestedType (Basename, TypeAttr, null, type_size); + } + + if (DeclaringAssembly.Importer != null) + DeclaringAssembly.Importer.AddCompiledType (TypeBuilder, spec); + + spec.SetMetaInfo (TypeBuilder); + spec.MemberCache = new MemberCache (this); + + TypeParameters parentAllTypeParameters = null; + if (parent_def != null) { + spec.DeclaringType = Parent.CurrentType; + parent_def.MemberCache.AddMember (spec); + parentAllTypeParameters = parent_def.all_type_parameters; + } + + if (MemberName.TypeParameters != null || parentAllTypeParameters != null) { + var tparam_names = CreateTypeParameters (parentAllTypeParameters); + + all_tp_builders = TypeBuilder.DefineGenericParameters (tparam_names); + + if (CurrentTypeParameters != null) { + CurrentTypeParameters.Create (spec, CurrentTypeParametersStartIndex, this); + CurrentTypeParameters.Define (all_tp_builders); + } + } + + return true; + } + + string[] CreateTypeParameters (TypeParameters parentAllTypeParameters) + { + string[] names; + int parent_offset = 0; + if (parentAllTypeParameters != null) { + if (CurrentTypeParameters == null) { + all_type_parameters = parentAllTypeParameters; + return parentAllTypeParameters.GetAllNames (); + } + + names = new string[parentAllTypeParameters.Count + CurrentTypeParameters.Count]; + all_type_parameters = new TypeParameters (names.Length); + all_type_parameters.Add (parentAllTypeParameters); + + parent_offset = all_type_parameters.Count; + for (int i = 0; i < parent_offset; ++i) + names[i] = all_type_parameters[i].MemberName.Name; + + } else { + names = new string[CurrentTypeParameters.Count]; + } + + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + if (all_type_parameters != null) + all_type_parameters.Add (MemberName.TypeParameters[i]); + + var name = CurrentTypeParameters[i].MemberName.Name; + names[parent_offset + i] = name; + for (int ii = 0; ii < parent_offset + i; ++ii) { + if (names[ii] != name) + continue; + + var tp = CurrentTypeParameters[i]; + var conflict = all_type_parameters[ii]; + + tp.WarningParentNameConflict (conflict); + } + } + + if (all_type_parameters == null) + all_type_parameters = CurrentTypeParameters; + + return names; + } + + + public SourceMethodBuilder CreateMethodSymbolEntry () + { + if (Module.DeclaringAssembly.SymbolWriter == null || (ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0) + return null; + + var source_file = GetCompilationSourceFile (); + if (source_file == null) + return null; + + return new SourceMethodBuilder (source_file.SymbolUnitEntry); + } + + // + // Creates a proxy base method call inside this container for hoisted base member calls + // + public MethodSpec CreateHoistedBaseCallProxy (ResolveContext rc, MethodSpec method) + { + Method proxy_method; + + // + // One proxy per base method is enough + // + if (hoisted_base_call_proxies == null) { + hoisted_base_call_proxies = new Dictionary (); + proxy_method = null; + } else { + hoisted_base_call_proxies.TryGetValue (method, out proxy_method); + } + + if (proxy_method == null) { + string name = CompilerGeneratedContainer.MakeName (method.Name, null, "BaseCallProxy", hoisted_base_call_proxies.Count); + + MemberName member_name; + TypeArguments targs = null; + TypeSpec return_type = method.ReturnType; + var local_param_types = method.Parameters.Types; + + if (method.IsGeneric) { + // + // Copy all base generic method type parameters info + // + var hoisted_tparams = method.GenericDefinition.TypeParameters; + var tparams = new TypeParameters (); + + targs = new TypeArguments (); + targs.Arguments = new TypeSpec[hoisted_tparams.Length]; + for (int i = 0; i < hoisted_tparams.Length; ++i) { + var tp = hoisted_tparams[i]; + var local_tp = new TypeParameter (tp, null, new MemberName (tp.Name, Location), null); + tparams.Add (local_tp); + + targs.Add (new SimpleName (tp.Name, Location)); + targs.Arguments[i] = local_tp.Type; + } + + member_name = new MemberName (name, tparams, Location); + + // + // Mutate any method type parameters from original + // to newly created hoisted version + // + var mutator = new TypeParameterMutator (hoisted_tparams, tparams); + return_type = mutator.Mutate (return_type); + local_param_types = mutator.Mutate (local_param_types); + } else { + member_name = new MemberName (name); + } + + var base_parameters = new Parameter[method.Parameters.Count]; + for (int i = 0; i < base_parameters.Length; ++i) { + var base_param = method.Parameters.FixedParameters[i]; + base_parameters[i] = new Parameter (new TypeExpression (local_param_types [i], Location), + base_param.Name, base_param.ModFlags, null, Location); + base_parameters[i].Resolve (this, i); + } + + var cloned_params = ParametersCompiled.CreateFullyResolved (base_parameters, method.Parameters.Types); + if (method.Parameters.HasArglist) { + cloned_params.FixedParameters[0] = new Parameter (null, "__arglist", Parameter.Modifier.NONE, null, Location); + cloned_params.Types[0] = Module.PredefinedTypes.RuntimeArgumentHandle.Resolve (); + } + + // Compiler generated proxy + proxy_method = new Method (this, new TypeExpression (return_type, Location), + Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN, + member_name, cloned_params, null); + + var block = new ToplevelBlock (Compiler, proxy_method.ParameterInfo, Location) { + IsCompilerGenerated = true + }; + + var mg = MethodGroupExpr.CreatePredefined (method, method.DeclaringType, Location); + mg.InstanceExpression = new BaseThis (method.DeclaringType, Location); + if (targs != null) + mg.SetTypeArguments (rc, targs); + + // Get all the method parameters and pass them as arguments + var real_base_call = new Invocation (mg, block.GetAllParametersArguments ()); + Statement statement; + if (method.ReturnType.Kind == MemberKind.Void) + statement = new StatementExpression (real_base_call); + else + statement = new Return (real_base_call, Location); + + block.AddStatement (statement); + proxy_method.Block = block; + + members.Add (proxy_method); + proxy_method.Define (); + proxy_method.PrepareEmit (); + + hoisted_base_call_proxies.Add (method, proxy_method); + } + + return proxy_method.Spec; + } + + protected bool DefineBaseTypes () + { + if (IsPartialPart && Kind == MemberKind.Class) + return true; + + return DoDefineBaseType (); + } + + bool DoDefineBaseType () + { + iface_exprs = ResolveBaseTypes (out base_type_expr); + bool set_base_type; + + if (IsPartialPart) { + set_base_type = false; + + if (base_type_expr != null) { + if (PartialContainer.base_type_expr != null && PartialContainer.base_type != base_type) { + Report.SymbolRelatedToPreviousError (base_type_expr.Location, ""); + Report.Error (263, Location, + "Partial declarations of `{0}' must not specify different base classes", + GetSignatureForError ()); + } else { + PartialContainer.base_type_expr = base_type_expr; + PartialContainer.base_type = base_type; + set_base_type = true; + } + } + + if (iface_exprs != null) { + if (PartialContainer.iface_exprs == null) + PartialContainer.iface_exprs = iface_exprs; + else { + var ifaces = new List (PartialContainer.iface_exprs); + foreach (var iface_partial in iface_exprs) { + if (ifaces.Contains (iface_partial)) + continue; + + ifaces.Add (iface_partial); + } + + PartialContainer.iface_exprs = ifaces.ToArray (); + } + } + + PartialContainer.members.AddRange (members); + if (containers != null) { + if (PartialContainer.containers == null) + PartialContainer.containers = new List (); + + PartialContainer.containers.AddRange (containers); + } + + if (PrimaryConstructorParameters != null) { + if (PartialContainer.PrimaryConstructorParameters != null) { + Report.Error (8036, Location, "Only one part of a partial type can declare primary constructor parameters"); + } else { + PartialContainer.PrimaryConstructorParameters = PrimaryConstructorParameters; + } + } + + members_defined = members_defined_ok = true; + caching_flags |= Flags.CloseTypeCreated; + } else { + set_base_type = true; + } + + var cycle = CheckRecursiveDefinition (this); + if (cycle != null) { + Report.SymbolRelatedToPreviousError (cycle); + if (this is Interface) { + Report.Error (529, Location, + "Inherited interface `{0}' causes a cycle in the interface hierarchy of `{1}'", + GetSignatureForError (), cycle.GetSignatureForError ()); + + iface_exprs = null; + PartialContainer.iface_exprs = null; + } else { + Report.Error (146, Location, + "Circular base class dependency involving `{0}' and `{1}'", + GetSignatureForError (), cycle.GetSignatureForError ()); + + base_type = null; + PartialContainer.base_type = null; + } + } + + if (iface_exprs != null) { + if (!PrimaryConstructorBaseArgumentsStart.IsNull) { + Report.Error (8049, PrimaryConstructorBaseArgumentsStart, "Implemented interfaces cannot have arguments"); + } + + foreach (var iface_type in iface_exprs) { + // Prevents a crash, the interface might not have been resolved: 442144 + if (iface_type == null) + continue; + + if (!spec.AddInterfaceDefined (iface_type)) + continue; + + TypeBuilder.AddInterfaceImplementation (iface_type.GetMetaInfo ()); + } + } + + if (Kind == MemberKind.Interface) { + spec.BaseType = Compiler.BuiltinTypes.Object; + return true; + } + + if (set_base_type) { + SetBaseType (); + } + + // + // Base type of partial container has to be resolved before we + // resolve any nested types of the container. We need to know + // partial parts because the base type can be specified in file + // defined after current container + // + if (class_partial_parts != null) { + foreach (var pp in class_partial_parts) { + if (pp.PrimaryConstructorBaseArguments != null) + PrimaryConstructorBaseArguments = pp.PrimaryConstructorBaseArguments; + + pp.DoDefineBaseType (); + } + + } + + return true; + } + + void SetBaseType () + { + if (base_type == null) { + TypeBuilder.SetParent (null); + return; + } + + if (spec.BaseType == base_type) + return; + + spec.BaseType = base_type; + + if (IsPartialPart) + spec.UpdateInflatedInstancesBaseType (); + + // Set base type after type creation + TypeBuilder.SetParent (base_type.GetMetaInfo ()); + } + + public override void ExpandBaseInterfaces () + { + if (!IsPartialPart) + DoExpandBaseInterfaces (); + + base.ExpandBaseInterfaces (); + } + + public void DoExpandBaseInterfaces () + { + if ((caching_flags & Flags.InterfacesExpanded) != 0) + return; + + caching_flags |= Flags.InterfacesExpanded; + + // + // Expand base interfaces. It cannot be done earlier because all partial + // interface parts need to be defined before the type they are used from + // + if (iface_exprs != null) { + foreach (var iface in iface_exprs) { + if (iface == null) + continue; + + var td = iface.MemberDefinition as TypeDefinition; + if (td != null) + td.DoExpandBaseInterfaces (); + + if (iface.Interfaces == null) + continue; + + foreach (var biface in iface.Interfaces) { + if (spec.AddInterfaceDefined (biface)) { + TypeBuilder.AddInterfaceImplementation (biface.GetMetaInfo ()); + } + } + } + } + + // + // Include all base type interfaces too, see ImportTypeBase for details + // + if (base_type != null) { + var td = base_type.MemberDefinition as TypeDefinition; + if (td != null) + td.DoExpandBaseInterfaces (); + + // + // Simply use base interfaces only, they are all expanded which makes + // it easy to handle generic type argument propagation with single + // inflator only. + // + // interface IA : IB + // interface IB : IC + // interface IC + // + if (base_type.Interfaces != null) { + foreach (var iface in base_type.Interfaces) { + spec.AddInterfaceDefined (iface); + } + } + } + } + + public override void PrepareEmit () + { + if ((caching_flags & Flags.CloseTypeCreated) != 0) + return; + + foreach (var member in members) { + var pbm = member as PropertyBasedMember; + if (pbm != null) + pbm.PrepareEmit (); + + var pm = member as IParametersMember; + if (pm != null) { + var mc = member as MethodOrOperator; + if (mc != null) { + mc.PrepareEmit (); + } + + var p = pm.Parameters; + if (p.IsEmpty) + continue; + + ((ParametersCompiled) p).ResolveDefaultValues (member); + continue; + } + + var c = member as Const; + if (c != null) + c.DefineValue (); + } + + base.PrepareEmit (); + } + + // + // Defines the type in the appropriate ModuleBuilder or TypeBuilder. + // + public override bool CreateContainer () + { + if (TypeBuilder != null) + return !error; + + if (error) + return false; + + if (IsPartialPart) { + spec = PartialContainer.spec; + TypeBuilder = PartialContainer.TypeBuilder; + all_tp_builders = PartialContainer.all_tp_builders; + all_type_parameters = PartialContainer.all_type_parameters; + } else { + if (!CreateTypeBuilder ()) { + error = true; + return false; + } + } + + return base.CreateContainer (); + } + + protected override void DoDefineContainer () + { + DefineBaseTypes (); + + DoResolveTypeParameters (); + } + + // + // Replaces normal spec with predefined one when compiling corlib + // and this type container defines predefined type + // + public void SetPredefinedSpec (BuiltinTypeSpec spec) + { + // When compiling build-in types we start with two + // version of same type. One is of BuiltinTypeSpec and + // second one is ordinary TypeSpec. The unification + // happens at later stage when we know which type + // really matches the builtin type signature. However + // that means TypeSpec create during CreateType of this + // type has to be replaced with builtin one + // + spec.SetMetaInfo (TypeBuilder); + spec.MemberCache = this.spec.MemberCache; + spec.DeclaringType = this.spec.DeclaringType; + + this.spec = spec; + current_type = null; + } + + public override void RemoveContainer (TypeContainer cont) + { + base.RemoveContainer (cont); + Members.Remove (cont); + Cache.Remove (cont.Basename); + } + + protected virtual bool DoResolveTypeParameters () + { + var tparams = MemberName.TypeParameters; + if (tparams == null) + return true; + + var base_context = new BaseContext (this); + for (int i = 0; i < tparams.Count; ++i) { + var tp = tparams[i]; + + if (!tp.ResolveConstraints (base_context)) { + error = true; + return false; + } + + if (IsPartialPart) { + var pc_tp = PartialContainer.CurrentTypeParameters [i]; + + tp.Create (spec, this); + tp.Define (pc_tp); + + if (tp.OptAttributes != null) { + if (pc_tp.OptAttributes == null) + pc_tp.OptAttributes = tp.OptAttributes; + else + pc_tp.OptAttributes.Attrs.AddRange (tp.OptAttributes.Attrs); + } + } + } + + if (IsPartialPart) { + PartialContainer.CurrentTypeParameters.UpdateConstraints (this); + } + + return true; + } + + TypeSpec CheckRecursiveDefinition (TypeDefinition tc) + { + if (InTransit != null) + return spec; + + InTransit = tc; + + if (base_type != null) { + var ptc = base_type.MemberDefinition as TypeDefinition; + if (ptc != null && ptc.CheckRecursiveDefinition (this) != null) + return base_type; + } + + if (iface_exprs != null) { + foreach (var iface in iface_exprs) { + // the interface might not have been resolved, prevents a crash, see #442144 + if (iface == null) + continue; + var ptc = iface.MemberDefinition as Interface; + if (ptc != null && ptc.CheckRecursiveDefinition (this) != null) + return iface; + } + } + + if (!IsTopLevel && Parent.PartialContainer.CheckRecursiveDefinition (this) != null) + return spec; + + InTransit = null; + return null; + } + + /// + /// Populates our TypeBuilder with fields and methods + /// + public sealed override bool Define () + { + if (members_defined) + return members_defined_ok; + + members_defined_ok = DoDefineMembers (); + members_defined = true; + + base.Define (); + + return members_defined_ok; + } + + protected virtual bool DoDefineMembers () + { + Debug.Assert (!IsPartialPart); + + if (iface_exprs != null) { + foreach (var iface_type in iface_exprs) { + if (iface_type == null) + continue; + + // Ensure the base is always setup + var compiled_iface = iface_type.MemberDefinition as Interface; + if (compiled_iface != null) + compiled_iface.Define (); + + ObsoleteAttribute oa = iface_type.GetAttributeObsolete (); + if (oa != null && !IsObsolete) + AttributeTester.Report_ObsoleteMessage (oa, iface_type.GetSignatureForError (), Location, Report); + + if (iface_type.Arity > 0) { + // TODO: passing `this' is wrong, should be base type iface instead + VarianceDecl.CheckTypeVariance (iface_type, Variance.Covariant, this); + + if (((InflatedTypeSpec) iface_type).HasDynamicArgument () && !IsCompilerGenerated) { + Report.Error (1966, Location, + "`{0}': cannot implement a dynamic interface `{1}'", + GetSignatureForError (), iface_type.GetSignatureForError ()); + return false; + } + } + + if (iface_type.IsGenericOrParentIsGeneric) { + foreach (var prev_iface in iface_exprs) { + if (prev_iface == iface_type || prev_iface == null) + break; + + if (!TypeSpecComparer.Unify.IsEqual (iface_type, prev_iface)) + continue; + + Report.Error (695, Location, + "`{0}' cannot implement both `{1}' and `{2}' because they may unify for some type parameter substitutions", + GetSignatureForError (), prev_iface.GetSignatureForError (), iface_type.GetSignatureForError ()); + } + } + } + + if (Kind == MemberKind.Interface) { + foreach (var iface in spec.Interfaces) { + MemberCache.AddInterface (iface); + } + } + } + + if (base_type != null) { + // + // Run checks skipped during DefineType (e.g FullNamedExpression::ResolveAsType) + // + if (base_type_expr != null) { + ObsoleteAttribute obsolete_attr = base_type.GetAttributeObsolete (); + if (obsolete_attr != null && !IsObsolete) + AttributeTester.Report_ObsoleteMessage (obsolete_attr, base_type.GetSignatureForError (), base_type_expr.Location, Report); + + if (IsGenericOrParentIsGeneric && base_type.IsAttribute) { + Report.Error (698, base_type_expr.Location, + "A generic type cannot derive from `{0}' because it is an attribute class", + base_type.GetSignatureForError ()); + } + } + + var baseContainer = base_type.MemberDefinition as ClassOrStruct; + if (baseContainer != null) { + baseContainer.Define (); + + // + // It can trigger define of this type (for generic types only) + // + if (HasMembersDefined) + return true; + } + } + + if (Kind == MemberKind.Struct || Kind == MemberKind.Class) { + pending = PendingImplementation.GetPendingImplementations (this); + } + + var count = members.Count; + for (int i = 0; i < count; ++i) { + var mc = members[i] as InterfaceMemberBase; + if (mc == null || !mc.IsExplicitImpl) + continue; + + try { + mc.Define (); + } catch (Exception e) { + throw new InternalErrorException (mc, e); + } + } + + for (int i = 0; i < count; ++i) { + var mc = members[i] as InterfaceMemberBase; + if (mc != null && mc.IsExplicitImpl) + continue; + + if (members[i] is TypeContainer) + continue; + + try { + members[i].Define (); + } catch (Exception e) { + throw new InternalErrorException (members[i], e); + } + } + + if (HasOperators) { + CheckPairedOperators (); + } + + if (requires_delayed_unmanagedtype_check) { + requires_delayed_unmanagedtype_check = false; + foreach (var member in members) { + var f = member as Field; + if (f != null && f.MemberType != null && f.MemberType.IsPointer) + TypeManager.VerifyUnmanaged (Module, f.MemberType, f.Location); + } + } + + ComputeIndexerName(); + + if (HasEquals && !HasGetHashCode) { + Report.Warning (659, 3, Location, + "`{0}' overrides Object.Equals(object) but does not override Object.GetHashCode()", GetSignatureForError ()); + } + + if (Kind == MemberKind.Interface && iface_exprs != null) { + MemberCache.RemoveHiddenMembers (spec); + } + + return true; + } + + void ComputeIndexerName () + { + var indexers = MemberCache.FindMembers (spec, MemberCache.IndexerNameAlias, true); + if (indexers == null) + return; + + string class_indexer_name = null; + + // + // Check normal indexers for consistent name, explicit interface implementation + // indexers are ignored + // + foreach (var indexer in indexers) { + // + // FindMembers can return unfiltered full hierarchy names + // + if (indexer.DeclaringType != spec) + continue; + + has_normal_indexers = true; + + if (class_indexer_name == null) { + indexer_name = class_indexer_name = indexer.Name; + continue; + } + + if (indexer.Name != class_indexer_name) + Report.Error (668, ((Indexer)indexer.MemberDefinition).Location, + "Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type"); + } + } + + void EmitIndexerName () + { + if (!has_normal_indexers) + return; + + var ctor = Module.PredefinedMembers.DefaultMemberAttributeCtor.Get (); + if (ctor == null) + return; + + var encoder = new AttributeEncoder (); + encoder.Encode (GetAttributeDefaultMember ()); + encoder.EncodeEmptyNamedArguments (); + + TypeBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + + public override void VerifyMembers () + { + // + // Check for internal or private fields that were never assigned + // + if (!IsCompilerGenerated && Compiler.Settings.WarningLevel >= 3 && this == PartialContainer) { + bool is_type_exposed = Kind == MemberKind.Struct || IsExposedFromAssembly (); + foreach (var member in members) { + if (member is Event) { + // + // An event can be assigned from same class only, report + // this warning for all accessibility modes + // + if (!member.IsUsed && !PartialContainer.HasStructLayout) + Report.Warning (67, 3, member.Location, "The event `{0}' is never used", member.GetSignatureForError ()); + + continue; + } + + if ((member.ModFlags & Modifiers.AccessibilityMask) != Modifiers.PRIVATE) { + if (is_type_exposed) + continue; + + member.SetIsUsed (); + } + + var f = member as Field; + if (f == null) + continue; + + if (!member.IsUsed) { + if (!PartialContainer.HasStructLayout) { + if ((member.caching_flags & Flags.IsAssigned) == 0) { + Report.Warning (169, 3, member.Location, "The private field `{0}' is never used", member.GetSignatureForError ()); + } else { + Report.Warning (414, 3, member.Location, "The private field `{0}' is assigned but its value is never used", + member.GetSignatureForError ()); + } + } + + continue; + } + + if ((f.caching_flags & Flags.IsAssigned) != 0) + continue; + + // + // Only report 649 on level 4 + // + if (Compiler.Settings.WarningLevel < 4) + continue; + + // + // Don't be pedantic when type requires specific layout + // + if (f.OptAttributes != null || PartialContainer.HasStructLayout) + continue; + + Constant c = New.Constantify (f.MemberType, f.Location); + string value; + if (c != null) { + value = c.GetValueAsLiteral (); + } else if (TypeSpec.IsReferenceType (f.MemberType)) { + value = "null"; + } else { + value = null; + } + + if (value != null) + value = " `" + value + "'"; + + Report.Warning (649, 4, f.Location, "Field `{0}' is never assigned to, and will always have its default value{1}", + f.GetSignatureForError (), value); + } + } + + base.VerifyMembers (); + } + + public override void Emit () + { + if (OptAttributes != null) + OptAttributes.Emit (); + + if (!IsCompilerGenerated) { + if (!IsTopLevel) { + MemberSpec candidate; + bool overrides = false; + var conflict_symbol = MemberCache.FindBaseMember (this, out candidate, ref overrides); + if (conflict_symbol == null && candidate == null) { + if ((ModFlags & Modifiers.NEW) != 0) + Report.Warning (109, 4, Location, "The member `{0}' does not hide an inherited member. The new keyword is not required", + GetSignatureForError ()); + } else { + if ((ModFlags & Modifiers.NEW) == 0) { + if (candidate == null) + candidate = conflict_symbol; + + Report.SymbolRelatedToPreviousError (candidate); + Report.Warning (108, 2, Location, "`{0}' hides inherited member `{1}'. Use the new keyword if hiding was intended", + GetSignatureForError (), candidate.GetSignatureForError ()); + } + } + } + + // Run constraints check on all possible generic types + if (base_type != null && base_type_expr != null) { + ConstraintChecker.Check (this, base_type, base_type_expr.Location); + } + + if (iface_exprs != null) { + foreach (var iface_type in iface_exprs) { + if (iface_type == null) + continue; + + ConstraintChecker.Check (this, iface_type, Location); // TODO: Location is wrong + } + } + } + + if (all_tp_builders != null) { + int current_starts_index = CurrentTypeParametersStartIndex; + for (int i = 0; i < all_tp_builders.Length; i++) { + if (i < current_starts_index) { + all_type_parameters[i].EmitConstraints (all_tp_builders [i]); + } else { + var tp = CurrentTypeParameters [i - current_starts_index]; + tp.CheckGenericConstraints (!IsObsolete); + tp.Emit (); + } + } + } + + if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated) + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (TypeBuilder); + +#if STATIC + if ((TypeBuilder.Attributes & TypeAttributes.StringFormatMask) == 0 && Module.HasDefaultCharSet) + TypeBuilder.__SetAttributes (TypeBuilder.Attributes | Module.DefaultCharSetType); +#endif + + base.Emit (); + + for (int i = 0; i < members.Count; i++) { + var m = members[i]; + if ((m.caching_flags & Flags.CloseTypeCreated) != 0) + continue; + + m.Emit (); + } + + EmitIndexerName (); + CheckAttributeClsCompliance (); + + if (pending != null) + pending.VerifyPendingMethods (); + } + + + void CheckAttributeClsCompliance () + { + if (!spec.IsAttribute || !IsExposedFromAssembly () || !Compiler.Settings.VerifyClsCompliance || !IsClsComplianceRequired ()) + return; + + foreach (var m in members) { + var c = m as Constructor; + if (c == null) + continue; + + if (c.HasCompliantArgs) + return; + } + + Report.Warning (3015, 1, Location, "`{0}' has no accessible constructors which use only CLS-compliant types", GetSignatureForError ()); + } + + public sealed override void EmitContainer () + { + if ((caching_flags & Flags.CloseTypeCreated) != 0) + return; + + Emit (); + } + + public override void CloseContainer () + { + if ((caching_flags & Flags.CloseTypeCreated) != 0) + return; + + // Close base type container first to avoid TypeLoadException + if (spec.BaseType != null) { + var btype = spec.BaseType.MemberDefinition as TypeContainer; + if (btype != null) { + btype.CloseContainer (); + + if ((caching_flags & Flags.CloseTypeCreated) != 0) + return; + } + } + + try { + caching_flags |= Flags.CloseTypeCreated; + TypeBuilder.CreateType (); + } catch (TypeLoadException) { + // + // This is fine, the code still created the type + // + } catch (Exception e) { + throw new InternalErrorException (this, e); + } + + base.CloseContainer (); + + containers = null; + initialized_fields = null; + initialized_static_fields = null; + type_bases = null; + OptAttributes = null; + } + + // + // Performs the validation on a Method's modifiers (properties have + // the same properties). + // + // TODO: Why is it not done at parse stage, move to Modifiers::Check + // + public bool MethodModifiersValid (MemberCore mc) + { + const Modifiers vao = (Modifiers.VIRTUAL | Modifiers.ABSTRACT | Modifiers.OVERRIDE); + const Modifiers nv = (Modifiers.NEW | Modifiers.VIRTUAL); + bool ok = true; + var flags = mc.ModFlags; + + // + // At most one of static, virtual or override + // + if ((flags & Modifiers.STATIC) != 0){ + if ((flags & vao) != 0){ + Report.Error (112, mc.Location, "A static member `{0}' cannot be marked as override, virtual or abstract", + mc.GetSignatureForError ()); + ok = false; + } + } + + if ((flags & Modifiers.OVERRIDE) != 0 && (flags & nv) != 0){ + Report.Error (113, mc.Location, "A member `{0}' marked as override cannot be marked as new or virtual", + mc.GetSignatureForError ()); + ok = false; + } + + // + // If the declaration includes the abstract modifier, then the + // declaration does not include static, virtual or extern + // + if ((flags & Modifiers.ABSTRACT) != 0){ + if ((flags & Modifiers.EXTERN) != 0){ + Report.Error ( + 180, mc.Location, "`{0}' cannot be both extern and abstract", mc.GetSignatureForError ()); + ok = false; + } + + if ((flags & Modifiers.SEALED) != 0) { + Report.Error (502, mc.Location, "`{0}' cannot be both abstract and sealed", mc.GetSignatureForError ()); + ok = false; + } + + if ((flags & Modifiers.VIRTUAL) != 0){ + Report.Error (503, mc.Location, "The abstract method `{0}' cannot be marked virtual", mc.GetSignatureForError ()); + ok = false; + } + + if ((ModFlags & Modifiers.ABSTRACT) == 0){ + Report.SymbolRelatedToPreviousError (this); + Report.Error (513, mc.Location, "`{0}' is abstract but it is declared in the non-abstract class `{1}'", + mc.GetSignatureForError (), GetSignatureForError ()); + ok = false; + } + } + + if ((flags & Modifiers.PRIVATE) != 0){ + if ((flags & vao) != 0){ + Report.Error (621, mc.Location, "`{0}': virtual or abstract members cannot be private", mc.GetSignatureForError ()); + ok = false; + } + } + + if ((flags & Modifiers.SEALED) != 0){ + if ((flags & Modifiers.OVERRIDE) == 0){ + Report.Error (238, mc.Location, "`{0}' cannot be sealed because it is not an override", mc.GetSignatureForError ()); + ok = false; + } + } + + return ok; + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + // Check all container names for user classes + if (Kind != MemberKind.Delegate) + MemberCache.VerifyClsCompliance (Definition, Report); + + if (BaseType != null && !BaseType.IsCLSCompliant ()) { + Report.Warning (3009, 1, Location, "`{0}': base type `{1}' is not CLS-compliant", + GetSignatureForError (), BaseType.GetSignatureForError ()); + } + return true; + } + + /// + /// Performs checks for an explicit interface implementation. First it + /// checks whether the `interface_type' is a base inteface implementation. + /// Then it checks whether `name' exists in the interface type. + /// + public bool VerifyImplements (InterfaceMemberBase mb) + { + var ifaces = PartialContainer.Interfaces; + if (ifaces != null) { + foreach (TypeSpec t in ifaces){ + if (t == mb.InterfaceType) + return true; + + var expanded_base = t.Interfaces; + if (expanded_base == null) + continue; + + foreach (var bt in expanded_base) { + if (bt == mb.InterfaceType) + return true; + } + } + } + + Report.SymbolRelatedToPreviousError (mb.InterfaceType); + Report.Error (540, mb.Location, "`{0}': containing type does not implement interface `{1}'", + mb.GetSignatureForError (), mb.InterfaceType.GetSignatureForError ()); + return false; + } + + // + // Used for visiblity checks to tests whether this definition shares + // base type baseType, it does member-definition search + // + public bool IsBaseTypeDefinition (TypeSpec baseType) + { + // RootContext check + if (TypeBuilder == null) + return false; + + var type = spec; + do { + if (type.MemberDefinition == baseType.MemberDefinition) + return true; + + type = type.BaseType; + } while (type != null); + + return false; + } + + public override bool IsClsComplianceRequired () + { + if (IsPartialPart) + return PartialContainer.IsClsComplianceRequired (); + + return base.IsClsComplianceRequired (); + } + + bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) + { + return Module.DeclaringAssembly == assembly; + } + + public virtual bool IsUnmanagedType () + { + return false; + } + + public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) + { + throw new NotSupportedException ("Not supported for compiled definition " + GetSignatureForError ()); + } + + // + // Public function used to locate types. + // + // Returns: Type or null if they type can not be found. + // + public override FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + FullNamedExpression e; + if (arity == 0 && Cache.TryGetValue (name, out e) && mode != LookupMode.IgnoreAccessibility) + return e; + + e = null; + + if (arity == 0) { + var tp = CurrentTypeParameters; + if (tp != null) { + TypeParameter tparam = tp.Find (name); + if (tparam != null) + e = new TypeParameterExpr (tparam, Location.Null); + } + } + + if (e == null) { + TypeSpec t = LookupNestedTypeInHierarchy (name, arity); + + if (t != null && (t.IsAccessible (this) || mode == LookupMode.IgnoreAccessibility)) + e = new TypeExpression (t, Location.Null); + else { + var errors = Compiler.Report.Errors; + e = Parent.LookupNamespaceOrType (name, arity, mode, loc); + + // TODO: LookupNamespaceOrType does more than just lookup. The result + // cannot be cached or the error reporting won't happen + if (errors != Compiler.Report.Errors) + return e; + } + } + + // TODO MemberCache: How to cache arity stuff ? + if (arity == 0 && mode == LookupMode.Normal) + Cache[name] = e; + + return e; + } + + TypeSpec LookupNestedTypeInHierarchy (string name, int arity) + { + // Has any nested type + // Does not work, because base type can have + //if (PartialContainer.Types == null) + // return null; + + var container = PartialContainer.CurrentType; + return MemberCache.FindNestedType (container, name, arity); + } + + public void Mark_HasEquals () + { + cached_method |= CachedMethods.Equals; + } + + public void Mark_HasGetHashCode () + { + cached_method |= CachedMethods.GetHashCode; + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (IsPartialPart) + return; + + foreach (var m in members) { + m.WriteDebugSymbol (file); + } + } + + /// + /// Method container contains Equals method + /// + public bool HasEquals { + get { + return (cached_method & CachedMethods.Equals) != 0; + } + } + + /// + /// Method container contains GetHashCode method + /// + public bool HasGetHashCode { + get { + return (cached_method & CachedMethods.GetHashCode) != 0; + } + } + + public bool HasStaticFieldInitializer { + get { + return (cached_method & CachedMethods.HasStaticFieldInitializer) != 0; + } + set { + if (value) + cached_method |= CachedMethods.HasStaticFieldInitializer; + else + cached_method &= ~CachedMethods.HasStaticFieldInitializer; + } + } + + public override string DocCommentHeader { + get { return "T:"; } + } + } + + public abstract class ClassOrStruct : TypeDefinition + { + public const TypeAttributes StaticClassAttribute = TypeAttributes.Abstract | TypeAttributes.Sealed; + + SecurityType declarative_security; + protected Constructor generated_primary_constructor; + + protected ClassOrStruct (TypeContainer parent, MemberName name, Attributes attrs, MemberKind kind) + : base (parent, name, attrs, kind) + { + } + + public ToplevelBlock PrimaryConstructorBlock { get; set; } + + protected override TypeAttributes TypeAttr { + get { + TypeAttributes ta = base.TypeAttr; + if (!has_static_constructor) + ta |= TypeAttributes.BeforeFieldInit; + + if (Kind == MemberKind.Class) { + ta |= TypeAttributes.AutoLayout | TypeAttributes.Class; + if (IsStatic) + ta |= StaticClassAttribute; + } else { + ta |= TypeAttributes.SequentialLayout; + } + + return ta; + } + } + + public override void AddNameToContainer (MemberCore symbol, string name) + { + if (!(symbol is Constructor) && symbol.MemberName.Name == MemberName.Name) { + if (symbol is TypeParameter) { + Report.Error (694, symbol.Location, + "Type parameter `{0}' has same name as containing type, or method", + symbol.GetSignatureForError ()); + return; + } + + InterfaceMemberBase imb = symbol as InterfaceMemberBase; + if (imb == null || !imb.IsExplicitImpl) { + Report.SymbolRelatedToPreviousError (this); + Report.Error (542, symbol.Location, "`{0}': member names cannot be the same as their enclosing type", + symbol.GetSignatureForError ()); + return; + } + } + + base.AddNameToContainer (symbol, name); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (a.Type == pa.StructLayout) { + PartialContainer.HasStructLayout = true; + if (a.IsExplicitLayoutKind ()) + PartialContainer.HasExplicitLayout = true; + } + + if (a.Type == pa.Dynamic) { + a.Error_MisusedDynamicAttribute (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + /// + /// Defines the default constructors + /// + protected virtual Constructor DefineDefaultConstructor (bool is_static) + { + // The default instance constructor is public + // If the class is abstract, the default constructor is protected + // The default static constructor is private + + Modifiers mods; + ParametersCompiled parameters = null; + if (is_static) { + mods = Modifiers.STATIC | Modifiers.PRIVATE; + parameters = ParametersCompiled.EmptyReadOnlyParameters; + } else { + mods = ((ModFlags & Modifiers.ABSTRACT) != 0) ? Modifiers.PROTECTED : Modifiers.PUBLIC; + parameters = PrimaryConstructorParameters ?? ParametersCompiled.EmptyReadOnlyParameters; + } + + var c = new Constructor (this, MemberName.Name, mods, null, parameters, Location); + if (Kind == MemberKind.Class) + c.Initializer = new GeneratedBaseInitializer (Location, PrimaryConstructorBaseArguments); + + if (PrimaryConstructorParameters != null && !is_static) + c.IsPrimaryConstructor = true; + + AddConstructor (c, true); + if (PrimaryConstructorBlock == null) { + c.Block = new ToplevelBlock (Compiler, parameters, Location) { + IsCompilerGenerated = true + }; + } else { + c.Block = PrimaryConstructorBlock; + } + + return c; + } + + protected override bool DoDefineMembers () + { + CheckProtectedModifier (); + + if (PrimaryConstructorParameters != null) { + foreach (Parameter p in PrimaryConstructorParameters.FixedParameters) { + if (p.Name == MemberName.Name) { + Report.Error (8039, p.Location, "Primary constructor of type `{0}' has parameter of same name as containing type", + GetSignatureForError ()); + } + + if (CurrentTypeParameters != null) { + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp = CurrentTypeParameters [i]; + if (p.Name == tp.Name) { + Report.Error (8038, p.Location, "Primary constructor of type `{0}' has parameter of same name as type parameter `{1}'", + GetSignatureForError (), p.GetSignatureForError ()); + } + } + } + } + } + + base.DoDefineMembers (); + + return true; + } + + public override void Emit () + { + if (!has_static_constructor && HasStaticFieldInitializer) { + var c = DefineDefaultConstructor (true); + c.Define (); + } + + base.Emit (); + + if (declarative_security != null) { + foreach (var de in declarative_security) { +#if STATIC + TypeBuilder.__AddDeclarativeSecurity (de); +#else + TypeBuilder.AddDeclarativeSecurity (de.Key, de.Value); +#endif + } + } + } + } + + + public sealed class Class : ClassOrStruct + { + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.ABSTRACT | + Modifiers.SEALED | + Modifiers.STATIC | + Modifiers.UNSAFE; + + public Class (TypeContainer parent, MemberName name, Modifiers mod, Attributes attrs) + : base (parent, name, attrs, MemberKind.Class) + { + var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE; + this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod, accmods, Location, Report); + spec = new TypeSpec (Kind, null, this, null, ModFlags); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void SetBaseTypes (List baseTypes) + { + var pmn = MemberName; + if (pmn.Name == "Object" && !pmn.IsGeneric && Parent.MemberName.Name == "System" && Parent.MemberName.Left == null) + Report.Error (537, Location, + "The class System.Object cannot have a base class or implement an interface."); + + base.SetBaseTypes (baseTypes); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.AttributeUsage) { + if (!BaseType.IsAttribute && spec.BuiltinType != BuiltinTypeSpec.Type.Attribute) { + Report.Error (641, a.Location, "Attribute `{0}' is only valid on classes derived from System.Attribute", a.GetSignatureForError ()); + } + } + + if (a.Type == pa.Conditional && !BaseType.IsAttribute) { + Report.Error (1689, a.Location, "Attribute `System.Diagnostics.ConditionalAttribute' is only valid on methods or attribute classes"); + return; + } + + if (a.Type == pa.ComImport && !attributes.Contains (pa.Guid)) { + a.Error_MissingGuidAttribute (); + return; + } + + if (a.Type == pa.Extension) { + a.Error_MisusedExtensionAttribute (); + return; + } + + if (a.Type.IsConditionallyExcluded (this)) + return; + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Class; + } + } + + protected override bool DoDefineMembers () + { + if ((ModFlags & Modifiers.ABSTRACT) == Modifiers.ABSTRACT && (ModFlags & (Modifiers.SEALED | Modifiers.STATIC)) != 0) { + Report.Error (418, Location, "`{0}': an abstract class cannot be sealed or static", GetSignatureForError ()); + } + + if ((ModFlags & (Modifiers.SEALED | Modifiers.STATIC)) == (Modifiers.SEALED | Modifiers.STATIC)) { + Report.Error (441, Location, "`{0}': a class cannot be both static and sealed", GetSignatureForError ()); + } + + if (IsStatic) { + if (PrimaryConstructorParameters != null) { + Report.Error (-800, Location, "`{0}': Static classes cannot have primary constructor", GetSignatureForError ()); + PrimaryConstructorParameters = null; + } + + foreach (var m in Members) { + if (m is Operator) { + Report.Error (715, m.Location, "`{0}': Static classes cannot contain user-defined operators", m.GetSignatureForError ()); + continue; + } + + if (m is Destructor) { + Report.Error (711, m.Location, "`{0}': Static classes cannot contain destructor", GetSignatureForError ()); + continue; + } + + if (m is Indexer) { + Report.Error (720, m.Location, "`{0}': cannot declare indexers in a static class", m.GetSignatureForError ()); + continue; + } + + if ((m.ModFlags & Modifiers.STATIC) != 0 || m is TypeContainer) + continue; + + if (m is Constructor) { + Report.Error (710, m.Location, "`{0}': Static classes cannot have instance constructors", GetSignatureForError ()); + continue; + } + + Report.Error (708, m.Location, "`{0}': cannot declare instance members in a static class", m.GetSignatureForError ()); + } + } else { + if (!PartialContainer.HasInstanceConstructor || PrimaryConstructorParameters != null) + generated_primary_constructor = DefineDefaultConstructor (false); + } + + return base.DoDefineMembers (); + } + + public override void Emit () + { + base.Emit (); + + if ((ModFlags & Modifiers.METHOD_EXTENSION) != 0) + Module.PredefinedAttributes.Extension.EmitAttribute (TypeBuilder); + + if (base_type != null && base_type.HasDynamicElement) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (TypeBuilder, base_type, Location); + } + } + + public override void GetCompletionStartingWith (string prefix, List results) + { + base.GetCompletionStartingWith (prefix, results); + + var bt = base_type; + while (bt != null) { + results.AddRange (MemberCache.GetCompletitionMembers (this, bt, prefix).Where (l => l.IsStatic).Select (l => l.Name)); + bt = bt.BaseType; + } + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + var ifaces = base.ResolveBaseTypes (out base_class); + + if (base_class == null) { + if (spec.BuiltinType != BuiltinTypeSpec.Type.Object) + base_type = Compiler.BuiltinTypes.Object; + } else { + if (base_type.IsGenericParameter){ + Report.Error (689, base_class.Location, "`{0}': Cannot derive from type parameter `{1}'", + GetSignatureForError (), base_type.GetSignatureForError ()); + } else if (base_type.IsStatic) { + Report.SymbolRelatedToPreviousError (base_type); + Report.Error (709, Location, "`{0}': Cannot derive from static class `{1}'", + GetSignatureForError (), base_type.GetSignatureForError ()); + } else if (base_type.IsSealed) { + Report.SymbolRelatedToPreviousError (base_type); + Report.Error (509, Location, "`{0}': cannot derive from sealed type `{1}'", + GetSignatureForError (), base_type.GetSignatureForError ()); + } else if (PartialContainer.IsStatic && base_type.BuiltinType != BuiltinTypeSpec.Type.Object) { + Report.Error (713, Location, "Static class `{0}' cannot derive from type `{1}'. Static classes must derive from object", + GetSignatureForError (), base_type.GetSignatureForError ()); + } + + switch (base_type.BuiltinType) { + case BuiltinTypeSpec.Type.Enum: + case BuiltinTypeSpec.Type.ValueType: + case BuiltinTypeSpec.Type.MulticastDelegate: + case BuiltinTypeSpec.Type.Delegate: + case BuiltinTypeSpec.Type.Array: + if (!(spec is BuiltinTypeSpec)) { + Report.Error (644, Location, "`{0}' cannot derive from special class `{1}'", + GetSignatureForError (), base_type.GetSignatureForError ()); + + base_type = Compiler.BuiltinTypes.Object; + } + break; + } + + if (!IsAccessibleAs (base_type)) { + Report.SymbolRelatedToPreviousError (base_type); + Report.Error (60, Location, "Inconsistent accessibility: base class `{0}' is less accessible than class `{1}'", + base_type.GetSignatureForError (), GetSignatureForError ()); + } + } + + if (PartialContainer.IsStatic && ifaces != null) { + foreach (var t in ifaces) + Report.SymbolRelatedToPreviousError (t); + Report.Error (714, Location, "Static class `{0}' cannot implement interfaces", GetSignatureForError ()); + } + + return ifaces; + } + + /// Search for at least one defined condition in ConditionalAttribute of attribute class + /// Valid only for attribute classes. + public override string[] ConditionalConditions () + { + if ((caching_flags & (Flags.Excluded_Undetected | Flags.Excluded)) == 0) + return null; + + caching_flags &= ~Flags.Excluded_Undetected; + + if (OptAttributes == null) + return null; + + Attribute[] attrs = OptAttributes.SearchMulti (Module.PredefinedAttributes.Conditional); + if (attrs == null) + return null; + + string[] conditions = new string[attrs.Length]; + for (int i = 0; i < conditions.Length; ++i) + conditions[i] = attrs[i].GetConditionalAttributeValue (); + + caching_flags |= Flags.Excluded; + return conditions; + } + } + + public sealed class Struct : ClassOrStruct + { + bool is_unmanaged, has_unmanaged_check_done; + bool InTransit; + + // + // Modifiers allowed in a struct declaration + // + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Struct (TypeContainer parent, MemberName name, Modifiers mod, Attributes attrs) + : base (parent, name, attrs, MemberKind.Struct) + { + var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE; + this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod, accmods, Location, Report) | Modifiers.SEALED ; + spec = new TypeSpec (Kind, null, this, null, ModFlags); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Struct; + } + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + + // + // When struct constains fixed fixed and struct layout has explicitly + // set CharSet, its value has to be propagated to compiler generated + // fixed types + // + if (a.Type == pa.StructLayout) { + var value = a.GetNamedValue ("CharSet"); + if (value == null) + return; + + for (int i = 0; i < Members.Count; ++i) { + FixedField ff = Members [i] as FixedField; + if (ff == null) + continue; + + ff.CharSet = (CharSet) System.Enum.Parse (typeof (CharSet), value.GetValue ().ToString ()); + } + } + } + + bool CheckStructCycles () + { + if (InTransit) + return false; + + InTransit = true; + foreach (var member in Members) { + var field = member as Field; + if (field == null) + continue; + + TypeSpec ftype = field.Spec.MemberType; + if (!ftype.IsStruct) + continue; + + if (ftype is BuiltinTypeSpec) + continue; + + foreach (var targ in ftype.TypeArguments) { + if (!CheckFieldTypeCycle (targ)) { + Report.Error (523, field.Location, + "Struct member `{0}' of type `{1}' causes a cycle in the struct layout", + field.GetSignatureForError (), ftype.GetSignatureForError ()); + break; + } + } + + // + // Static fields of exactly same type are allowed + // + if (field.IsStatic && ftype == CurrentType) + continue; + + if (!CheckFieldTypeCycle (ftype)) { + Report.Error (523, field.Location, + "Struct member `{0}' of type `{1}' causes a cycle in the struct layout", + field.GetSignatureForError (), ftype.GetSignatureForError ()); + break; + } + } + + InTransit = false; + return true; + } + + static bool CheckFieldTypeCycle (TypeSpec ts) + { + var fts = ts.MemberDefinition as Struct; + if (fts == null) + return true; + + return fts.CheckStructCycles (); + } + + protected override bool DoDefineMembers () + { + if (PrimaryConstructorParameters != null) + generated_primary_constructor = DefineDefaultConstructor (false); + + return base.DoDefineMembers (); + } + + public override void Emit () + { + CheckStructCycles (); + + base.Emit (); + } + + bool HasExplicitConstructor () + { + foreach (var m in Members) { + var c = m as Constructor; + if (c == null) + continue; + + if (!c.ParameterInfo.IsEmpty) + return true; + } + + return false; + } + + public override bool IsUnmanagedType () + { + if (has_unmanaged_check_done) + return is_unmanaged; + + if (requires_delayed_unmanagedtype_check) + return true; + + var parent_def = Parent.PartialContainer; + if (parent_def != null && parent_def.IsGenericOrParentIsGeneric) { + has_unmanaged_check_done = true; + return false; + } + + if (first_nonstatic_field != null) { + requires_delayed_unmanagedtype_check = true; + + foreach (var member in Members) { + var f = member as Field; + if (f == null) + continue; + + if (f.IsStatic) + continue; + + // It can happen when recursive unmanaged types are defined + // struct S { S* s; } + TypeSpec mt = f.MemberType; + if (mt == null) { + return true; + } + + if (mt.IsUnmanaged) + continue; + + has_unmanaged_check_done = true; + return false; + } + + has_unmanaged_check_done = true; + } + + is_unmanaged = true; + return true; + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + var ifaces = base.ResolveBaseTypes (out base_class); + base_type = Compiler.BuiltinTypes.ValueType; + return ifaces; + } + + public override void RegisterFieldForInitialization (MemberCore field, FieldInitializer expression) + { + if ((field.ModFlags & Modifiers.STATIC) == 0 && !HasExplicitConstructor ()) { + Report.Error (8054, field.Location, "`{0}': Structs without explicit constructors cannot contain members with initializers", + field.GetSignatureForError ()); + + return; + } + + base.RegisterFieldForInitialization (field, expression); + } + } + + /// + /// Interfaces + /// + public sealed class Interface : TypeDefinition { + + /// + /// Modifiers allowed in a class declaration + /// + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Interface (TypeContainer parent, MemberName name, Modifiers mod, Attributes attrs) + : base (parent, name, attrs, MemberKind.Interface) + { + var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE; + + this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod, accmods, name.Location, Report); + spec = new TypeSpec (Kind, null, this, null, ModFlags); + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Interface; + } + } + + protected override TypeAttributes TypeAttr { + get { + const TypeAttributes DefaultTypeAttributes = + TypeAttributes.AutoLayout | + TypeAttributes.Abstract | + TypeAttributes.Interface; + + return base.TypeAttr | DefaultTypeAttributes; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.ComImport && !attributes.Contains (pa.Guid)) { + a.Error_MissingGuidAttribute (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if (iface_exprs != null) { + foreach (var iface in iface_exprs) { + if (iface.IsCLSCompliant ()) + continue; + + Report.SymbolRelatedToPreviousError (iface); + Report.Warning (3027, 1, Location, "`{0}' is not CLS-compliant because base interface `{1}' is not CLS-compliant", + GetSignatureForError (), iface.GetSignatureForError ()); + } + } + + return true; + } + } + + public abstract class InterfaceMemberBase : MemberBase + { + // + // Common modifiers allowed in a class declaration + // + protected const Modifiers AllowedModifiersClass = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.VIRTUAL | + Modifiers.SEALED | + Modifiers.OVERRIDE | + Modifiers.ABSTRACT | + Modifiers.UNSAFE | + Modifiers.EXTERN; + + // + // Common modifiers allowed in a struct declaration + // + protected const Modifiers AllowedModifiersStruct = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.OVERRIDE | + Modifiers.UNSAFE | + Modifiers.EXTERN; + + // + // Common modifiers allowed in a interface declaration + // + protected const Modifiers AllowedModifiersInterface = + Modifiers.NEW | + Modifiers.UNSAFE; + + // + // Whether this is an interface member. + // + public bool IsInterface; + + // + // If true, this is an explicit interface implementation + // + public readonly bool IsExplicitImpl; + + protected bool is_external_implementation; + + // + // The interface type we are explicitly implementing + // + public TypeSpec InterfaceType; + + // + // The method we're overriding if this is an override method. + // + protected MethodSpec base_method; + + readonly Modifiers explicit_mod_flags; + public MethodAttributes flags; + + protected InterfaceMemberBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, Attributes attrs) + : base (parent, type, mod, allowed_mod, Modifiers.PRIVATE, name, attrs) + { + IsInterface = parent.Kind == MemberKind.Interface; + IsExplicitImpl = (MemberName.ExplicitInterface != null); + explicit_mod_flags = mod; + } + + public abstract Variance ExpectedMemberTypeVariance { get; } + + protected override bool CheckBase () + { + if (!base.CheckBase ()) + return false; + + if ((caching_flags & Flags.MethodOverloadsExist) != 0) + CheckForDuplications (); + + if (IsExplicitImpl) + return true; + + // For System.Object only + if (Parent.BaseType == null) + return true; + + MemberSpec candidate; + bool overrides = false; + var base_member = FindBaseMember (out candidate, ref overrides); + + if ((ModFlags & Modifiers.OVERRIDE) != 0) { + if (base_member == null) { + if (candidate == null) { + if (this is Method && ((Method)this).ParameterInfo.IsEmpty && MemberName.Name == Destructor.MetadataName && MemberName.Arity == 0) { + Report.Error (249, Location, "Do not override `{0}'. Use destructor syntax instead", + "object.Finalize()"); + } else { + Report.Error (115, Location, "`{0}' is marked as an override but no suitable {1} found to override", + GetSignatureForError (), SimpleName.GetMemberType (this)); + } + } else { + Report.SymbolRelatedToPreviousError (candidate); + if (this is Event) + Report.Error (72, Location, "`{0}': cannot override because `{1}' is not an event", + GetSignatureForError (), TypeManager.GetFullNameSignature (candidate)); + else if (this is PropertyBase) + Report.Error (544, Location, "`{0}': cannot override because `{1}' is not a property", + GetSignatureForError (), TypeManager.GetFullNameSignature (candidate)); + else + Report.Error (505, Location, "`{0}': cannot override because `{1}' is not a method", + GetSignatureForError (), TypeManager.GetFullNameSignature (candidate)); + } + + return false; + } + + // + // Handles ambiguous overrides + // + if (candidate != null) { + Report.SymbolRelatedToPreviousError (candidate); + Report.SymbolRelatedToPreviousError (base_member); + + // Get member definition for error reporting + var m1 = MemberCache.GetMember (base_member.DeclaringType.GetDefinition (), base_member); + var m2 = MemberCache.GetMember (candidate.DeclaringType.GetDefinition (), candidate); + + Report.Error (462, Location, + "`{0}' cannot override inherited members `{1}' and `{2}' because they have the same signature when used in type `{3}'", + GetSignatureForError (), m1.GetSignatureForError (), m2.GetSignatureForError (), Parent.GetSignatureForError ()); + } + + if (!CheckOverrideAgainstBase (base_member)) + return false; + + ObsoleteAttribute oa = base_member.GetAttributeObsolete (); + if (oa != null) { + if (OptAttributes == null || !OptAttributes.Contains (Module.PredefinedAttributes.Obsolete)) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Warning (672, 1, Location, "Member `{0}' overrides obsolete member `{1}'. Add the Obsolete attribute to `{0}'", + GetSignatureForError (), base_member.GetSignatureForError ()); + } + } else { + if (OptAttributes != null && OptAttributes.Contains (Module.PredefinedAttributes.Obsolete)) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Warning (809, 1, Location, "Obsolete member `{0}' overrides non-obsolete member `{1}'", + GetSignatureForError (), base_member.GetSignatureForError ()); + } + } + + base_method = base_member as MethodSpec; + return true; + } + + if (base_member == null && candidate != null && (!(candidate is IParametersMember) || !(this is IParametersMember))) + base_member = candidate; + + if (base_member == null) { + if ((ModFlags & Modifiers.NEW) != 0) { + if (base_member == null) { + Report.Warning (109, 4, Location, "The member `{0}' does not hide an inherited member. The new keyword is not required", + GetSignatureForError ()); + } + } + } else { + if ((ModFlags & Modifiers.NEW) == 0) { + ModFlags |= Modifiers.NEW; + if (!IsCompilerGenerated) { + Report.SymbolRelatedToPreviousError (base_member); + if (!IsInterface && (base_member.Modifiers & (Modifiers.ABSTRACT | Modifiers.VIRTUAL | Modifiers.OVERRIDE)) != 0) { + Report.Warning (114, 2, Location, "`{0}' hides inherited member `{1}'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword", + GetSignatureForError (), base_member.GetSignatureForError ()); + } else { + Report.Warning (108, 2, Location, "`{0}' hides inherited member `{1}'. Use the new keyword if hiding was intended", + GetSignatureForError (), base_member.GetSignatureForError ()); + } + } + } + + if (!IsInterface && base_member.IsAbstract && !overrides && !IsStatic) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Error (533, Location, "`{0}' hides inherited abstract member `{1}'", + GetSignatureForError (), base_member.GetSignatureForError ()); + } + } + + return true; + } + + protected virtual bool CheckForDuplications () + { + return Parent.MemberCache.CheckExistingMembersOverloads (this, ParametersCompiled.EmptyReadOnlyParameters); + } + + // + // Performs various checks on the MethodInfo `mb' regarding the modifier flags + // that have been defined. + // + protected virtual bool CheckOverrideAgainstBase (MemberSpec base_member) + { + bool ok = true; + + if ((base_member.Modifiers & (Modifiers.ABSTRACT | Modifiers.VIRTUAL | Modifiers.OVERRIDE)) == 0) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Error (506, Location, + "`{0}': cannot override inherited member `{1}' because it is not marked virtual, abstract or override", + GetSignatureForError (), TypeManager.CSharpSignature (base_member)); + ok = false; + } + + // Now we check that the overriden method is not final + if ((base_member.Modifiers & Modifiers.SEALED) != 0) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Error (239, Location, "`{0}': cannot override inherited member `{1}' because it is sealed", + GetSignatureForError (), TypeManager.CSharpSignature (base_member)); + ok = false; + } + + var base_member_type = ((IInterfaceMemberSpec) base_member).MemberType; + if (!TypeSpecComparer.Override.IsEqual (MemberType, base_member_type)) { + Report.SymbolRelatedToPreviousError (base_member); + if (this is PropertyBasedMember) { + Report.Error (1715, Location, "`{0}': type must be `{1}' to match overridden member `{2}'", + GetSignatureForError (), base_member_type.GetSignatureForError (), base_member.GetSignatureForError ()); + } else { + Report.Error (508, Location, "`{0}': return type must be `{1}' to match overridden member `{2}'", + GetSignatureForError (), base_member_type.GetSignatureForError (), base_member.GetSignatureForError ()); + } + ok = false; + } + + return ok; + } + + protected static bool CheckAccessModifiers (MemberCore this_member, MemberSpec base_member) + { + var thisp = this_member.ModFlags & Modifiers.AccessibilityMask; + var base_classp = base_member.Modifiers & Modifiers.AccessibilityMask; + + if ((base_classp & (Modifiers.PROTECTED | Modifiers.INTERNAL)) == (Modifiers.PROTECTED | Modifiers.INTERNAL)) { + // + // It must be at least "protected" + // + if ((thisp & Modifiers.PROTECTED) == 0) { + return false; + } + + // + // when overriding protected internal, the method can be declared + // protected internal only within the same assembly or assembly + // which has InternalsVisibleTo + // + if ((thisp & Modifiers.INTERNAL) != 0) { + return base_member.DeclaringType.MemberDefinition.IsInternalAsPublic (this_member.Module.DeclaringAssembly); + } + + // + // protected overriding protected internal inside same assembly + // requires internal modifier as well + // + if (base_member.DeclaringType.MemberDefinition.IsInternalAsPublic (this_member.Module.DeclaringAssembly)) { + return false; + } + + return true; + } + + return thisp == base_classp; + } + + public override bool Define () + { + if (IsInterface) { + ModFlags = Modifiers.PUBLIC | Modifiers.ABSTRACT | + Modifiers.VIRTUAL | (ModFlags & (Modifiers.UNSAFE | Modifiers.NEW)); + + flags = MethodAttributes.Public | + MethodAttributes.Abstract | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.Virtual; + } else { + Parent.PartialContainer.MethodModifiersValid (this); + + flags = ModifiersExtensions.MethodAttr (ModFlags); + } + + if (IsExplicitImpl) { + InterfaceType = MemberName.ExplicitInterface.ResolveAsType (Parent); + if (InterfaceType == null) + return false; + + if ((ModFlags & Modifiers.PARTIAL) != 0) { + Report.Error (754, Location, "A partial method `{0}' cannot explicitly implement an interface", + GetSignatureForError ()); + } + + if (!InterfaceType.IsInterface) { + Report.SymbolRelatedToPreviousError (InterfaceType); + Report.Error (538, Location, "The type `{0}' in explicit interface declaration is not an interface", + InterfaceType.GetSignatureForError ()); + } else { + Parent.PartialContainer.VerifyImplements (this); + } + + Modifiers allowed_explicit = Modifiers.AllowedExplicitImplFlags; + if (this is Method) + allowed_explicit |= Modifiers.ASYNC; + + ModifiersExtensions.Check (allowed_explicit, explicit_mod_flags, 0, Location, Report); + } + + return base.Define (); + } + + protected bool DefineParameters (ParametersCompiled parameters) + { + if (!parameters.Resolve (this)) + return false; + + bool error = false; + for (int i = 0; i < parameters.Count; ++i) { + Parameter p = parameters [i]; + + if (p.HasDefaultValue && (IsExplicitImpl || this is Operator || (this is Indexer && parameters.Count == 1))) + p.Warning_UselessOptionalParameter (Report); + + if (p.CheckAccessibility (this)) + continue; + + TypeSpec t = parameters.Types [i]; + Report.SymbolRelatedToPreviousError (t); + if (this is Indexer) + Report.Error (55, Location, + "Inconsistent accessibility: parameter type `{0}' is less accessible than indexer `{1}'", + t.GetSignatureForError (), GetSignatureForError ()); + else if (this is Operator) + Report.Error (57, Location, + "Inconsistent accessibility: parameter type `{0}' is less accessible than operator `{1}'", + t.GetSignatureForError (), GetSignatureForError ()); + else + Report.Error (51, Location, + "Inconsistent accessibility: parameter type `{0}' is less accessible than method `{1}'", + t.GetSignatureForError (), GetSignatureForError ()); + error = true; + } + return !error; + } + + protected override void DoMemberTypeDependentChecks () + { + base.DoMemberTypeDependentChecks (); + + VarianceDecl.CheckTypeVariance (MemberType, ExpectedMemberTypeVariance, this); + } + + public override void Emit() + { + // for extern static method must be specified either DllImport attribute or MethodImplAttribute. + // We are more strict than csc and report this as an error because SRE does not allow emit that + if ((ModFlags & Modifiers.EXTERN) != 0 && !is_external_implementation && (OptAttributes == null || !OptAttributes.HasResolveError ())) { + if (this is Constructor) { + Report.Warning (824, 1, Location, + "Constructor `{0}' is marked `external' but has no external implementation specified", GetSignatureForError ()); + } else { + Report.Warning (626, 1, Location, + "`{0}' is marked as an external but has no DllImport attribute. Consider adding a DllImport attribute to specify the external implementation", + GetSignatureForError ()); + } + } + + base.Emit (); + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + // + // Two members can differ in their explicit interface + // type parameter only + // + InterfaceMemberBase imb = overload as InterfaceMemberBase; + if (imb != null && imb.IsExplicitImpl) { + if (IsExplicitImpl) { + caching_flags |= Flags.MethodOverloadsExist; + } + return true; + } + + return IsExplicitImpl; + } + + protected void Error_CannotChangeAccessModifiers (MemberCore member, MemberSpec base_member) + { + var base_modifiers = base_member.Modifiers; + + // Remove internal modifier from types which are not internally accessible + if ((base_modifiers & Modifiers.AccessibilityMask) == (Modifiers.PROTECTED | Modifiers.INTERNAL) && + !base_member.DeclaringType.MemberDefinition.IsInternalAsPublic (member.Module.DeclaringAssembly)) + base_modifiers = Modifiers.PROTECTED; + + Report.SymbolRelatedToPreviousError (base_member); + Report.Error (507, member.Location, + "`{0}': cannot change access modifiers when overriding `{1}' inherited member `{2}'", + member.GetSignatureForError (), + ModifiersExtensions.AccessibilityName (base_modifiers), + base_member.GetSignatureForError ()); + } + + protected void Error_StaticReturnType () + { + Report.Error (722, Location, + "`{0}': static types cannot be used as return types", + MemberType.GetSignatureForError ()); + } + + /// + /// Gets base method and its return type + /// + protected virtual MemberSpec FindBaseMember (out MemberSpec bestCandidate, ref bool overrides) + { + return MemberCache.FindBaseMember (this, out bestCandidate, ref overrides); + } + + // + // The "short" name of this property / indexer / event. This is the + // name without the explicit interface. + // + public string ShortName { + get { return MemberName.Name; } + } + + // + // Returns full metadata method name + // + public string GetFullName (MemberName name) + { + return GetFullName (name.Name); + } + + public string GetFullName (string name) + { + if (!IsExplicitImpl) + return name; + + // + // When dealing with explicit members a full interface type + // name is added to member name to avoid possible name conflicts + // + // We use CSharpName which gets us full name with benefit of + // replacing predefined names which saves some space and name + // is still unique + // + return InterfaceType.GetSignatureForError () + "." + name; + } + + public override string GetSignatureForDocumentation () + { + if (IsExplicitImpl) + return Parent.GetSignatureForDocumentation () + "." + InterfaceType.GetExplicitNameSignatureForDocumentation () + "#" + ShortName; + + return Parent.GetSignatureForDocumentation () + "." + ShortName; + } + + public override bool IsUsed + { + get { return IsExplicitImpl || base.IsUsed; } + } + + public override void SetConstraints (List constraints_list) + { + if (((ModFlags & Modifiers.OVERRIDE) != 0 || IsExplicitImpl)) { + Report.Error (460, Location, + "`{0}': Cannot specify constraints for overrides and explicit interface implementation methods", + GetSignatureForError ()); + } + + base.SetConstraints (constraints_list); + } + } + + public abstract class MemberBase : MemberCore + { + protected FullNamedExpression type_expr; + protected TypeSpec member_type; + public new TypeDefinition Parent; + + protected MemberBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, Modifiers def_mod, MemberName name, Attributes attrs) + : base (parent, name, attrs) + { + this.Parent = parent; + this.type_expr = type; + + if (name != MemberName.Null) + ModFlags = ModifiersExtensions.Check (allowed_mod, mod, def_mod, Location, Report); + } + + #region Properties + + public TypeSpec MemberType { + get { + return member_type; + } + } + + public FullNamedExpression TypeExpression { + get { + return type_expr; + } + } + + #endregion + + // + // Main member define entry + // + public override bool Define () + { + DoMemberTypeIndependentChecks (); + + // + // Returns false only when type resolution failed + // + if (!ResolveMemberType ()) + return false; + + DoMemberTypeDependentChecks (); + return true; + } + + // + // Any type_name independent checks + // + protected virtual void DoMemberTypeIndependentChecks () + { + if ((Parent.ModFlags & Modifiers.SEALED) != 0 && + (ModFlags & (Modifiers.VIRTUAL | Modifiers.ABSTRACT)) != 0) { + Report.Error (549, Location, "New virtual member `{0}' is declared in a sealed class `{1}'", + GetSignatureForError (), Parent.GetSignatureForError ()); + } + } + + // + // Any type_name dependent checks + // + protected virtual void DoMemberTypeDependentChecks () + { + // verify accessibility + if (!IsAccessibleAs (MemberType)) { + Report.SymbolRelatedToPreviousError (MemberType); + if (this is Property) + Report.Error (53, Location, + "Inconsistent accessibility: property type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than property `" + GetSignatureForError () + "'"); + else if (this is Indexer) + Report.Error (54, Location, + "Inconsistent accessibility: indexer return type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than indexer `" + GetSignatureForError () + "'"); + else if (this is MethodCore) { + if (this is Operator) + Report.Error (56, Location, + "Inconsistent accessibility: return type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than operator `" + GetSignatureForError () + "'"); + else + Report.Error (50, Location, + "Inconsistent accessibility: return type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than method `" + GetSignatureForError () + "'"); + } else if (this is Event) { + Report.Error (7025, Location, + "Inconsistent accessibility: event type `{0}' is less accessible than event `{1}'", + MemberType.GetSignatureForError (), GetSignatureForError ()); + } else { + Report.Error (52, Location, + "Inconsistent accessibility: field type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than field `" + GetSignatureForError () + "'"); + } + } + } + + protected void IsTypePermitted () + { + if (MemberType.IsSpecialRuntimeType) { + if (Parent is StateMachine) { + Report.Error (4012, Location, + "Parameters or local variables of type `{0}' cannot be declared in async methods or iterators", + MemberType.GetSignatureForError ()); + } else if (Parent is HoistedStoreyClass) { + Report.Error (4013, Location, + "Local variables of type `{0}' cannot be used inside anonymous methods, lambda expressions or query expressions", + MemberType.GetSignatureForError ()); + } else { + Report.Error (610, Location, + "Field or property cannot be of type `{0}'", MemberType.GetSignatureForError ()); + } + } + } + + protected virtual bool CheckBase () + { + CheckProtectedModifier (); + + return true; + } + + public override string GetSignatureForDocumentation () + { + return Parent.GetSignatureForDocumentation () + "." + MemberName.Basename; + } + + protected virtual bool ResolveMemberType () + { + if (member_type != null) + throw new InternalErrorException ("Multi-resolve"); + + member_type = type_expr.ResolveAsType (this); + return member_type != null; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/codegen.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/codegen.cs new file mode 100644 index 000000000..55a061cf0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/codegen.cs @@ -0,0 +1,1365 @@ +// +// codegen.cs: The code generator +// +// Authors: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using Mono.CompilerServices.SymbolWriter; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + /// + /// An Emit Context is created for each body of code (from methods, + /// properties bodies, indexer bodies or constructor bodies) + /// + public class EmitContext : BuilderContext + { + // TODO: Has to be private + public readonly ILGenerator ig; + + /// + /// The value that is allowed to be returned or NULL if there is no + /// return type. + /// + readonly TypeSpec return_type; + + /// + /// Keeps track of the Type to LocalBuilder temporary storage created + /// to store structures (used to compute the address of the structure + /// value on structure method invocations) + /// + Dictionary temporary_storage; + + /// + /// The location where we store the return value. + /// + public LocalBuilder return_value; + + + /// + /// Current loop begin and end labels. + /// + public Label LoopBegin, LoopEnd; + + /// + /// Default target in a switch statement. Only valid if + /// InSwitch is true + /// + public Label DefaultTarget; + + /// + /// If this is non-null, points to the current switch statement + /// + public Switch Switch; + + /// + /// Whether we are inside an anonymous method. + /// + public AnonymousExpression CurrentAnonymousMethod; + + readonly IMemberContext member_context; + + readonly SourceMethodBuilder methodSymbols; + + DynamicSiteClass dynamic_site_container; + + Label? return_label; + + List epilogue_expressions; + + public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type, SourceMethodBuilder methodSymbols) + { + this.member_context = rc; + this.ig = ig; + this.return_type = return_type; + + if (rc.Module.Compiler.Settings.Checked) + flags |= Options.CheckedScope; + + if (methodSymbols != null) { + this.methodSymbols = methodSymbols; + if (!rc.Module.Compiler.Settings.Optimize) + flags |= Options.AccurateDebugInfo; + } else { + flags |= Options.OmitDebugInfo; + } + +#if STATIC + ig.__CleverExceptionBlockAssistance (); +#endif + } + + #region Properties + + internal AsyncTaskStorey AsyncTaskStorey { + get { + return CurrentAnonymousMethod.Storey as AsyncTaskStorey; + } + } + + public BuiltinTypes BuiltinTypes { + get { + return MemberContext.Module.Compiler.BuiltinTypes; + } + } + + public ConditionalAccessContext ConditionalAccess { get; set; } + + public TypeSpec CurrentType { + get { return member_context.CurrentType; } + } + + public TypeParameters CurrentTypeParameters { + get { return member_context.CurrentTypeParameters; } + } + + public MemberCore CurrentTypeDefinition { + get { return member_context.CurrentMemberDefinition; } + } + + public bool EmitAccurateDebugInfo { + get { + return (flags & Options.AccurateDebugInfo) != 0; + } + } + + public bool HasMethodSymbolBuilder { + get { + return methodSymbols != null; + } + } + + public bool HasReturnLabel { + get { + return return_label.HasValue; + } + } + + public bool IsStatic { + get { return member_context.IsStatic; } + } + + public bool IsStaticConstructor { + get { + return member_context.IsStatic && (flags & Options.ConstructorScope) != 0; + } + } + + public bool IsAnonymousStoreyMutateRequired { + get { + return CurrentAnonymousMethod != null && + CurrentAnonymousMethod.Storey != null && + CurrentAnonymousMethod.Storey.Mutator != null; + } + } + + public IMemberContext MemberContext { + get { + return member_context; + } + } + + public ModuleContainer Module { + get { + return member_context.Module; + } + } + + public bool NotifyEvaluatorOnStore { + get { + return Module.Evaluator != null && Module.Evaluator.ModificationListener != null; + } + } + + // Has to be used for specific emitter errors only any + // possible resolver errors have to be reported during Resolve + public Report Report { + get { + return member_context.Module.Compiler.Report; + } + } + + public TypeSpec ReturnType { + get { + return return_type; + } + } + + // + // The label where we have to jump before leaving the context + // + public Label ReturnLabel { + get { + return return_label.Value; + } + } + + public List StatementEpilogue { + get { + return epilogue_expressions; + } + } + + public LocalVariable AsyncThrowVariable { get; set; } + + public List TryFinallyUnwind { get; set; } + + #endregion + + public void AddStatementEpilog (IExpressionCleanup cleanupExpression) + { + if (epilogue_expressions == null) { + epilogue_expressions = new List (); + } else if (epilogue_expressions.Contains (cleanupExpression)) { + return; + } + + epilogue_expressions.Add (cleanupExpression); + } + + public void AssertEmptyStack () + { +#if STATIC + if (ig.__StackHeight != 0) + throw new InternalErrorException ("Await yields with non-empty stack in `{0}", + member_context.GetSignatureForError ()); +#endif + } + + /// + /// This is called immediately before emitting an IL opcode to tell the symbol + /// writer to which source line this opcode belongs. + /// + public bool Mark (Location loc) + { + if ((flags & Options.OmitDebugInfo) != 0) + return false; + + if (loc.IsNull || methodSymbols == null) + return false; + + var sf = loc.SourceFile; + if (sf.IsHiddenLocation (loc)) + return false; + +#if NET_4_0 + methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false); +#endif + return true; + } + + public void MarkCallEntry (Location loc) + { + if (!EmitAccurateDebugInfo) + return; + + // + // TODO: This should emit different kind of sequence point to make + // step-over work for statement over multiple lines + // + // Debugging experience for Foo (A () + B ()) where A and B are + // on separate lines is not great + // + Mark (loc); + } + + public void DefineLocalVariable (string name, LocalBuilder builder) + { + if ((flags & Options.OmitDebugInfo) != 0) + return; + + methodSymbols.AddLocal (builder.LocalIndex, name); + } + + public void BeginCatchBlock (TypeSpec type) + { + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.BeginCatchBlock (type.GetMetaInfo ()); + } + + public void BeginFilterHandler () + { + ig.BeginCatchBlock (null); + } + + public void BeginExceptionBlock () + { + ig.BeginExceptionBlock (); + } + + public void BeginExceptionFilterBlock () + { + ig.BeginExceptFilterBlock (); + } + + public void BeginFinallyBlock () + { + ig.BeginFinallyBlock (); + } + + public void BeginScope () + { + if ((flags & Options.OmitDebugInfo) != 0) + return; + +#if NET_4_0 + methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset); +#endif + } + + public void BeginCompilerScope () + { + if ((flags & Options.OmitDebugInfo) != 0) + return; + +#if NET_4_0 + methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset); +#endif + } + + public void EndExceptionBlock () + { + ig.EndExceptionBlock (); + } + + public void EndScope () + { + if ((flags & Options.OmitDebugInfo) != 0) + return; + +#if NET_4_0 + methodSymbols.EndBlock (ig.ILOffset); +#endif + } + + public void CloseConditionalAccess (TypeSpec type) + { + if (type != null) + Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type)); + + MarkLabel (ConditionalAccess.EndLabel); + ConditionalAccess = null; + } + + // + // Creates a nested container in this context for all dynamic compiler generated stuff + // + internal DynamicSiteClass CreateDynamicSite () + { + if (dynamic_site_container == null) { + var mc = member_context.CurrentMemberDefinition as MemberBase; + dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters); + + CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container); + dynamic_site_container.CreateContainer (); + dynamic_site_container.DefineContainer (); + dynamic_site_container.Define (); + + var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes); + var inflated = dynamic_site_container.CurrentType.InflateMember (inflator); + CurrentType.MemberCache.AddMember (inflated); + } + + return dynamic_site_container; + } + + public Label CreateReturnLabel () + { + if (!return_label.HasValue) + return_label = DefineLabel (); + + return return_label.Value; + } + + public LocalBuilder DeclareLocal (TypeSpec type, bool pinned) + { + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + return ig.DeclareLocal (type.GetMetaInfo (), pinned); + } + + public Label DefineLabel () + { + return ig.DefineLabel (); + } + + // + // Creates temporary field in current async storey + // + public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false) + { + var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired); + var fexpr = new StackFieldExpr (f); + fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null); + return fexpr; + } + + public void MarkLabel (Label label) + { + ig.MarkLabel (label); + } + + public void Emit (OpCode opcode) + { + ig.Emit (opcode); + } + + public void Emit (OpCode opcode, LocalBuilder local) + { + ig.Emit (opcode, local); + } + + public void Emit (OpCode opcode, string arg) + { + ig.Emit (opcode, arg); + } + + public void Emit (OpCode opcode, double arg) + { + ig.Emit (opcode, arg); + } + + public void Emit (OpCode opcode, float arg) + { + ig.Emit (opcode, arg); + } + + public void Emit (OpCode opcode, Label label) + { + ig.Emit (opcode, label); + } + + public void Emit (OpCode opcode, Label[] labels) + { + ig.Emit (opcode, labels); + } + + public void Emit (OpCode opcode, TypeSpec type) + { + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (opcode, type.GetMetaInfo ()); + } + + public void Emit (OpCode opcode, FieldSpec field) + { + if (IsAnonymousStoreyMutateRequired) + field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (opcode, field.GetMetaInfo ()); + } + + public void Emit (OpCode opcode, MethodSpec method) + { + if (IsAnonymousStoreyMutateRequired) + method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + if (method.IsConstructor) + ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ()); + else + ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ()); + } + + // TODO: REMOVE breaks mutator + public void Emit (OpCode opcode, MethodInfo method) + { + ig.Emit (opcode, method); + } + + public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs) + { + // TODO MemberCache: This should mutate too + ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs); + } + + public void EmitArrayNew (ArrayContainer ac) + { + if (ac.Rank == 1) { + var type = IsAnonymousStoreyMutateRequired ? + CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) : + ac.Element; + + ig.Emit (OpCodes.Newarr, type.GetMetaInfo ()); + } else { + if (IsAnonymousStoreyMutateRequired) + ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (OpCodes.Newobj, ac.GetConstructor ()); + } + } + + public void EmitArrayAddress (ArrayContainer ac) + { + if (ac.Rank > 1) { + if (IsAnonymousStoreyMutateRequired) + ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (OpCodes.Call, ac.GetAddressMethod ()); + } else { + var type = IsAnonymousStoreyMutateRequired ? + CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) : + ac.Element; + + ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ()); + } + } + + // + // Emits the right opcode to load from an array + // + public void EmitArrayLoad (ArrayContainer ac) + { + if (ac.Rank > 1) { + if (IsAnonymousStoreyMutateRequired) + ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (OpCodes.Call, ac.GetGetMethod ()); + return; + } + + + var type = ac.Element; + if (type.Kind == MemberKind.Enum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Bool: + // + // Workaround MSIL limitation. Load bool element as single bit, + // bool array can actually store any byte value + // + ig.Emit (OpCodes.Ldelem_U1); + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Cgt_Un); + break; + case BuiltinTypeSpec.Type.Byte: + ig.Emit (OpCodes.Ldelem_U1); + break; + case BuiltinTypeSpec.Type.SByte: + ig.Emit (OpCodes.Ldelem_I1); + break; + case BuiltinTypeSpec.Type.Short: + ig.Emit (OpCodes.Ldelem_I2); + break; + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Char: + ig.Emit (OpCodes.Ldelem_U2); + break; + case BuiltinTypeSpec.Type.Int: + ig.Emit (OpCodes.Ldelem_I4); + break; + case BuiltinTypeSpec.Type.UInt: + ig.Emit (OpCodes.Ldelem_U4); + break; + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Long: + ig.Emit (OpCodes.Ldelem_I8); + break; + case BuiltinTypeSpec.Type.Float: + ig.Emit (OpCodes.Ldelem_R4); + break; + case BuiltinTypeSpec.Type.Double: + ig.Emit (OpCodes.Ldelem_R8); + break; + case BuiltinTypeSpec.Type.IntPtr: + ig.Emit (OpCodes.Ldelem_I); + break; + default: + switch (type.Kind) { + case MemberKind.Struct: + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ()); + ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ()); + break; + case MemberKind.TypeParameter: + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ()); + break; + case MemberKind.PointerType: + ig.Emit (OpCodes.Ldelem_I); + break; + default: + ig.Emit (OpCodes.Ldelem_Ref); + break; + } + break; + } + } + + // + // Emits the right opcode to store to an array + // + public void EmitArrayStore (ArrayContainer ac) + { + if (ac.Rank > 1) { + if (IsAnonymousStoreyMutateRequired) + ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (OpCodes.Call, ac.GetSetMethod ()); + return; + } + + var type = ac.Element; + + if (type.Kind == MemberKind.Enum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Bool: + Emit (OpCodes.Stelem_I1); + return; + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Char: + Emit (OpCodes.Stelem_I2); + return; + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + Emit (OpCodes.Stelem_I4); + return; + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + Emit (OpCodes.Stelem_I8); + return; + case BuiltinTypeSpec.Type.Float: + Emit (OpCodes.Stelem_R4); + return; + case BuiltinTypeSpec.Type.Double: + Emit (OpCodes.Stelem_R8); + return; + } + + switch (type.Kind) { + case MemberKind.Struct: + Emit (OpCodes.Stobj, type); + break; + case MemberKind.TypeParameter: + Emit (OpCodes.Stelem, type); + break; + case MemberKind.PointerType: + Emit (OpCodes.Stelem_I); + break; + default: + Emit (OpCodes.Stelem_Ref); + break; + } + } + + public void EmitInt (int i) + { + EmitIntConstant (i); + } + + void EmitIntConstant (int i) + { + switch (i) { + case -1: + ig.Emit (OpCodes.Ldc_I4_M1); + break; + + case 0: + ig.Emit (OpCodes.Ldc_I4_0); + break; + + case 1: + ig.Emit (OpCodes.Ldc_I4_1); + break; + + case 2: + ig.Emit (OpCodes.Ldc_I4_2); + break; + + case 3: + ig.Emit (OpCodes.Ldc_I4_3); + break; + + case 4: + ig.Emit (OpCodes.Ldc_I4_4); + break; + + case 5: + ig.Emit (OpCodes.Ldc_I4_5); + break; + + case 6: + ig.Emit (OpCodes.Ldc_I4_6); + break; + + case 7: + ig.Emit (OpCodes.Ldc_I4_7); + break; + + case 8: + ig.Emit (OpCodes.Ldc_I4_8); + break; + + default: + if (i >= -128 && i <= 127) { + ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i); + } else + ig.Emit (OpCodes.Ldc_I4, i); + break; + } + } + + public void EmitLong (long l) + { + if (l >= int.MinValue && l <= int.MaxValue) { + EmitIntConstant (unchecked ((int) l)); + ig.Emit (OpCodes.Conv_I8); + } else if (l >= 0 && l <= uint.MaxValue) { + EmitIntConstant (unchecked ((int) l)); + ig.Emit (OpCodes.Conv_U8); + } else { + ig.Emit (OpCodes.Ldc_I8, l); + } + } + + // + // Load the object from the pointer. + // + public void EmitLoadFromPtr (TypeSpec type) + { + if (type.Kind == MemberKind.Enum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + ig.Emit (OpCodes.Ldind_I4); + break; + case BuiltinTypeSpec.Type.UInt: + ig.Emit (OpCodes.Ldind_U4); + break; + case BuiltinTypeSpec.Type.Short: + ig.Emit (OpCodes.Ldind_I2); + break; + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Char: + ig.Emit (OpCodes.Ldind_U2); + break; + case BuiltinTypeSpec.Type.Byte: + ig.Emit (OpCodes.Ldind_U1); + break; + case BuiltinTypeSpec.Type.SByte: + ig.Emit (OpCodes.Ldind_I1); + break; + case BuiltinTypeSpec.Type.Bool: + ig.Emit (OpCodes.Ldind_I1); + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Cgt_Un); + break; + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Long: + ig.Emit (OpCodes.Ldind_I8); + break; + case BuiltinTypeSpec.Type.Float: + ig.Emit (OpCodes.Ldind_R4); + break; + case BuiltinTypeSpec.Type.Double: + ig.Emit (OpCodes.Ldind_R8); + break; + case BuiltinTypeSpec.Type.IntPtr: + ig.Emit (OpCodes.Ldind_I); + break; + default: + switch (type.Kind) { + case MemberKind.Struct: + case MemberKind.TypeParameter: + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ()); + break; + case MemberKind.PointerType: + ig.Emit (OpCodes.Ldind_I); + break; + default: + ig.Emit (OpCodes.Ldind_Ref); + break; + } + break; + } + } + + public void EmitNull () + { + ig.Emit (OpCodes.Ldnull); + } + + public void EmitArgumentAddress (int pos) + { + if (!IsStatic) + ++pos; + + if (pos > byte.MaxValue) + ig.Emit (OpCodes.Ldarga, pos); + else + ig.Emit (OpCodes.Ldarga_S, (byte) pos); + } + + public void EmitArgumentLoad (int pos) + { + if (!IsStatic) + ++pos; + + switch (pos) { + case 0: ig.Emit (OpCodes.Ldarg_0); break; + case 1: ig.Emit (OpCodes.Ldarg_1); break; + case 2: ig.Emit (OpCodes.Ldarg_2); break; + case 3: ig.Emit (OpCodes.Ldarg_3); break; + default: + if (pos > byte.MaxValue) + ig.Emit (OpCodes.Ldarg, pos); + else + ig.Emit (OpCodes.Ldarg_S, (byte) pos); + break; + } + } + + public void EmitArgumentStore (int pos) + { + if (!IsStatic) + ++pos; + + if (pos > byte.MaxValue) + ig.Emit (OpCodes.Starg, pos); + else + ig.Emit (OpCodes.Starg_S, (byte) pos); + } + + // + // The stack contains the pointer and the value of type `type' + // + public void EmitStoreFromPtr (TypeSpec type) + { + if (type.IsEnum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + ig.Emit (OpCodes.Stind_I4); + return; + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + ig.Emit (OpCodes.Stind_I8); + return; + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + ig.Emit (OpCodes.Stind_I2); + return; + case BuiltinTypeSpec.Type.Float: + ig.Emit (OpCodes.Stind_R4); + return; + case BuiltinTypeSpec.Type.Double: + ig.Emit (OpCodes.Stind_R8); + return; + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Bool: + ig.Emit (OpCodes.Stind_I1); + return; + case BuiltinTypeSpec.Type.IntPtr: + ig.Emit (OpCodes.Stind_I); + return; + } + + switch (type.Kind) { + case MemberKind.Struct: + case MemberKind.TypeParameter: + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (OpCodes.Stobj, type.GetMetaInfo ()); + break; + default: + ig.Emit (OpCodes.Stind_Ref); + break; + } + } + + public void EmitThis () + { + ig.Emit (OpCodes.Ldarg_0); + } + + public void EmitEpilogue () + { + if (epilogue_expressions == null) + return; + + foreach (var e in epilogue_expressions) + e.EmitCleanup (this); + + epilogue_expressions = null; + } + + /// + /// Returns a temporary storage for a variable of type t as + /// a local variable in the current body. + /// + public LocalBuilder GetTemporaryLocal (TypeSpec t) + { + if (temporary_storage != null) { + object o; + if (temporary_storage.TryGetValue (t, out o)) { + if (o is Stack) { + var s = (Stack) o; + o = s.Count == 0 ? null : s.Pop (); + } else { + temporary_storage.Remove (t); + } + } + if (o != null) + return (LocalBuilder) o; + } + return DeclareLocal (t, false); + } + + public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t) + { + if (temporary_storage == null) { + temporary_storage = new Dictionary (ReferenceEquality.Default); + temporary_storage.Add (t, b); + return; + } + object o; + + if (!temporary_storage.TryGetValue (t, out o)) { + temporary_storage.Add (t, b); + return; + } + var s = o as Stack; + if (s == null) { + s = new Stack (); + s.Push ((LocalBuilder)o); + temporary_storage [t] = s; + } + s.Push (b); + } + + /// + /// ReturnValue creates on demand the LocalBuilder for the + /// return value from the function. By default this is not + /// used. This is only required when returns are found inside + /// Try or Catch statements. + /// + /// This method is typically invoked from the Emit phase, so + /// we allow the creation of a return label if it was not + /// requested during the resolution phase. Could be cleaned + /// up, but it would replicate a lot of logic in the Emit phase + /// of the code that uses it. + /// + public LocalBuilder TemporaryReturn () + { + if (return_value == null){ + return_value = DeclareLocal (return_type, false); + } + + return return_value; + } + } + + public class ConditionalAccessContext + { + public ConditionalAccessContext (TypeSpec type, Label endLabel) + { + Type = type; + EndLabel = endLabel; + } + + public bool Statement { get; set; } + public Label EndLabel { get; private set; } + public TypeSpec Type { get; private set; } + } + + struct CallEmitter + { + public Expression InstanceExpression; + + // + // When call has to leave an extra copy of all arguments on the stack + // + public bool DuplicateArguments; + + // + // Does not emit InstanceExpression load when InstanceExpressionOnStack + // is set. Used by compound assignments. + // + public bool InstanceExpressionOnStack; + + // + // Any of arguments contains await expression + // + public bool HasAwaitArguments; + + public bool ConditionalAccess; + + // + // When dealing with await arguments the original arguments are converted + // into a new set with hoisted stack results + // + public Arguments EmittedArguments; + + public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc) + { + EmitPredefined (ec, method, Arguments, false, loc); + } + + public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc) + { + EmitPredefined (ec, method, Arguments, true, loc); + } + + public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null) + { + Expression instance_copy = null; + + if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) { + HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait (); + if (HasAwaitArguments && InstanceExpressionOnStack) { + throw new NotSupportedException (); + } + } + + OpCode call_op; + LocalTemporary lt = null; + + if (method.IsStatic) { + call_op = OpCodes.Call; + } else { + call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call; + + if (HasAwaitArguments) { + instance_copy = InstanceExpression.EmitToField (ec); + var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType)); + + if (Arguments == null) { + ie.EmitLoad (ec); + } + } else if (!InstanceExpressionOnStack) { + var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType)); + ie.Emit (ec, ConditionalAccess); + + if (DuplicateArguments) { + ec.Emit (OpCodes.Dup); + if (Arguments != null && Arguments.Count != 0) { + lt = new LocalTemporary (ie.GetStackType (ec)); + lt.Store (ec); + instance_copy = lt; + } + } + } + } + + if (Arguments != null && !InstanceExpressionOnStack) { + EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments); + if (EmittedArguments != null) { + if (instance_copy != null) { + var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType)); + ie.Emit (ec, ConditionalAccess); + + if (lt != null) + lt.Release (ec); + } + + EmittedArguments.Emit (ec); + } + } + + if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) { + ec.Emit (OpCodes.Constrained, InstanceExpression.Type); + } + + if (loc != null) { + // + // Emit explicit sequence point for expressions like Foo.Bar () to help debugger to + // break at right place when LHS expression can be stepped-into + // + ec.MarkCallEntry (loc.Value); + } + + // + // Set instance expression to actual result expression. When it contains await it can be + // picked up by caller + // + InstanceExpression = instance_copy; + + if (method.Parameters.HasArglist) { + var varargs_types = GetVarargsTypes (method, Arguments); + ec.Emit (call_op, method, varargs_types); + } else { + // + // If you have: + // this.DoFoo (); + // and DoFoo is not virtual, you can omit the callvirt, + // because you don't need the null checking behavior. + // + ec.Emit (call_op, method); + } + + // + // Pop the return value if there is one and stack should be empty + // + if (statement && method.ReturnType.Kind != MemberKind.Void) + ec.Emit (OpCodes.Pop); + } + + static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments) + { + AParametersCollection pd = method.Parameters; + + Argument a = arguments[pd.Count - 1]; + Arglist list = (Arglist) a.Expr; + + return list.ArgumentTypes; + } + + // + // Used to decide whether call or callvirt is needed + // + static bool IsVirtualCallRequired (Expression instance, MethodSpec method) + { + // + // There are 2 scenarious where we emit callvirt + // + // Case 1: A method is virtual and it's not used to call base + // Case 2: A method instance expression can be null. In this casen callvirt ensures + // correct NRE exception when the method is called + // + var decl_type = method.DeclaringType; + if (decl_type.IsStruct || decl_type.IsEnum) + return false; + + if (instance is BaseThis) + return false; + + // + // It's non-virtual and will never be null and it can be determined + // whether it's known value or reference type by verifier + // + if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter) + return false; + + return true; + } + + static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType) + { + var instance_type = instance.Type; + return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) || + instance_type.IsGenericParameter || declaringType.IsNullableType; + } + } + + public struct InstanceEmitter + { + readonly Expression instance; + readonly bool addressRequired; + + public InstanceEmitter (Expression instance, bool addressLoad) + { + this.instance = instance; + this.addressRequired = addressLoad; + } + + public void Emit (EmitContext ec, bool conditionalAccess) + { + Label NullOperatorLabel; + Nullable.Unwrap unwrap; + + if (conditionalAccess && Expression.IsNeverNull (instance)) + conditionalAccess = false; + + if (conditionalAccess) { + NullOperatorLabel = ec.DefineLabel (); + unwrap = instance as Nullable.Unwrap; + } else { + NullOperatorLabel = new Label (); + unwrap = null; + } + + IMemoryLocation instance_address = null; + bool conditional_access_dup = false; + + if (unwrap != null) { + unwrap.Store (ec); + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); + } else { + if (conditionalAccess && addressRequired) { + // + // Don't allocate temp variable when instance load is cheap and load and load-address + // operate on same memory + // + instance_address = instance as VariableReference; + if (instance_address == null) + instance_address = instance as LocalTemporary; + + if (instance_address == null) { + EmitLoad (ec); + ec.Emit (OpCodes.Dup); + ec.EmitLoadFromPtr (instance.Type); + + conditional_access_dup = true; + } else { + instance.Emit (ec); + } + + if (instance.Type.Kind == MemberKind.TypeParameter) + ec.Emit (OpCodes.Box, instance.Type); + } else { + EmitLoad (ec); + + if (conditionalAccess) { + conditional_access_dup = !IsInexpensiveLoad (); + if (conditional_access_dup) + ec.Emit (OpCodes.Dup); + } + } + + if (conditionalAccess) { + ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); + + if (conditional_access_dup) + ec.Emit (OpCodes.Pop); + } + } + + if (conditionalAccess) { + if (!ec.ConditionalAccess.Statement) { + if (ec.ConditionalAccess.Type.IsNullableType) + Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec); + else + ec.EmitNull (); + } + + ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel); + ec.MarkLabel (NullOperatorLabel); + + if (instance_address != null) { + instance_address.AddressOf (ec, AddressOp.Load); + } else if (unwrap != null) { + unwrap.Emit (ec); + var tmp = ec.GetTemporaryLocal (unwrap.Type); + ec.Emit (OpCodes.Stloc, tmp); + ec.Emit (OpCodes.Ldloca, tmp); + ec.FreeTemporaryLocal (tmp, unwrap.Type); + } else if (!conditional_access_dup) { + instance.Emit (ec); + } + } + } + + public void EmitLoad (EmitContext ec) + { + var instance_type = instance.Type; + + // + // Push the instance expression + // + if (addressRequired) { + // + // If the expression implements IMemoryLocation, then + // we can optimize and use AddressOf on the + // return. + // + // If not we have to use some temporary storage for + // it. + var iml = instance as IMemoryLocation; + if (iml != null) { + iml.AddressOf (ec, AddressOp.Load); + } else { + LocalTemporary temp = new LocalTemporary (instance_type); + instance.Emit (ec); + temp.Store (ec); + temp.AddressOf (ec, AddressOp.Load); + } + + return; + } + + instance.Emit (ec); + + // Only to make verifier happy + if (RequiresBoxing ()) + ec.Emit (OpCodes.Box, instance_type); + } + + public TypeSpec GetStackType (EmitContext ec) + { + var instance_type = instance.Type; + + if (addressRequired) + return ReferenceContainer.MakeType (ec.Module, instance_type); + + if (instance_type.IsStructOrEnum) + return ec.Module.Compiler.BuiltinTypes.Object; + + return instance_type; + } + + bool RequiresBoxing () + { + var instance_type = instance.Type; + if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type)) + return true; + + if (instance_type.IsStructOrEnum) + return true; + + return false; + } + + bool IsInexpensiveLoad () + { + if (instance is Constant) + return instance.IsSideEffectFree; + + if (RequiresBoxing ()) + return false; + + var vr = instance as VariableReference; + if (vr != null) + return !vr.IsRef; + + if (instance is LocalTemporary) + return true; + + var fe = instance as FieldExpr; + if (fe != null) + return fe.IsStatic || fe.InstanceExpression is This; + + return false; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/complete.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/complete.cs new file mode 100644 index 000000000..3504302d5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/complete.cs @@ -0,0 +1,226 @@ +// +// complete.cs: Expression that are used for completion suggestions. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2009 Novell, Inc. +// Copyright 2011 Xamarin Inc +// +// Completion* classes derive from ExpressionStatement as this allows +// them to pass through the parser in many conditions that require +// statements even when the expression is incomplete (for example +// completing inside a lambda +// +using System.Collections.Generic; +using System.Linq; + +namespace Mono.CSharp { + + // + // A common base class for Completing expressions, it + // is just a very simple ExpressionStatement + // + public abstract class CompletingExpression : ExpressionStatement + { + public static void AppendResults (List results, string prefix, IEnumerable names) + { + foreach (string name in names) { + if (name == null) + continue; + + if (prefix != null && !name.StartsWith (prefix)) + continue; + + if (results.Contains (name)) + continue; + + if (prefix != null) + results.Add (name.Substring (prefix.Length)); + else + results.Add (name); + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return null; + } + + public override void EmitStatement (EmitContext ec) + { + // Do nothing + } + + public override void Emit (EmitContext ec) + { + // Do nothing + } + } + + public class CompletionSimpleName : CompletingExpression { + public string Prefix; + + public CompletionSimpleName (string prefix, Location l) + { + this.loc = l; + this.Prefix = prefix; + } + + protected override Expression DoResolve (ResolveContext ec) + { + var results = new List (); + + ec.CurrentMemberDefinition.GetCompletionStartingWith (Prefix, results); + + throw new CompletionResult (Prefix, results.Distinct ().Select (l => l.Substring (Prefix.Length)).ToArray ()); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + } + + public class CompletionMemberAccess : CompletingExpression { + Expression expr; + string partial_name; + TypeArguments targs; + + public CompletionMemberAccess (Expression e, string partial_name, Location l) + { + this.expr = e; + this.loc = l; + this.partial_name = partial_name; + } + + public CompletionMemberAccess (Expression e, string partial_name, TypeArguments targs, Location l) + { + this.expr = e; + this.loc = l; + this.partial_name = partial_name; + this.targs = targs; + } + + protected override Expression DoResolve (ResolveContext rc) + { + var sn = expr as SimpleName; + const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type; + + if (sn != null) { + expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity); + + // + // Resolve expression which does have type set as we need expression type + // with disable flow analysis as we don't know whether left side expression + // is used as variable or type + // + if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) { + expr = expr.Resolve (rc); + } else if (expr is TypeParameterExpr) { + expr.Error_UnexpectedKind (rc, flags, sn.Location); + expr = null; + } + } else { + expr = expr.Resolve (rc, flags); + } + + if (expr == null) + return null; + + TypeSpec expr_type = expr.Type; + if (expr_type.IsPointer || expr_type.Kind == MemberKind.Void || expr_type == InternalType.NullLiteral || expr_type == InternalType.AnonymousMethod) { + expr.Error_OperatorCannotBeApplied (rc, loc, ".", expr_type); + return null; + } + + if (targs != null) { + if (!targs.Resolve (rc)) + return null; + } + + var results = new List (); + var nexpr = expr as NamespaceExpression; + if (nexpr != null) { + string namespaced_partial; + + if (partial_name == null) + namespaced_partial = nexpr.Namespace.Name; + else + namespaced_partial = nexpr.Namespace.Name + "." + partial_name; + + rc.CurrentMemberDefinition.GetCompletionStartingWith (namespaced_partial, results); + if (partial_name != null) + results = results.Select (l => l.Substring (partial_name.Length)).ToList (); + } else { + var r = MemberCache.GetCompletitionMembers (rc, expr_type, partial_name).Select (l => l.Name); + AppendResults (results, partial_name, r); + } + + throw new CompletionResult (partial_name == null ? "" : partial_name, results.Distinct ().ToArray ()); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CompletionMemberAccess target = (CompletionMemberAccess) t; + + if (targs != null) + target.targs = targs.Clone (); + + target.expr = expr.Clone (clonectx); + } + } + + public class CompletionElementInitializer : CompletingExpression { + string partial_name; + + public CompletionElementInitializer (string partial_name, Location l) + { + this.partial_name = partial_name; + this.loc = l; + } + + protected override Expression DoResolve (ResolveContext ec) + { + var members = MemberCache.GetCompletitionMembers (ec, ec.CurrentInitializerVariable.Type, partial_name); + +// TODO: Does this mean exact match only ? +// if (partial_name != null && results.Count > 0 && result [0] == "") +// throw new CompletionResult ("", new string [] { "=" }); + + var results = members.Where (l => (l.Kind & (MemberKind.Field | MemberKind.Property)) != 0).Select (l => l.Name).ToList (); + if (partial_name != null) { + var temp = new List (); + AppendResults (temp, partial_name, results); + results = temp; + } + + throw new CompletionResult (partial_name == null ? "" : partial_name, results.Distinct ().ToArray ()); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + } + + public class EmptyCompletion : CompletingExpression + { + protected override void CloneTo (CloneContext clonectx, Expression target) + { + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new CompletionResult ("", new string [0]); + } + } + +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/const.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/const.cs new file mode 100644 index 000000000..eef90b346 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/const.cs @@ -0,0 +1,237 @@ +// +// const.cs: Constant declarations. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Copyright 2001-2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// + +#if STATIC +using IKVM.Reflection; +#else +using System.Reflection; +#endif + +namespace Mono.CSharp { + + public class Const : FieldBase + { + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE; + + public Const (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, AllowedModifiers, name, attrs) + { + ModFlags |= Modifiers.STATIC; + } + + /// + /// Defines the constant in the @parent + /// + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!member_type.IsConstantCompatible) { + Error_InvalidConstantType (member_type, Location, Report); + } + + FieldAttributes field_attr = FieldAttributes.Static | ModifiersExtensions.FieldAttr (ModFlags); + // Decimals cannot be emitted into the constant blob. So, convert to 'readonly'. + if (member_type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + field_attr |= FieldAttributes.InitOnly; + } else { + field_attr |= FieldAttributes.Literal; + } + + FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), field_attr); + spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer); + + Parent.MemberCache.AddMember (spec); + + if ((field_attr & FieldAttributes.InitOnly) != 0) + Parent.PartialContainer.RegisterFieldForInitialization (this, + new FieldInitializer (this, initializer, Location)); + + if (declarators != null) { + var t = new TypeExpression (MemberType, TypeExpression.Location); + foreach (var d in declarators) { + var c = new Const (Parent, t, ModFlags & ~Modifiers.STATIC, new MemberName (d.Name.Value, d.Name.Location), OptAttributes); + c.initializer = d.Initializer; + ((ConstInitializer) c.initializer).Name = d.Name.Value; + c.Define (); + Parent.PartialContainer.Members.Add (c); + } + } + + return true; + } + + public void DefineValue () + { + var rc = new ResolveContext (this); + ((ConstSpec) spec).GetConstant (rc); + } + + /// + /// Emits the field value by evaluating the expression + /// + public override void Emit () + { + var c = ((ConstSpec) spec).Value as Constant; + if (c.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + Module.PredefinedAttributes.DecimalConstant.EmitAttribute (FieldBuilder, (decimal) c.GetValue (), c.Location); + } else { + FieldBuilder.SetConstant (c.GetValue ()); + } + + base.Emit (); + } + + public static void Error_InvalidConstantType (TypeSpec t, Location loc, Report Report) + { + if (t.IsGenericParameter) { + Report.Error (1959, loc, + "Type parameter `{0}' cannot be declared const", t.GetSignatureForError ()); + } else { + Report.Error (283, loc, + "The type `{0}' cannot be declared const", t.GetSignatureForError ()); + } + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } + + public class ConstSpec : FieldSpec + { + Expression value; + + public ConstSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo fi, Modifiers mod, Expression value) + : base (declaringType, definition, memberType, fi, mod) + { + this.value = value; + } + + // + // This expresion is guarantee to be a constant at emit phase only + // + public Expression Value { + get { + return value; + } + } + + // + // For compiled constants we have to resolve the value as there could be constant dependecies. This + // is needed for imported constants too to get the right context type + // + public Constant GetConstant (ResolveContext rc) + { + if (value.eclass != ExprClass.Value) + value = value.Resolve (rc); + + return (Constant) value; + } + } + + public class ConstInitializer : ShimExpression + { + bool in_transit; + readonly FieldBase field; + + public ConstInitializer (FieldBase field, Expression value, Location loc) + : base (value) + { + this.loc = loc; + this.field = field; + } + + public string Name { get; set; } + + protected override Expression DoResolve (ResolveContext unused) + { + if (type != null) + return expr; + + var opt = ResolveContext.Options.ConstantScope; + if (field is EnumMember) + opt |= ResolveContext.Options.EnumScope; + + // + // Use a context in which the constant was declared and + // not the one in which is referenced + // + var rc = new ResolveContext (field, opt); + expr = DoResolveInitializer (rc); + type = expr.Type; + + return expr; + } + + protected virtual Expression DoResolveInitializer (ResolveContext rc) + { + if (in_transit) { + field.Compiler.Report.Error (110, expr.Location, + "The evaluation of the constant value for `{0}' involves a circular definition", + GetSignatureForError ()); + + expr = null; + } else { + in_transit = true; + expr = expr.Resolve (rc); + } + + in_transit = false; + + if (expr != null) { + Constant c = expr as Constant; + if (c != null) + c = field.ConvertInitializer (rc, c); + + if (c == null) { + if (TypeSpec.IsReferenceType (field.MemberType)) + Error_ConstantCanBeInitializedWithNullOnly (rc, field.MemberType, expr.Location, GetSignatureForError ()); + else if (!(expr is Constant)) + Error_ExpressionMustBeConstant (rc, expr.Location, GetSignatureForError ()); + else + expr.Error_ValueCannotBeConverted (rc, field.MemberType, false); + } + + expr = c; + } + + if (expr == null) { + expr = New.Constantify (field.MemberType, Location); + if (expr == null) + expr = Constant.CreateConstantFromValue (field.MemberType, null, Location); + expr = expr.Resolve (rc); + } + + return expr; + } + + public override string GetSignatureForError () + { + if (Name == null) + return field.GetSignatureForError (); + + return field.Parent.GetSignatureForError () + "." + Name; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/constant.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/constant.cs new file mode 100644 index 000000000..a50baacc7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/constant.cs @@ -0,0 +1,2496 @@ +// +// constant.cs: Constants. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001-2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011-2013 Xamarin Inc +// + +using System; +using System.Globalization; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// Base class for constants and literals. + /// + public abstract class Constant : Expression + { + static readonly NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat; + + protected Constant (Location loc) + { + this.loc = loc; + } + + override public string ToString () + { + return this.GetType ().Name + " (" + GetValueAsLiteral () + ")"; + } + + /// + /// This is used to obtain the actual value of the literal + /// cast into an object. + /// + public abstract object GetValue (); + + public abstract long GetValueAsLong (); + + public abstract string GetValueAsLiteral (); + +#if !STATIC + // + // Returns an object value which is typed to contant type + // + public virtual object GetTypedValue () + { + return GetValue (); + } +#endif + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + if (!expl && IsLiteral && + BuiltinTypeSpec.IsPrimitiveTypeOrDecimal (target) && + BuiltinTypeSpec.IsPrimitiveTypeOrDecimal (type)) { + ec.Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'", + GetValueAsLiteral (), target.GetSignatureForError ()); + } else { + base.Error_ValueCannotBeConverted (ec, target, expl); + } + } + + public Constant ImplicitConversionRequired (ResolveContext ec, TypeSpec type) + { + Constant c = ConvertImplicitly (type); + if (c == null) + Error_ValueCannotBeConverted (ec, type, false); + + return c; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public virtual Constant ConvertImplicitly (TypeSpec type) + { + if (this.type == type) + return this; + + if (!Convert.ImplicitNumericConversionExists (this.type, type)) + return null; + + bool fail; + object constant_value = ChangeType (GetValue (), type, out fail); + if (fail){ + // + // We should always catch the error before this is ever + // reached, by calling Convert.ImplicitStandardConversionExists + // + throw new InternalErrorException ("Missing constant conversion between `{0}' and `{1}'", + Type.GetSignatureForError (), type.GetSignatureForError ()); + } + + return CreateConstantFromValue (type, constant_value, loc); + } + + // + // Returns a constant instance based on Type + // + public static Constant CreateConstantFromValue (TypeSpec t, object v, Location loc) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + return new IntConstant (t, (int) v, loc); + case BuiltinTypeSpec.Type.String: + return new StringConstant (t, (string) v, loc); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (t, (uint) v, loc); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (t, (long) v, loc); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (t, (ulong) v, loc); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (t, (float) v, loc); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (t, (double) v, loc); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (t, (short) v, loc); + case BuiltinTypeSpec.Type.UShort: + return new UShortConstant (t, (ushort) v, loc); + case BuiltinTypeSpec.Type.SByte: + return new SByteConstant (t, (sbyte) v, loc); + case BuiltinTypeSpec.Type.Byte: + return new ByteConstant (t, (byte) v, loc); + case BuiltinTypeSpec.Type.Char: + return new CharConstant (t, (char) v, loc); + case BuiltinTypeSpec.Type.Bool: + return new BoolConstant (t, (bool) v, loc); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (t, (decimal) v, loc); + } + + if (t.IsEnum) { + var real_type = EnumSpec.GetUnderlyingType (t); + return new EnumConstant (CreateConstantFromValue (real_type, v, loc), t); + } + + if (v == null) { + if (t.IsNullableType) + return Nullable.LiftedNull.Create (t, loc); + + if (TypeSpec.IsReferenceType (t)) + return new NullConstant (t, loc); + } + +#if STATIC + throw new InternalErrorException ("Constant value `{0}' has unexpected underlying type `{1}'", v, t.GetSignatureForError ()); +#else + return null; +#endif + } + + // + // Returns a constant instance based on value and type. This is probing version of + // CreateConstantFromValue + // + public static Constant ExtractConstantFromValue (TypeSpec t, object v, Location loc) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + if (v is int) + return new IntConstant (t, (int) v, loc); + break; + case BuiltinTypeSpec.Type.String: + if (v is string) + return new StringConstant (t, (string) v, loc); + break; + case BuiltinTypeSpec.Type.UInt: + if (v is uint) + return new UIntConstant (t, (uint) v, loc); + break; + case BuiltinTypeSpec.Type.Long: + if (v is long) + return new LongConstant (t, (long) v, loc); + break; + case BuiltinTypeSpec.Type.ULong: + if (v is ulong) + return new ULongConstant (t, (ulong) v, loc); + break; + case BuiltinTypeSpec.Type.Float: + if (v is float) + return new FloatConstant (t, (float) v, loc); + break; + case BuiltinTypeSpec.Type.Double: + if (v is double) + return new DoubleConstant (t, (double) v, loc); + break; + case BuiltinTypeSpec.Type.Short: + if (v is short) + return new ShortConstant (t, (short) v, loc); + break; + case BuiltinTypeSpec.Type.UShort: + if (v is ushort) + return new UShortConstant (t, (ushort) v, loc); + break; + case BuiltinTypeSpec.Type.SByte: + if (v is sbyte) + return new SByteConstant (t, (sbyte) v, loc); + break; + case BuiltinTypeSpec.Type.Byte: + if (v is byte) + return new ByteConstant (t, (byte) v, loc); + break; + case BuiltinTypeSpec.Type.Char: + if (v is char) + return new CharConstant (t, (char) v, loc); + break; + case BuiltinTypeSpec.Type.Bool: + if (v is bool) + return new BoolConstant (t, (bool) v, loc); + break; + case BuiltinTypeSpec.Type.Decimal: + if (v is decimal) + return new DecimalConstant (t, (decimal) v, loc); + break; + } + + if (t.IsEnum) { + var real_type = EnumSpec.GetUnderlyingType (t); + return new EnumConstant (CreateConstantFromValue (real_type, v, loc), t); + } + + if (v == null) { + if (t.IsNullableType) + return Nullable.LiftedNull.Create (t, loc); + + if (TypeSpec.IsReferenceType (t)) + return new NullConstant (t, loc); + } + + return null; + } + + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (type, loc))); + + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + /// + /// Maybe ConvertTo name is better. It tries to convert `this' constant to target_type. + /// It throws OverflowException + /// + // DON'T CALL THIS METHOD DIRECTLY AS IT DOES NOT HANDLE ENUMS + public abstract Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type); + + // This is a custom version of Convert.ChangeType() which works + // with the TypeBuilder defined types when compiling corlib. + static object ChangeType (object value, TypeSpec targetType, out bool error) + { + IConvertible convert_value = value as IConvertible; + + if (convert_value == null) { + error = true; + return null; + } + + // + // We cannot rely on build-in type conversions as they are + // more limited than what C# supports. + // See char -> float/decimal/double conversion + // + error = false; + try { + switch (targetType.BuiltinType) { + case BuiltinTypeSpec.Type.Bool: + return convert_value.ToBoolean (nfi); + case BuiltinTypeSpec.Type.Byte: + return convert_value.ToByte (nfi); + case BuiltinTypeSpec.Type.Char: + return convert_value.ToChar (nfi); + case BuiltinTypeSpec.Type.Short: + return convert_value.ToInt16 (nfi); + case BuiltinTypeSpec.Type.Int: + return convert_value.ToInt32 (nfi); + case BuiltinTypeSpec.Type.Long: + return convert_value.ToInt64 (nfi); + case BuiltinTypeSpec.Type.SByte: + return convert_value.ToSByte (nfi); + case BuiltinTypeSpec.Type.Decimal: + if (convert_value.GetType () == typeof (char)) + return (decimal) convert_value.ToInt32 (nfi); + return convert_value.ToDecimal (nfi); + case BuiltinTypeSpec.Type.Double: + if (convert_value.GetType () == typeof (char)) + return (double) convert_value.ToInt32 (nfi); + return convert_value.ToDouble (nfi); + case BuiltinTypeSpec.Type.Float: + if (convert_value.GetType () == typeof (char)) + return (float) convert_value.ToInt32 (nfi); + return convert_value.ToSingle (nfi); + case BuiltinTypeSpec.Type.String: + return convert_value.ToString (nfi); + case BuiltinTypeSpec.Type.UShort: + return convert_value.ToUInt16 (nfi); + case BuiltinTypeSpec.Type.UInt: + return convert_value.ToUInt32 (nfi); + case BuiltinTypeSpec.Type.ULong: + return convert_value.ToUInt64 (nfi); + case BuiltinTypeSpec.Type.Object: + return value; + } + } catch { + } + + error = true; + return null; + } + + protected override Expression DoResolve (ResolveContext rc) + { + return this; + } + + // + // Attempts to do a compile-time folding of a constant cast and handles + // error reporting for constant overlows only, on normal conversion + // errors returns null + // + public Constant Reduce (ResolveContext ec, TypeSpec target_type) + { + try { + return TryReduceConstant (ec, target_type); + } catch (OverflowException) { + if (ec.ConstantCheckState && Type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { + ec.Report.Error (221, loc, + "Constant value `{0}' cannot be converted to a `{1}' (use `unchecked' syntax to override)", + GetValueAsLiteral (), target_type.GetSignatureForError ()); + } else { + Error_ValueCannotBeConverted (ec, target_type, false); + } + + return New.Constantify (target_type, loc); + } + } + + public Constant TryReduce (ResolveContext rc, TypeSpec targetType) + { + try { + return TryReduceConstant (rc, targetType); + } catch (OverflowException) { + return null; + } + } + + Constant TryReduceConstant (ResolveContext ec, TypeSpec target_type) + { + if (Type == target_type) { + // + // Reducing literal value produces a new constant. Syntactically 10 is not same as (int)10 + // + if (IsLiteral) + return CreateConstantFromValue (target_type, GetValue (), loc); + + return this; + } + + Constant c; + if (target_type.IsEnum) { + c = TryReduceConstant (ec, EnumSpec.GetUnderlyingType (target_type)); + if (c == null) + return null; + + return new EnumConstant (c, target_type); + } + + return ConvertExplicitly (ec.ConstantCheckState, target_type); + } + + /// + /// Need to pass type as the constant can require a boxing + /// and in such case no optimization is possible + /// + public bool IsDefaultInitializer (TypeSpec type) + { + if (type == Type) + return IsDefaultValue; + + return this is NullLiteral; + } + + public abstract bool IsDefaultValue { + get; + } + + public abstract bool IsNegative { + get; + } + + // + // When constant is declared as literal + // + public virtual bool IsLiteral { + get { return false; } + } + + public virtual bool IsOneInteger { + get { return false; } + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + // + // Returns true iff 1) the stack type of this is one of Object, + // int32, int64 and 2) this == 0 or this == null. + // + public virtual bool IsZeroInteger { + get { return false; } + } + + public override void EmitSideEffect (EmitContext ec) + { + // do nothing + } + + public sealed override Expression Clone (CloneContext clonectx) + { + // No cloning is not needed for constants + return this; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + throw new NotSupportedException ("should not be reached"); + } + + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return System.Linq.Expressions.Expression.Constant (GetTypedValue (), type.GetMetaInfo ()); +#endif + } + + public new bool Resolve (ResolveContext rc) + { + // It exists only as hint not to call Resolve on constants + return true; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + public abstract class IntegralConstant : Constant + { + protected IntegralConstant (TypeSpec type, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + try { + ConvertExplicitly (true, target); + base.Error_ValueCannotBeConverted (ec, target, expl); + } + catch + { + ec.Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'", + GetValue ().ToString (), target.GetSignatureForError ()); + } + } + + public override string GetValueAsLiteral () + { + return GetValue ().ToString (); + } + + public abstract Constant Increment (); + } + + public class BoolConstant : Constant { + public readonly bool Value; + + public BoolConstant (BuiltinTypes types, bool val, Location loc) + : this (types.Bool, val, loc) + { + } + + public BoolConstant (TypeSpec type, bool val, Location loc) + : base (loc) + { + eclass = ExprClass.Value; + this.type = type; + + Value = val; + } + + public override object GetValue () + { + return (object) Value; + } + + public override string GetValueAsLiteral () + { + return Value ? "true" : "false"; + } + + public override long GetValueAsLong () + { + return Value ? 1 : 0; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + if (Value) + ec.EmitInt (1); + else + ec.EmitInt (0); + } + + public override bool IsDefaultValue { + get { + return !Value; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsZeroInteger { + get { return Value == false; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + return null; + } + + } + + public class ByteConstant : IntegralConstant + { + public readonly byte Value; + + public ByteConstant (BuiltinTypes types, byte v, Location loc) + : this (types.Byte, v, loc) + { + } + + public ByteConstant (TypeSpec type, byte v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new ByteConstant (type, checked ((byte)(Value + 1)), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context){ + if (Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class CharConstant : Constant { + public readonly char Value; + + public CharConstant (BuiltinTypes types, char v, Location loc) + : this (types.Char, v, loc) + { + } + + public CharConstant (TypeSpec type, char v, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode ((ushort) Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + static string descape (char c) + { + switch (c){ + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\n': + return "\\n"; + case '\t': + return "\\t"; + case '\v': + return "\\v"; + case '\r': + return "\\r"; + case '\\': + return "\\\\"; + case '\f': + return "\\f"; + case '\0': + return "\\0"; + case '"': + return "\\\""; + case '\'': + return "\\\'"; + } + return c.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override string GetValueAsLiteral () + { + return "\"" + descape (Value) + "\""; + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsZeroInteger { + get { return Value == '\0'; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class SByteConstant : IntegralConstant + { + public readonly sbyte Value; + + public SByteConstant (BuiltinTypes types, sbyte v, Location loc) + : this (types.SByte, v, loc) + { + } + + public SByteConstant (TypeSpec type, sbyte v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new SByteConstant (type, checked((sbyte)(Value + 1)), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class ShortConstant : IntegralConstant { + public readonly short Value; + + public ShortConstant (BuiltinTypes types, short v, Location loc) + : this (types.Short, v, loc) + { + } + + public ShortConstant (TypeSpec type, short v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new ShortConstant (type, checked((short)(Value + 1)), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < SByte.MinValue || Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context && Value < 0) + throw new OverflowException (); + + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < Char.MinValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class UShortConstant : IntegralConstant + { + public readonly ushort Value; + + public UShortConstant (BuiltinTypes types, ushort v, Location loc) + : this (types.UShort, v, loc) + { + } + + public UShortConstant (TypeSpec type, ushort v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new UShortConstant (type, checked((ushort)(Value + 1)), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value > Char.MaxValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + } + + public class IntConstant : IntegralConstant + { + public readonly int Value; + + public IntConstant (BuiltinTypes types, int v, Location loc) + : this (types.Int, v, loc) + { + } + + public IntConstant (TypeSpec type, int v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new IntConstant (type, checked(Value + 1), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < SByte.MinValue || Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value < Int16.MinValue || Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < UInt16.MinValue || Value > UInt16.MaxValue) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context) { + if (Value < UInt32.MinValue) + throw new OverflowException (); + } + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < Char.MinValue || Value > Char.MaxValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (this.type == type) + return this; + + Constant c = TryImplicitIntConversion (type); + if (c != null) + return c; //.Resolve (rc); + + return base.ConvertImplicitly (type); + } + + /// + /// Attempts to perform an implicit constant conversion of the IntConstant + /// into a different data type using casts (See Implicit Constant + /// Expression Conversions) + /// + Constant TryImplicitIntConversion (TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + if (Value >= SByte.MinValue && Value <= SByte.MaxValue) + return new SByteConstant (target_type, (sbyte) Value, loc); + break; + case BuiltinTypeSpec.Type.Byte: + if (Value >= Byte.MinValue && Value <= Byte.MaxValue) + return new ByteConstant (target_type, (byte) Value, loc); + break; + case BuiltinTypeSpec.Type.Short: + if (Value >= Int16.MinValue && Value <= Int16.MaxValue) + return new ShortConstant (target_type, (short) Value, loc); + break; + case BuiltinTypeSpec.Type.UShort: + if (Value >= UInt16.MinValue && Value <= UInt16.MaxValue) + return new UShortConstant (target_type, (ushort) Value, loc); + break; + case BuiltinTypeSpec.Type.UInt: + if (Value >= 0) + return new UIntConstant (target_type, (uint) Value, loc); + break; + case BuiltinTypeSpec.Type.ULong: + // + // we can optimize this case: a positive int32 + // always fits on a uint64. But we need an opcode + // to do it. + // + if (Value >= 0) + return new ULongConstant (target_type, (ulong) Value, loc); + break; + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, loc); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, loc); + } + + return null; + } + } + + public class UIntConstant : IntegralConstant { + public readonly uint Value; + + public UIntConstant (BuiltinTypes types, uint v, Location loc) + : this (types.UInt, v, loc) + { + } + + public UIntConstant (TypeSpec type, uint v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (unchecked ((int) Value)); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new UIntConstant (type, checked(Value + 1), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < 0 || Value > byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < UInt16.MinValue || Value > UInt16.MaxValue) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context) { + if (Value > Int32.MaxValue) + throw new OverflowException (); + } + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < Char.MinValue || Value > Char.MaxValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class LongConstant : IntegralConstant { + public readonly long Value; + + public LongConstant (BuiltinTypes types, long v, Location loc) + : this (types.Long, v, loc) + { + } + + public LongConstant (TypeSpec type, long v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitLong (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new LongConstant (type, checked(Value + 1), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < SByte.MinValue || Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value < Int16.MinValue || Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < UInt16.MinValue || Value > UInt16.MaxValue) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context) { + if (Value < Int32.MinValue || Value > Int32.MaxValue) + throw new OverflowException (); + } + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context) { + if (Value < UInt32.MinValue || Value > UInt32.MaxValue) + throw new OverflowException (); + } + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < Char.MinValue || Value > Char.MaxValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (Value >= 0 && type.BuiltinType == BuiltinTypeSpec.Type.ULong) { + return new ULongConstant (type, (ulong) Value, loc); + } + + return base.ConvertImplicitly (type); + } + } + + public class ULongConstant : IntegralConstant { + public readonly ulong Value; + + public ULongConstant (BuiltinTypes types, ulong v, Location loc) + : this (types.ULong, v, loc) + { + } + + public ULongConstant (TypeSpec type, ulong v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitLong (unchecked ((long) Value)); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return (long) Value; + } + + public override Constant Increment () + { + return new ULongConstant (type, checked(Value + 1), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context && Value > Byte.MaxValue) + throw new OverflowException (); + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context && Value > ((ulong) SByte.MaxValue)) + throw new OverflowException (); + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context && Value > ((ulong) Int16.MaxValue)) + throw new OverflowException (); + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context && Value > UInt16.MaxValue) + throw new OverflowException (); + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context && Value > UInt32.MaxValue) + throw new OverflowException (); + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context && Value > UInt32.MaxValue) + throw new OverflowException (); + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + if (in_checked_context && Value > Int64.MaxValue) + throw new OverflowException (); + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context && Value > Char.MaxValue) + throw new OverflowException (); + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class FloatConstant : Constant { + // + // Store constant value as double because float constant operations + // need to work on double value to match JIT + // + public readonly double DoubleValue; + + public FloatConstant (BuiltinTypes types, double v, Location loc) + : this (types.Float, v, loc) + { + } + + public FloatConstant (TypeSpec type, double v, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + DoubleValue = v; + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (type.BuiltinType == BuiltinTypeSpec.Type.Double) + return new DoubleConstant (type, DoubleValue, loc); + + return base.ConvertImplicitly (type); + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldc_R4, Value); + } + + public float Value { + get { + return (float) DoubleValue; + } + } + + public override object GetValue () + { + return Value; + } + + public override string GetValueAsLiteral () + { + return Value.ToString (); + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < byte.MinValue || Value > byte.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) DoubleValue, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < sbyte.MinValue || Value > sbyte.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) DoubleValue, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value < short.MinValue || Value > short.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) DoubleValue, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < ushort.MinValue || Value > ushort.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) DoubleValue, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context) { + if (Value < int.MinValue || Value > int.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new IntConstant (target_type, (int) DoubleValue, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context) { + if (Value < uint.MinValue || Value > uint.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new UIntConstant (target_type, (uint) DoubleValue, Location); + case BuiltinTypeSpec.Type.Long: + if (in_checked_context) { + if (Value < long.MinValue || Value > long.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new LongConstant (target_type, (long) DoubleValue, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context) { + if (Value < ulong.MinValue || Value > ulong.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new ULongConstant (target_type, (ulong) DoubleValue, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, DoubleValue, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < (float) char.MinValue || Value > (float) char.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) DoubleValue, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) DoubleValue, Location); + } + + return null; + } + + } + + public class DoubleConstant : Constant + { + public readonly double Value; + + public DoubleConstant (BuiltinTypes types, double v, Location loc) + : this (types.Double, v, loc) + { + } + + public DoubleConstant (TypeSpec type, double v, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldc_R8, Value); + } + + public override object GetValue () + { + return Value; + } + + public override string GetValueAsLiteral () + { + return Value.ToString (); + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < SByte.MinValue || Value > SByte.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value < short.MinValue || Value > short.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < ushort.MinValue || Value > ushort.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context) { + if (Value < int.MinValue || Value > int.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context) { + if (Value < uint.MinValue || Value > uint.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + if (in_checked_context) { + if (Value < long.MinValue || Value > long.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context) { + if (Value < ulong.MinValue || Value > ulong.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < (double) char.MinValue || Value > (double) char.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class DecimalConstant : Constant { + public readonly decimal Value; + + public DecimalConstant (BuiltinTypes types, decimal d, Location loc) + : this (types.Decimal, d, loc) + { + } + + public DecimalConstant (TypeSpec type, decimal d, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + Value = d; + } + + public override void Emit (EmitContext ec) + { + MethodSpec m; + + int [] words = decimal.GetBits (Value); + int power = (words [3] >> 16) & 0xff; + + if (power == 0) { + if (Value <= int.MaxValue && Value >= int.MinValue) { + m = ec.Module.PredefinedMembers.DecimalCtorInt.Resolve (loc); + if (m == null) { + return; + } + + ec.EmitInt ((int) Value); + ec.Emit (OpCodes.Newobj, m); + return; + } + + if (Value <= long.MaxValue && Value >= long.MinValue) { + m = ec.Module.PredefinedMembers.DecimalCtorLong.Resolve (loc); + if (m == null) { + return; + } + + ec.EmitLong ((long) Value); + ec.Emit (OpCodes.Newobj, m); + return; + } + } + + ec.EmitInt (words [0]); + ec.EmitInt (words [1]); + ec.EmitInt (words [2]); + + // sign + ec.EmitInt (words [3] >> 31); + + // power + ec.EmitInt (power); + + m = ec.Module.PredefinedMembers.DecimalCtor.Resolve (loc); + if (m != null) { + ec.Emit (OpCodes.Newobj, m); + } + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new SByteConstant (target_type, (sbyte) Value, loc); + case BuiltinTypeSpec.Type.Byte: + return new ByteConstant (target_type, (byte) Value, loc); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (target_type, (short) Value, loc); + case BuiltinTypeSpec.Type.UShort: + return new UShortConstant (target_type, (ushort) Value, loc); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, loc); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (target_type, (uint) Value, loc); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, loc); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, loc); + case BuiltinTypeSpec.Type.Char: + return new CharConstant (target_type, (char) Value, loc); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, loc); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, loc); + } + + return null; + } + + public override object GetValue () + { + return Value; + } + + public override string GetValueAsLiteral () + { + return Value.ToString () + "M"; + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + } + + public class StringConstant : Constant { + public StringConstant (BuiltinTypes types, string s, Location loc) + : this (types.String, s, loc) + { + } + + public StringConstant (TypeSpec type, string s, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + Value = s; + } + + protected StringConstant (Location loc) + : base (loc) + { + } + + public string Value { get; protected set; } + + public override object GetValue () + { + return Value; + } + + public override string GetValueAsLiteral () + { + // FIXME: Escape the string. + return "\"" + Value + "\""; + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + + public override void Emit (EmitContext ec) + { + if (Value == null) { + ec.EmitNull (); + return; + } + + // + // Use string.Empty for both literals and constants even if + // it's not allowed at language level + // + if (Value.Length == 0 && ec.Module.Compiler.Settings.Optimize) { + var string_type = ec.BuiltinTypes.String; + if (ec.CurrentType != string_type) { + var m = ec.Module.PredefinedMembers.StringEmpty.Get (); + if (m != null) { + ec.Emit (OpCodes.Ldsfld, m); + return; + } + } + } + + ec.Emit (OpCodes.Ldstr, Value); + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // cast to object + if (type != targetType) + enc.Encode (type); + + enc.Encode (Value); + } + + public override bool IsDefaultValue { + get { + return Value == null; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsNull { + get { + return IsDefaultValue; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + return null; + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (IsDefaultValue && type.BuiltinType == BuiltinTypeSpec.Type.Object) + return new NullConstant (type, loc); + + return base.ConvertImplicitly (type); + } + } + + class NameOf : StringConstant + { + readonly SimpleName name; + + public NameOf (SimpleName name) + : base (name.Location) + { + this.name = name; + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotSupportedException (); + } + + bool ResolveArgumentExpression (ResolveContext rc, Expression expr) + { + var sn = expr as SimpleName; + if (sn != null) { + Value = sn.Name; + + if (rc.Module.Compiler.Settings.Version < LanguageVersion.V_6) + rc.Report.FeatureIsNotAvailable (rc.Module.Compiler, Location, "nameof operator"); + + if (sn.HasTypeArguments) { + // TODO: csc compatible but unhelpful error message + rc.Report.Error (1001, loc, "Identifier expected"); + return true; + } + + sn.LookupNameExpression (rc, MemberLookupRestrictions.IgnoreArity | MemberLookupRestrictions.IgnoreAmbiguity); + return true; + } + + var ma = expr as MemberAccess; + if (ma != null) { + FullNamedExpression fne = ma.LeftExpression as ATypeNameExpression; + if (fne == null) { + var qam = ma as QualifiedAliasMember; + if (qam == null) + return false; + + fne = qam.CreateExpressionFromAlias (rc); + if (fne == null) + return true; + } + + Value = ma.Name; + + if (rc.Module.Compiler.Settings.Version < LanguageVersion.V_6) + rc.Report.FeatureIsNotAvailable (rc.Module.Compiler, Location, "nameof operator"); + + if (ma.HasTypeArguments) { + // TODO: csc compatible but unhelpful error message + rc.Report.Error (1001, loc, "Identifier expected"); + return true; + } + + var left = fne.ResolveAsTypeOrNamespace (rc, true); + if (left == null) + return true; + + var ns = left as NamespaceExpression; + if (ns != null) { + FullNamedExpression retval = ns.LookupTypeOrNamespace (rc, ma.Name, 0, LookupMode.NameOf, loc); + if (retval == null) + ns.Error_NamespaceDoesNotExist (rc, ma.Name, 0); + + return true; + } + + if (left.Type.IsGenericOrParentIsGeneric && left.Type.GetDefinition () != left.Type) { + rc.Report.Error (8071, loc, "Type arguments are not allowed in the nameof operator"); + } + + var mexpr = MemberLookup (rc, false, left.Type, ma.Name, 0, MemberLookupRestrictions.IgnoreArity | MemberLookupRestrictions.IgnoreAmbiguity, loc); + if (mexpr == null) { + ma.Error_IdentifierNotFound (rc, left.Type); + return true; + } + + return true; + } + + return false; + } + + public Expression ResolveOverload (ResolveContext rc, Arguments args) + { + if (args == null || args.Count != 1) { + name.Error_NameDoesNotExist (rc); + return null; + } + + var arg = args [0]; + var res = ResolveArgumentExpression (rc, arg.Expr); + if (!res) { + name.Error_NameDoesNotExist (rc); + return null; + } + + type = rc.BuiltinTypes.String; + eclass = ExprClass.Value; + return this; + } + } + + // + // Null constant can have its own type, think of `default (Foo)' + // + public class NullConstant : Constant + { + public NullConstant (TypeSpec type, Location loc) + : base (loc) + { + eclass = ExprClass.Value; + this.type = type; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (type == InternalType.NullLiteral || type.BuiltinType == BuiltinTypeSpec.Type.Object) { + // Optimized version, also avoids referencing literal internal type + Arguments args = new Arguments (1); + args.Add (new Argument (this)); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + return base.CreateExpressionTree (ec); + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + switch (targetType.BuiltinType) { + case BuiltinTypeSpec.Type.Object: + // Type it as string cast + enc.Encode (rc.Module.Compiler.BuiltinTypes.String); + goto case BuiltinTypeSpec.Type.String; + case BuiltinTypeSpec.Type.String: + case BuiltinTypeSpec.Type.Type: + enc.Encode (byte.MaxValue); + return; + default: + var ac = targetType as ArrayContainer; + if (ac != null && ac.Rank == 1 && !ac.Element.IsArray) { + enc.Encode (uint.MaxValue); + return; + } + + break; + } + + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + } + + public override void Emit (EmitContext ec) + { + ec.EmitNull (); + + // Only to make verifier happy + if (type.IsGenericParameter) + ec.Emit (OpCodes.Unbox_Any, type); + } + + public override string ExprClassName { + get { + return GetSignatureForError (); + } + } + + public override Constant ConvertExplicitly (bool inCheckedContext, TypeSpec targetType) + { + if (targetType.IsPointer) { + if (IsLiteral || this is NullPointer) + return new NullPointer (targetType, loc); + + return null; + } + + // Exlude internal compiler types + if (targetType.Kind == MemberKind.InternalCompilerType && targetType.BuiltinType != BuiltinTypeSpec.Type.Dynamic) + return null; + + if (!IsLiteral && !Convert.ImplicitStandardConversionExists (this, targetType)) + return null; + + if (TypeSpec.IsReferenceType (targetType)) + return new NullConstant (targetType, loc); + + if (targetType.IsNullableType) + return Nullable.LiftedNull.Create (targetType, loc); + + return null; + } + + public override Constant ConvertImplicitly (TypeSpec targetType) + { + return ConvertExplicitly (false, targetType); + } + + public override string GetSignatureForError () + { + return "null"; + } + + public override object GetValue () + { + return null; + } + + public override string GetValueAsLiteral () + { + return GetSignatureForError (); + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + + public override bool IsDefaultValue { + get { return true; } + } + + public override bool IsNegative { + get { return false; } + } + + public override bool IsNull { + get { return true; } + } + + public override bool IsZeroInteger { + get { return true; } + } + } + + + // + // A null constant in a pointer context + // + class NullPointer : NullConstant + { + public NullPointer (TypeSpec type, Location loc) + : base (type, loc) + { + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return base.CreateExpressionTree (ec); + } + + public override void Emit (EmitContext ec) + { + // + // Emits null pointer + // + ec.EmitInt (0); + ec.Emit (OpCodes.Conv_U); + } + } + + /// + /// The value is constant, but when emitted has a side effect. This is + /// used by BitwiseAnd to ensure that the second expression is invoked + /// regardless of the value of the left side. + /// + public class SideEffectConstant : Constant + { + public readonly Constant value; + Expression side_effect; + + public SideEffectConstant (Constant value, Expression side_effect, Location loc) + : base (loc) + { + this.value = value; + type = value.Type; + eclass = ExprClass.Value; + + while (side_effect is SideEffectConstant) + side_effect = ((SideEffectConstant) side_effect).side_effect; + this.side_effect = side_effect; + } + + public override bool IsSideEffectFree { + get { + return false; + } + } + + public override bool ContainsEmitWithAwait () + { + return side_effect.ContainsEmitWithAwait (); + } + + public override object GetValue () + { + return value.GetValue (); + } + + public override string GetValueAsLiteral () + { + return value.GetValueAsLiteral (); + } + + public override long GetValueAsLong () + { + return value.GetValueAsLong (); + } + + public override void Emit (EmitContext ec) + { + side_effect.EmitSideEffect (ec); + value.Emit (ec); + } + + public override void EmitSideEffect (EmitContext ec) + { + side_effect.EmitSideEffect (ec); + value.EmitSideEffect (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + side_effect.FlowAnalysis (fc); + } + + public override bool IsDefaultValue { + get { return value.IsDefaultValue; } + } + + public override bool IsNegative { + get { return value.IsNegative; } + } + + public override bool IsZeroInteger { + get { return value.IsZeroInteger; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + Constant new_value = value.ConvertExplicitly (in_checked_context, target_type); + if (new_value == null) + return null; + + var c = new SideEffectConstant (new_value, side_effect, new_value.Location); + c.type = target_type; + c.eclass = eclass; + return c; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/context.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/context.cs new file mode 100644 index 000000000..230aa1257 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/context.cs @@ -0,0 +1,751 @@ +// +// context.cs: Various compiler contexts. +// +// Author: +// Marek Safar (marek.safar@gmail.com) +// Miguel de Icaza (miguel@ximian.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004-2009 Novell, Inc. +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Diagnostics; + +namespace Mono.CSharp +{ + public enum LookupMode + { + Normal = 0, + Probing = 1, + IgnoreAccessibility = 2, + NameOf = 3 + } + + // + // Implemented by elements which can act as independent contexts + // during resolve phase. Used mostly for lookups. + // + public interface IMemberContext : IModuleContext + { + // + // A scope type context, it can be inflated for generic types + // + TypeSpec CurrentType { get; } + + // + // A scope type parameters either VAR or MVAR + // + TypeParameters CurrentTypeParameters { get; } + + // + // A member definition of the context. For partial types definition use + // CurrentTypeDefinition.PartialContainer otherwise the context is local + // + // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value + // + MemberCore CurrentMemberDefinition { get; } + + bool IsObsolete { get; } + bool IsUnsafe { get; } + bool IsStatic { get; } + + string GetSignatureForError (); + + ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity); + FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc); + FullNamedExpression LookupNamespaceAlias (string name); + } + + public interface IModuleContext + { + ModuleContainer Module { get; } + } + + // + // Block or statement resolving context + // + public class BlockContext : ResolveContext + { + readonly TypeSpec return_type; + + // + // Tracks the last offset used by VariableInfo + // + public int AssignmentInfoOffset; + + public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType) + : base (mc) + { + if (returnType == null) + throw new ArgumentNullException ("returnType"); + + this.return_type = returnType; + + // TODO: check for null value + CurrentBlock = block; + } + + public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType) + : this (rc.MemberContext, block, returnType) + { + if (rc.IsUnsafe) + flags |= ResolveContext.Options.UnsafeScope; + + if (rc.HasSet (ResolveContext.Options.CheckedScope)) + flags |= ResolveContext.Options.CheckedScope; + + if (!rc.ConstantCheckState) + flags &= ~Options.ConstantCheckState; + + if (rc.IsInProbingMode) + flags |= ResolveContext.Options.ProbingMode; + + if (rc.HasSet (ResolveContext.Options.FieldInitializerScope)) + flags |= ResolveContext.Options.FieldInitializerScope; + + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) + flags |= ResolveContext.Options.ExpressionTreeConversion; + + if (rc.HasSet (ResolveContext.Options.BaseInitializer)) + flags |= ResolveContext.Options.BaseInitializer; + } + + public ExceptionStatement CurrentTryBlock { get; set; } + + public LoopStatement EnclosingLoop { get; set; } + + public LoopStatement EnclosingLoopOrSwitch { get; set; } + + public Switch Switch { get; set; } + + public TypeSpec ReturnType { + get { return return_type; } + } + } + + // + // Expression resolving context + // + public class ResolveContext : IMemberContext + { + [Flags] + public enum Options + { + /// + /// This flag tracks the `checked' state of the compilation, + /// it controls whether we should generate code that does overflow + /// checking, or if we generate code that ignores overflows. + /// + /// The default setting comes from the command line option to generate + /// checked or unchecked code plus any source code changes using the + /// checked/unchecked statements or expressions. Contrast this with + /// the ConstantCheckState flag. + /// + CheckedScope = 1 << 0, + + /// + /// The constant check state is always set to `true' and cant be changed + /// from the command line. The source code can change this setting with + /// the `checked' and `unchecked' statements and expressions. + /// + ConstantCheckState = 1 << 1, + + AllCheckStateFlags = CheckedScope | ConstantCheckState, + + // + // unsafe { ... } scope + // + UnsafeScope = 1 << 2, + CatchScope = 1 << 3, + FinallyScope = 1 << 4, + FieldInitializerScope = 1 << 5, + CompoundAssignmentScope = 1 << 6, + FixedInitializerScope = 1 << 7, + BaseInitializer = 1 << 8, + + // + // Inside an enum definition, we do not resolve enumeration values + // to their enumerations, but rather to the underlying type/value + // This is so EnumVal + EnumValB can be evaluated. + // + // There is no "E operator + (E x, E y)", so during an enum evaluation + // we relax the rules + // + EnumScope = 1 << 9, + + ConstantScope = 1 << 10, + + ConstructorScope = 1 << 11, + + UsingInitializerScope = 1 << 12, + + LockScope = 1 << 13, + + TryScope = 1 << 14, + + TryWithCatchScope = 1 << 15, + + ConditionalAccessReceiver = 1 << 16, + + /// + /// Indicates the current context is in probing mode, no errors are reported. + /// + ProbingMode = 1 << 22, + + // + // Return and ContextualReturn statements will set the ReturnType + // value based on the expression types of each return statement + // instead of the method return type which is initially null. + // + InferReturnType = 1 << 23, + + OmitDebuggingInfo = 1 << 24, + + ExpressionTreeConversion = 1 << 25, + + InvokeSpecialName = 1 << 26 + } + + // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements + // it's public so that we can use a struct at the callsite + public struct FlagsHandle : IDisposable + { + readonly ResolveContext ec; + readonly Options invmask, oldval; + + public FlagsHandle (ResolveContext ec, Options flagsToSet) + : this (ec, flagsToSet, flagsToSet) + { + } + + internal FlagsHandle (ResolveContext ec, Options mask, Options val) + { + this.ec = ec; + invmask = ~mask; + oldval = ec.flags & mask; + ec.flags = (ec.flags & invmask) | (val & mask); + +// if ((mask & Options.ProbingMode) != 0) +// ec.Report.DisableReporting (); + } + + public void Dispose () + { +// if ((invmask & Options.ProbingMode) == 0) +// ec.Report.EnableReporting (); + + ec.flags = (ec.flags & invmask) | oldval; + } + } + + protected Options flags; + + // + // Whether we are inside an anonymous method. + // + public AnonymousExpression CurrentAnonymousMethod; + + // + // Holds a varible used during collection or object initialization. + // + public Expression CurrentInitializerVariable; + + public Block CurrentBlock; + + public readonly IMemberContext MemberContext; + + public ResolveContext (IMemberContext mc) + { + if (mc == null) + throw new ArgumentNullException (); + + MemberContext = mc; + + // + // The default setting comes from the command line option + // + if (mc.Module.Compiler.Settings.Checked) + flags |= Options.CheckedScope; + + // + // The constant check state is always set to true + // + flags |= Options.ConstantCheckState; + } + + public ResolveContext (IMemberContext mc, Options options) + : this (mc) + { + flags |= options; + } + + #region Properties + + public BuiltinTypes BuiltinTypes { + get { + return MemberContext.Module.Compiler.BuiltinTypes; + } + } + + public virtual ExplicitBlock ConstructorBlock { + get { + return CurrentBlock.Explicit; + } + } + + // + // The current iterator + // + public Iterator CurrentIterator { + get { return CurrentAnonymousMethod as Iterator; } + } + + public TypeSpec CurrentType { + get { return MemberContext.CurrentType; } + } + + public TypeParameters CurrentTypeParameters { + get { return MemberContext.CurrentTypeParameters; } + } + + public MemberCore CurrentMemberDefinition { + get { return MemberContext.CurrentMemberDefinition; } + } + + public bool ConstantCheckState { + get { return (flags & Options.ConstantCheckState) != 0; } + } + + public bool IsInProbingMode { + get { + return (flags & Options.ProbingMode) != 0; + } + } + + public bool IsObsolete { + get { + // Disables obsolete checks when probing is on + return MemberContext.IsObsolete; + } + } + + public bool IsStatic { + get { + return MemberContext.IsStatic; + } + } + + public bool IsUnsafe { + get { + return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; + } + } + + public bool IsRuntimeBinder { + get { + return Module.Compiler.IsRuntimeBinder; + } + } + + public bool IsVariableCapturingRequired { + get { + return !IsInProbingMode; + } + } + + public ModuleContainer Module { + get { + return MemberContext.Module; + } + } + + public Report Report { + get { + return Module.Compiler.Report; + } + } + + #endregion + + public bool MustCaptureVariable (INamedBlockVariable local) + { + if (CurrentAnonymousMethod == null) + return false; + + // + // Capture only if this or any of child blocks contain yield + // or it's a parameter + // + if (CurrentAnonymousMethod.IsIterator) + return local.IsParameter || local.Block.Explicit.HasYield; + + // + // Capture only if this or any of child blocks contain await + // or it's a parameter or we need to access variable from + // different parameter block + // + if (CurrentAnonymousMethod is AsyncInitializer) + return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait || + local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original; + + return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original; + } + + public bool HasSet (Options options) + { + return (this.flags & options) == options; + } + + public bool HasAny (Options options) + { + return (this.flags & options) != 0; + } + + + // Temporarily set all the given flags to the given value. Should be used in an 'using' statement + public FlagsHandle Set (Options options) + { + return new FlagsHandle (this, options); + } + + public FlagsHandle With (Options options, bool enable) + { + return new FlagsHandle (this, options, enable ? options : 0); + } + + #region IMemberContext Members + + public string GetSignatureForError () + { + return MemberContext.GetSignatureForError (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + return MemberContext.LookupExtensionMethod (extensionType, name, arity); + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + return MemberContext.LookupNamespaceOrType (name, arity, mode, loc); + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + return MemberContext.LookupNamespaceAlias (name); + } + + #endregion + } + + public class FlowAnalysisContext + { + readonly CompilerContext ctx; + DefiniteAssignmentBitSet conditional_access; + + public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength) + { + this.ctx = ctx; + this.ParametersBlock = parametersBlock; + + DefiniteAssignment = definiteAssignmentLength == 0 ? + DefiniteAssignmentBitSet.Empty : + new DefiniteAssignmentBitSet (definiteAssignmentLength); + } + + public DefiniteAssignmentBitSet DefiniteAssignment { get; set; } + + public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; } + + public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; } + + public List LabelStack { get; set; } + + public ParametersBlock ParametersBlock { get; set; } + + public Report Report { + get { + return ctx.Report; + } + } + + public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; } + + public TryFinally TryFinally { get; set; } + + public bool UnreachableReported { get; set; } + + public DefiniteAssignmentBitSet BranchDefiniteAssignment () + { + var dat = DefiniteAssignment; + if (dat != DefiniteAssignmentBitSet.Empty) + DefiniteAssignment = new DefiniteAssignmentBitSet (dat); + return dat; + } + + public void BranchConditionalAccessDefiniteAssignment () + { + if (conditional_access == null) + conditional_access = BranchDefiniteAssignment (); + } + + public void ConditionalAccessEnd () + { + Debug.Assert (conditional_access != null); + DefiniteAssignment = conditional_access; + conditional_access = null; + } + + public bool IsDefinitelyAssigned (VariableInfo variable) + { + return variable.IsAssigned (DefiniteAssignment); + } + + public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name) + { + return variable.IsStructFieldAssigned (DefiniteAssignment, name); + } + + public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false) + { + variable.SetAssigned (DefiniteAssignment, generatedAssignment); + } + + public void SetStructFieldAssigned (VariableInfo variable, string name) + { + variable.SetStructFieldAssigned (DefiniteAssignment, name); + } + } + + + // + // This class is used during the Statement.Clone operation + // to remap objects that have been cloned. + // + // Since blocks are cloned by Block.Clone, we need a way for + // expressions that must reference the block to be cloned + // pointing to the new cloned block. + // + public class CloneContext + { + Dictionary block_map = new Dictionary (); + + public void AddBlockMap (Block from, Block to) + { + block_map.Add (from, to); + } + + public Block LookupBlock (Block from) + { + Block result; + if (!block_map.TryGetValue (from, out result)) { + result = (Block) from.Clone (this); + } + + return result; + } + + /// + /// Remaps block to cloned copy if one exists. + /// + public Block RemapBlockCopy (Block from) + { + Block mapped_to; + if (!block_map.TryGetValue (from, out mapped_to)) + return from; + + return mapped_to; + } + } + + // + // Main compiler context + // + public class CompilerContext + { + static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false); + + readonly Report report; + readonly BuiltinTypes builtin_types; + readonly CompilerSettings settings; + + Dictionary all_source_files; + + public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter) + { + this.settings = settings; + this.report = new Report (this, reportPrinter); + this.builtin_types = new BuiltinTypes (); + this.TimeReporter = DisabledTimeReporter; + } + + #region Properties + + public BuiltinTypes BuiltinTypes { + get { + return builtin_types; + } + } + + // Used for special handling of runtime dynamic context mostly + // by error reporting but also by member accessibility checks + public bool IsRuntimeBinder { + get; set; + } + + public Report Report { + get { + return report; + } + } + + public CompilerSettings Settings { + get { + return settings; + } + } + + public List SourceFiles { + get { + return settings.SourceFiles; + } + } + + internal TimeReporter TimeReporter { + get; set; + } + + #endregion + + // + // This is used when we encounter a #line preprocessing directive during parsing + // to register additional source file names + // + public SourceFile LookupFile (CompilationSourceFile comp_unit, string name) + { + if (all_source_files == null) { + all_source_files = new Dictionary (); + foreach (var source in SourceFiles) + all_source_files[source.FullPathName] = source; + } + + string path; + if (!Path.IsPathRooted (name)) { + var loc = comp_unit.SourceFile; + string root = Path.GetDirectoryName (loc.FullPathName); + path = Path.GetFullPath (Path.Combine (root, name)); + var dir = Path.GetDirectoryName (loc.Name); + if (!string.IsNullOrEmpty (dir)) + name = Path.Combine (dir, name); + } else + path = name; + + SourceFile retval; + if (all_source_files.TryGetValue (path, out retval)) + return retval; + + retval = new SourceFile (name, path, all_source_files.Count + 1); + Location.AddFile (retval); + all_source_files.Add (path, retval); + return retval; + } + } + + // + // Generic code emitter context + // + public class BuilderContext + { + [Flags] + public enum Options + { + /// + /// This flag tracks the `checked' state of the compilation, + /// it controls whether we should generate code that does overflow + /// checking, or if we generate code that ignores overflows. + /// + /// The default setting comes from the command line option to generate + /// checked or unchecked code plus any source code changes using the + /// checked/unchecked statements or expressions. Contrast this with + /// the ConstantCheckState flag. + /// + CheckedScope = 1 << 0, + + AccurateDebugInfo = 1 << 1, + + OmitDebugInfo = 1 << 2, + + ConstructorScope = 1 << 3, + + AsyncBody = 1 << 4, + } + + // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements + // it's public so that we can use a struct at the callsite + public struct FlagsHandle : IDisposable + { + readonly BuilderContext ec; + readonly Options invmask, oldval; + + public FlagsHandle (BuilderContext ec, Options flagsToSet) + : this (ec, flagsToSet, flagsToSet) + { + } + + internal FlagsHandle (BuilderContext ec, Options mask, Options val) + { + this.ec = ec; + invmask = ~mask; + oldval = ec.flags & mask; + ec.flags = (ec.flags & invmask) | (val & mask); + } + + public void Dispose () + { + ec.flags = (ec.flags & invmask) | oldval; + } + } + + protected Options flags; + + public bool HasSet (Options options) + { + return (this.flags & options) == options; + } + + // Temporarily set all the given flags to the given value. Should be used in an 'using' statement + public FlagsHandle With (Options options, bool enable) + { + return new FlagsHandle (this, options, enable ? options : 0); + } + } + + // + // Parser session objects. We could recreate all these objects for each parser + // instance but the best parser performance the session object can be reused + // + public class ParserSession + { + MD5 md5; + + public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2]; + public readonly Dictionary[] Identifiers = new Dictionary[Tokenizer.MaxIdentifierLength + 1]; + public readonly List ParametersStack = new List (4); + public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength]; + public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength]; + + public LocationsBag LocationsBag { get; set; } + public bool UseJayGlobalArrays { get; set; } + public LocatedToken[] LocatedTokens { get; set; } + + public MD5 GetChecksumAlgorithm () + { + return md5 ?? (md5 = MD5.Create ()); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/convert.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/convert.cs new file mode 100644 index 000000000..065c9a7e3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/convert.cs @@ -0,0 +1,2266 @@ +// +// conversion.cs: various routines for implementing conversions. +// +// Authors: +// Miguel de Icaza (miguel@ximian.com) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc (http://www.xamarin.com) +// + +using System; +using System.Collections.Generic; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + // + // A container class for all the conversion operations + // + static class Convert + { + [Flags] + public enum UserConversionRestriction + { + None = 0, + ImplicitOnly = 1, + ProbingOnly = 1 << 1, + NullableSourceOnly = 1 << 2 + + } + // + // From a one-dimensional array-type S[] to System.Collections.IList and base + // interfaces of this interface, provided there is an implicit reference conversion + // from S to T. + // + static bool ArrayToIList (ArrayContainer array, TypeSpec list, bool isExplicit) + { + if (array.Rank != 1 || !list.IsArrayGenericInterface) + return false; + + var arg_type = list.TypeArguments[0]; + if (array.Element == arg_type) + return true; + + // + // Reject conversion from T[] to IList even if T has U dependency + // + if (arg_type.IsGenericParameter) + return false; + + if (isExplicit) + return ExplicitReferenceConversionExists (array.Element, arg_type); + + return ImplicitReferenceConversionExists (array.Element, arg_type); + } + + static bool IList_To_Array(TypeSpec list, ArrayContainer array) + { + if (array.Rank != 1 || !list.IsArrayGenericInterface) + return false; + + var arg_type = list.TypeArguments[0]; + if (array.Element == arg_type) + return true; + + return ImplicitReferenceConversionExists (array.Element, arg_type) || ExplicitReferenceConversionExists (array.Element, arg_type); + } + + public static Expression ImplicitTypeParameterConversion (Expression expr, TypeParameterSpec expr_type, TypeSpec target_type) + { + // + // From T to a type parameter U, provided T depends on U + // + if (target_type.IsGenericParameter) { + if (expr_type.TypeArguments != null && expr_type.HasDependencyOn (target_type)) { + if (expr == null) + return EmptyExpression.Null; + + if (expr_type.IsReferenceType && !((TypeParameterSpec) target_type).IsReferenceType) + return new BoxedCast (expr, target_type); + + return new ClassCast (expr, target_type); + } + + return null; + } + + // + // LAMESPEC: From T to dynamic type because it's like T to object + // + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + if (expr == null) + return EmptyExpression.Null; + + if (expr_type.IsReferenceType) + return new ClassCast (expr, target_type); + + return new BoxedCast (expr, target_type); + } + + // + // From T to its effective base class C + // From T to any base class of C (it cannot contain dynamic or be of dynamic type) + // From T to any interface implemented by C + // + var base_type = expr_type.GetEffectiveBase (); + if (base_type == target_type || TypeSpec.IsBaseClass (base_type, target_type, false) || base_type.ImplementsInterface (target_type, true)) { + if (expr == null) + return EmptyExpression.Null; + + if (expr_type.IsReferenceType) + return new ClassCast (expr, target_type); + + return new BoxedCast (expr, target_type); + } + + if (target_type.IsInterface && expr_type.IsConvertibleToInterface (target_type)) { + if (expr == null) + return EmptyExpression.Null; + + if (expr_type.IsReferenceType) + return new ClassCast (expr, target_type); + + return new BoxedCast (expr, target_type); + } + + return null; + } + + static Expression ExplicitTypeParameterConversionFromT (Expression source, TypeSpec source_type, TypeSpec target_type) + { + var target_tp = target_type as TypeParameterSpec; + if (target_tp != null) { + // + // From a type parameter U to T, provided T depends on U + // + if (target_tp.TypeArguments != null && target_tp.HasDependencyOn (source_type)) { + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + } + } + + // + // From T to any interface-type I provided there is not already an implicit conversion from T to I + // + if (target_type.IsInterface) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type, true); + + return null; + } + + static Expression ExplicitTypeParameterConversionToT (Expression source, TypeSpec source_type, TypeParameterSpec target_type) + { + // + // From the effective base class C of T to T and from any base class of C to T + // + var effective = target_type.GetEffectiveBase (); + if (TypeSpecComparer.IsEqual (effective, source_type) || TypeSpec.IsBaseClass (effective, source_type, false)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + return null; + } + + public static Expression ImplicitReferenceConversion (Expression expr, TypeSpec target_type, bool explicit_cast) + { + TypeSpec expr_type = expr.Type; + + if (expr_type.Kind == MemberKind.TypeParameter) + return ImplicitTypeParameterConversion (expr, (TypeParameterSpec) expr.Type, target_type); + + // + // from the null type to any reference-type. + // + NullLiteral nl = expr as NullLiteral; + if (nl != null) { + return nl.ConvertImplicitly (target_type); + } + + if (ImplicitReferenceConversionExists (expr_type, target_type)) { + // + // Avoid wrapping implicitly convertible reference type + // + if (!explicit_cast) + return expr; + + return EmptyCast.Create (expr, target_type); + } + + return null; + } + + // + // Implicit reference conversions + // + public static bool ImplicitReferenceConversionExists (TypeSpec expr_type, TypeSpec target_type) + { + return ImplicitReferenceConversionExists (expr_type, target_type, true); + } + + public static bool ImplicitReferenceConversionExists (TypeSpec expr_type, TypeSpec target_type, bool refOnlyTypeParameter) + { + // It's here only to speed things up + if (target_type.IsStruct) + return false; + + switch (expr_type.Kind) { + case MemberKind.TypeParameter: + return ImplicitTypeParameterConversion (null, (TypeParameterSpec) expr_type, target_type) != null && + (!refOnlyTypeParameter || TypeSpec.IsReferenceType (expr_type)); + + case MemberKind.Class: + // + // From any class-type to dynamic (+object to speed up common path) + // + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Object || target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + + if (target_type.IsClass) { + // + // Identity conversion, including dynamic erasure + // + if (TypeSpecComparer.IsEqual (expr_type, target_type)) + return true; + + // + // From any class-type S to any class-type T, provided S is derived from T + // + return TypeSpec.IsBaseClass (expr_type, target_type, true); + } + + // + // From any class-type S to any interface-type T, provided S implements T + // + if (target_type.IsInterface) + return expr_type.ImplementsInterface (target_type, true); + + return false; + + case MemberKind.ArrayType: + // + // Identity array conversion + // + if (expr_type == target_type) + return true; + + // + // From any array-type to System.Array + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Array: + case BuiltinTypeSpec.Type.Object: + case BuiltinTypeSpec.Type.Dynamic: + return true; + } + + var expr_type_array = (ArrayContainer) expr_type; + var target_type_array = target_type as ArrayContainer; + + // + // From an array-type S to an array-type of type T + // + if (target_type_array != null && expr_type_array.Rank == target_type_array.Rank) { + + // + // Both SE and TE are reference-types. TE check is defered + // to ImplicitReferenceConversionExists + // + TypeSpec expr_element_type = expr_type_array.Element; + if (!TypeSpec.IsReferenceType (expr_element_type)) + return false; + + // + // An implicit reference conversion exists from SE to TE + // + return ImplicitReferenceConversionExists (expr_element_type, target_type_array.Element); + } + + // + // From any array-type to the interfaces it implements + // + if (target_type.IsInterface) { + if (expr_type.ImplementsInterface (target_type, false)) + return true; + + // from an array-type of type T to IList + if (ArrayToIList (expr_type_array, target_type, false)) + return true; + } + + return false; + + case MemberKind.Delegate: + // + // From any delegate-type to System.Delegate (and its base types) + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Delegate: + case BuiltinTypeSpec.Type.MulticastDelegate: + case BuiltinTypeSpec.Type.Object: + case BuiltinTypeSpec.Type.Dynamic: + return true; + } + + // + // Identity conversion, including dynamic erasure + // + if (TypeSpecComparer.IsEqual (expr_type, target_type)) + return true; + + // + // From any delegate-type to the interfaces it implements + // From any reference-type to an delegate type if is variance-convertible + // + return expr_type.ImplementsInterface (target_type, false) || TypeSpecComparer.Variant.IsEqual (expr_type, target_type); + + case MemberKind.Interface: + // + // Identity conversion, including dynamic erasure + // + if (TypeSpecComparer.IsEqual (expr_type, target_type)) + return true; + + // + // From any interface type S to interface-type T + // From any reference-type to an interface if is variance-convertible + // + if (target_type.IsInterface) + return TypeSpecComparer.Variant.IsEqual (expr_type, target_type) || expr_type.ImplementsInterface (target_type, true); + + return target_type.BuiltinType == BuiltinTypeSpec.Type.Object || target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + + case MemberKind.InternalCompilerType: + // + // from the null literal to any reference-type. + // + if (expr_type == InternalType.NullLiteral) { + // Exlude internal compiler types + if (target_type.Kind == MemberKind.InternalCompilerType) + return target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + + return TypeSpec.IsReferenceType (target_type) || target_type.Kind == MemberKind.PointerType; + } + + // + // Implicit dynamic conversion + // + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + switch (target_type.Kind) { + case MemberKind.ArrayType: + case MemberKind.Class: + case MemberKind.Delegate: + case MemberKind.Interface: + case MemberKind.TypeParameter: + return true; + } + + // dynamic to __arglist + if (target_type == InternalType.Arglist) + return true; + + return false; + } + + break; + } + + return false; + } + + public static Expression ImplicitBoxingConversion (Expression expr, TypeSpec expr_type, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + // + // From any non-nullable-value-type to the type object and dynamic + // + case BuiltinTypeSpec.Type.Object: + case BuiltinTypeSpec.Type.Dynamic: + // + // From any non-nullable-value-type to the type System.ValueType + // + case BuiltinTypeSpec.Type.ValueType: + // + // No ned to check for nullable type as underlying type is always convertible + // + if (!TypeSpec.IsValueType (expr_type)) + return null; + + return expr == null ? EmptyExpression.Null : new BoxedCast (expr, target_type); + + case BuiltinTypeSpec.Type.Enum: + // + // From any enum-type to the type System.Enum. + // + if (expr_type.IsEnum) + return expr == null ? EmptyExpression.Null : new BoxedCast (expr, target_type); + + break; + } + + // + // From a nullable-type to a reference type, if a boxing conversion exists from + // the underlying type to the reference type + // + if (expr_type.IsNullableType) { + if (!TypeSpec.IsReferenceType (target_type)) + return null; + + var res = ImplicitBoxingConversion (expr, Nullable.NullableInfo.GetUnderlyingType (expr_type), target_type); + + // "cast" underlying type to target type to emit correct InvalidCastException when + // underlying hierarchy changes without recompilation + if (res != null && expr != null) + res = new UnboxCast (res, target_type); + + return res; + } + + // + // A value type has a boxing conversion to an interface type I if it has a boxing conversion + // to an interface or delegate type I0 and I0 is variance-convertible to I + // + if (target_type.IsInterface && TypeSpec.IsValueType (expr_type) && expr_type.ImplementsInterface (target_type, true)) { + return expr == null ? EmptyExpression.Null : new BoxedCast (expr, target_type); + } + + return null; + } + + public static Expression ImplicitNulableConversion (ResolveContext ec, Expression expr, TypeSpec target_type) + { + TypeSpec expr_type = expr.Type; + + // + // From null to any nullable type + // + if (expr_type == InternalType.NullLiteral) + return ec == null ? EmptyExpression.Null : Nullable.LiftedNull.Create (target_type, expr.Location); + + // S -> T? + TypeSpec t_el = Nullable.NullableInfo.GetUnderlyingType (target_type); + + // S? -> T? + if (expr_type.IsNullableType) + expr_type = Nullable.NullableInfo.GetUnderlyingType (expr_type); + + // + // Predefined implicit identity or implicit numeric conversion + // has to exist between underlying type S and underlying type T + // + + // conversion exists only mode + if (ec == null) { + if (TypeSpecComparer.IsEqual (expr_type, t_el)) + return EmptyExpression.Null; + + if (expr is Constant) + return ((Constant) expr).ConvertImplicitly (t_el); + + return ImplicitNumericConversion (null, expr_type, t_el); + } + + Expression unwrap; + if (expr_type != expr.Type) + unwrap = Nullable.Unwrap.Create (expr); + else + unwrap = expr; + + Expression conv = unwrap; + if (!TypeSpecComparer.IsEqual (expr_type, t_el)) { + if (conv is Constant) + conv = ((Constant)conv).ConvertImplicitly (t_el); + else + conv = ImplicitNumericConversion (conv, expr_type, t_el); + + if (conv == null) + return null; + } + + if (expr_type != expr.Type) + return new Nullable.LiftedConversion (conv, unwrap, target_type).Resolve (ec); + + return Nullable.Wrap.Create (conv, target_type); + } + + /// + /// Implicit Numeric Conversions. + /// + /// expr is the expression to convert, returns a new expression of type + /// target_type or null if an implicit conversion is not possible. + /// + public static Expression ImplicitNumericConversion (Expression expr, TypeSpec target_type) + { + return ImplicitNumericConversion (expr, expr.Type, target_type); + } + + public static bool ImplicitNumericConversionExists (TypeSpec expr_type, TypeSpec target_type) + { + return ImplicitNumericConversion (null, expr_type, target_type) != null; + } + + static Expression ImplicitNumericConversion (Expression expr, TypeSpec expr_type, TypeSpec target_type) + { + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + // + // From sbyte to short, int, long, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I4); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Short: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I2); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + + } + + break; + case BuiltinTypeSpec.Type.Byte: + // + // From byte to short, ushort, int, uint, long, ulong, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + return expr == null ? EmptyExpression.Null : EmptyCast.Create (expr, target_type); + case BuiltinTypeSpec.Type.ULong: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Short: + // + // From short to int, long, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + return expr == null ? EmptyExpression.Null : EmptyCast.Create (expr, target_type); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.UShort: + // + // From ushort to int, uint, long, ulong, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + return expr == null ? EmptyExpression.Null : EmptyCast.Create (expr, target_type); + case BuiltinTypeSpec.Type.ULong: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Int: + // + // From int to long, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.UInt: + // + // From uint to long, ulong, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.ULong: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCastDuplex (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCastDuplex (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Long: + // + // From long to float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.ULong: + // + // From ulong to float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCastDuplex (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCastDuplex (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Char: + // + // From char to ushort, int, uint, long, ulong, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + return expr == null ? EmptyExpression.Null : EmptyCast.Create (expr, target_type); + case BuiltinTypeSpec.Type.ULong: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Float: + // + // float to double + // + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Double) + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + break; + } + + return null; + } + + // + // Full version of implicit conversion + // + public static bool ImplicitConversionExists (ResolveContext ec, Expression expr, TypeSpec target_type) + { + if (ImplicitStandardConversionExists (ec, expr, target_type)) + return true; + + if (expr.Type == InternalType.AnonymousMethod) { + if (!target_type.IsDelegate && !target_type.IsExpressionTreeType) + return false; + + AnonymousMethodExpression ame = (AnonymousMethodExpression) expr; + return ame.ImplicitStandardConversionExists (ec, target_type); + } + + // Conversion from __arglist to System.ArgIterator + if (expr.Type == InternalType.Arglist) + return target_type == ec.Module.PredefinedTypes.ArgIterator.TypeSpec; + + return UserDefinedConversion (ec, expr, target_type, + UserConversionRestriction.ImplicitOnly | UserConversionRestriction.ProbingOnly, Location.Null) != null; + } + + public static bool ImplicitStandardConversionExists (ResolveContext rc, Expression expr, TypeSpec target_type) + { + if (expr.eclass == ExprClass.MethodGroup) { + if (target_type.IsDelegate && rc.Module.Compiler.Settings.Version != LanguageVersion.ISO_1) { + MethodGroupExpr mg = expr as MethodGroupExpr; + if (mg != null) + return DelegateCreation.ImplicitStandardConversionExists (rc, mg, target_type); + } + + return false; + } + + return ImplicitStandardConversionExists (expr, target_type); + } + + // + // Implicit standard conversion (only core conversions are used here) + // + public static bool ImplicitStandardConversionExists (Expression expr, TypeSpec target_type) + { + // + // Identity conversions + // Implicit numeric conversions + // Implicit nullable conversions + // Implicit reference conversions + // Boxing conversions + // Implicit constant expression conversions + // Implicit conversions involving type parameters + // + + TypeSpec expr_type = expr.Type; + + if (expr_type == target_type) + return true; + + if (target_type.IsNullableType) + return ImplicitNulableConversion (null, expr, target_type) != null; + + if (ImplicitNumericConversion (null, expr_type, target_type) != null) + return true; + + if (ImplicitReferenceConversionExists (expr_type, target_type, false)) + return true; + + if (ImplicitBoxingConversion (null, expr_type, target_type) != null) + return true; + + // + // Implicit Constant Expression Conversions + // + if (expr is IntConstant){ + int value = ((IntConstant) expr).Value; + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + if (value >= SByte.MinValue && value <= SByte.MaxValue) + return true; + break; + case BuiltinTypeSpec.Type.Byte: + if (value >= 0 && value <= Byte.MaxValue) + return true; + break; + case BuiltinTypeSpec.Type.Short: + if (value >= Int16.MinValue && value <= Int16.MaxValue) + return true; + break; + case BuiltinTypeSpec.Type.UShort: + if (value >= UInt16.MinValue && value <= UInt16.MaxValue) + return true; + break; + case BuiltinTypeSpec.Type.UInt: + if (value >= 0) + return true; + break; + case BuiltinTypeSpec.Type.ULong: + // + // we can optimize this case: a positive int32 + // always fits on a uint64. But we need an opcode + // to do it. + // + if (value >= 0) + return true; + + break; + } + } + + if (expr is LongConstant && target_type.BuiltinType == BuiltinTypeSpec.Type.ULong){ + // + // Try the implicit constant expression conversion + // from long to ulong, instead of a nice routine, + // we just inline it + // + long v = ((LongConstant) expr).Value; + if (v >= 0) + return true; + } + + if (expr is IntegralConstant && target_type.IsEnum) { + var i = (IntegralConstant) expr; + // + // LAMESPEC: csc allows any constant like 0 values to be converted, including const float f = 0.0 + // + // An implicit enumeration conversion permits the decimal-integer-literal 0 + // to be converted to any enum-type and to any nullable-type whose underlying + // type is an enum-type + // + return i.IsZeroInteger; + } + + // + // Implicit dynamic conversion for remaining value types. It should probably + // go somewhere else + // + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + switch (target_type.Kind) { + case MemberKind.Struct: + case MemberKind.Enum: + return true; + } + + return false; + } + + // + // In an unsafe context implicit conversions is extended to include + // + // From any pointer-type to the type void* + // From the null literal to any pointer-type. + // + // LAMESPEC: The specification claims this conversion is allowed in implicit conversion but + // in reality implicit standard conversion uses it + // + if (target_type.IsPointer && expr.Type.IsPointer && ((PointerContainer) target_type).Element.Kind == MemberKind.Void) + return true; + + // + // Struct identity conversion, including dynamic erasure + // + if (expr_type.IsStruct && TypeSpecComparer.IsEqual (expr_type, target_type)) + return true; + + return false; + } + + /// + /// Finds "most encompassed type" according to the spec (13.4.2) + /// amongst the methods in the MethodGroupExpr + /// + public static TypeSpec FindMostEncompassedType (IList types) + { + TypeSpec best = null; + EmptyExpression expr; + + foreach (TypeSpec t in types) { + if (best == null) { + best = t; + continue; + } + + expr = new EmptyExpression (t); + if (ImplicitStandardConversionExists (expr, best)) + best = t; + } + + expr = new EmptyExpression (best); + foreach (TypeSpec t in types) { + if (best == t) + continue; + if (!ImplicitStandardConversionExists (expr, t)) { + best = null; + break; + } + } + + return best; + } + + // + // Finds the most encompassing type (type into which all other + // types can convert to) amongst the types in the given set + // + static TypeSpec FindMostEncompassingType (IList types) + { + if (types.Count == 0) + return null; + + if (types.Count == 1) + return types [0]; + + TypeSpec best = null; + for (int i = 0; i < types.Count; ++i) { + int ii = 0; + for (; ii < types.Count; ++ii) { + if (ii == i) + continue; + + var expr = new EmptyExpression (types[ii]); + if (!ImplicitStandardConversionExists (expr, types [i])) { + ii = 0; + break; + } + } + + if (ii == 0) + continue; + + if (best == null) { + best = types[i]; + continue; + } + + // Indicates multiple best types + return InternalType.FakeInternalType; + } + + return best; + } + + // + // Finds the most specific source Sx according to the rules of the spec (13.4.4) + // by making use of FindMostEncomp* methods. Applies the correct rules separately + // for explicit and implicit conversion operators. + // + static TypeSpec FindMostSpecificSource (ResolveContext rc, List list, TypeSpec sourceType, Expression source, bool apply_explicit_conv_rules) + { + TypeSpec[] src_types_set = null; + + // + // Try exact match first, if any operator converts from S then Sx = S + // + for (int i = 0; i < list.Count; ++i) { + TypeSpec param_type = list [i].Parameters.Types [0]; + + if (param_type == sourceType) + return param_type; + + if (src_types_set == null) + src_types_set = new TypeSpec [list.Count]; + + src_types_set [i] = param_type; + } + + // + // Explicit Conv rules + // + if (apply_explicit_conv_rules) { + var candidate_set = new List (); + + foreach (TypeSpec param_type in src_types_set){ + if (ImplicitStandardConversionExists (rc, source, param_type)) + candidate_set.Add (param_type); + } + + if (candidate_set.Count != 0) { + if (source.eclass == ExprClass.MethodGroup) + return InternalType.FakeInternalType; + + return FindMostEncompassedType (candidate_set); + } + } + + // + // Final case + // + if (apply_explicit_conv_rules) + return FindMostEncompassingType (src_types_set); + else + return FindMostEncompassedType (src_types_set); + } + + /// + /// Finds the most specific target Tx according to section 13.4.4 + /// + static public TypeSpec FindMostSpecificTarget (IList list, + TypeSpec target, bool apply_explicit_conv_rules) + { + List tgt_types_set = null; + + // + // If any operator converts to T then Tx = T + // + foreach (var mi in list){ + TypeSpec ret_type = mi.ReturnType; + if (ret_type == target) + return ret_type; + + if (tgt_types_set == null) { + tgt_types_set = new List (list.Count); + } else if (tgt_types_set.Contains (ret_type)) { + continue; + } + + tgt_types_set.Add (ret_type); + } + + // + // Explicit conv rules + // + if (apply_explicit_conv_rules) { + var candidate_set = new List (); + + foreach (TypeSpec ret_type in tgt_types_set) { + var expr = new EmptyExpression (ret_type); + + if (ImplicitStandardConversionExists (expr, target)) + candidate_set.Add (ret_type); + } + + if (candidate_set.Count != 0) + return FindMostEncompassingType (candidate_set); + } + + // + // Okay, final case ! + // + if (apply_explicit_conv_rules) + return FindMostEncompassedType (tgt_types_set); + else + return FindMostEncompassingType (tgt_types_set); + } + + /// + /// User-defined Implicit conversions + /// + static public Expression ImplicitUserConversion (ResolveContext ec, Expression source, TypeSpec target, Location loc) + { + return UserDefinedConversion (ec, source, target, UserConversionRestriction.ImplicitOnly, loc); + } + + /// + /// User-defined Explicit conversions + /// + static Expression ExplicitUserConversion (ResolveContext ec, Expression source, TypeSpec target, Location loc) + { + return UserDefinedConversion (ec, source, target, 0, loc); + } + + static void FindApplicableUserDefinedConversionOperators (ResolveContext rc, IList operators, Expression source, TypeSpec target, UserConversionRestriction restr, ref List candidates) + { + if (source.Type.IsInterface) { + // Neither A nor B are interface-types + return; + } + + // For a conversion operator to be applicable, it must be possible + // to perform a standard conversion from the source type to + // the operand type of the operator, and it must be possible + // to perform a standard conversion from the result type of + // the operator to the target type. + + Expression texpr = null; + + foreach (MethodSpec op in operators) { + + // Can be null because MemberCache.GetUserOperator does not resize the array + if (op == null) + continue; + + var t = op.Parameters.Types[0]; + if (source.Type != t && !ImplicitStandardConversionExists (rc, source, t)) { + if ((restr & UserConversionRestriction.ImplicitOnly) != 0) + continue; + + if (!ImplicitStandardConversionExists (new EmptyExpression (t), source.Type)) + continue; + } + + if ((restr & UserConversionRestriction.NullableSourceOnly) != 0 && !t.IsNullableType) + continue; + + t = op.ReturnType; + + if (t.IsInterface) + continue; + + if (target != t) { + if (t.IsNullableType) + t = Nullable.NullableInfo.GetUnderlyingType (t); + + if (!ImplicitStandardConversionExists (new EmptyExpression (t), target)) { + if ((restr & UserConversionRestriction.ImplicitOnly) != 0) + continue; + + if (texpr == null) + texpr = new EmptyExpression (target); + + if (!ImplicitStandardConversionExists (texpr, t)) + continue; + } + } + + if (candidates == null) + candidates = new List (); + + candidates.Add (op); + } + } + + // + // User-defined conversions + // + public static Expression UserDefinedConversion (ResolveContext rc, Expression source, TypeSpec target, UserConversionRestriction restr, Location loc) + { + List candidates = null; + + // + // If S or T are nullable types, source_type and target_type are their underlying types + // otherwise source_type and target_type are equal to S and T respectively. + // + TypeSpec source_type = source.Type; + TypeSpec target_type = target; + Expression source_type_expr; + bool nullable_source = false; + var implicitOnly = (restr & UserConversionRestriction.ImplicitOnly) != 0; + + if (source_type.IsNullableType) { + // No unwrapping conversion S? -> T for non-reference types + if (implicitOnly && !TypeSpec.IsReferenceType (target_type) && !target_type.IsNullableType) { + source_type_expr = source; + } else { + source_type_expr = Nullable.Unwrap.CreateUnwrapped (source); + source_type = source_type_expr.Type; + nullable_source = true; + } + } else { + source_type_expr = source; + } + + if (target_type.IsNullableType) + target_type = Nullable.NullableInfo.GetUnderlyingType (target_type); + + // Only these containers can contain a user defined implicit or explicit operators + const MemberKind user_conversion_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.TypeParameter; + + if ((source_type.Kind & user_conversion_kinds) != 0 && source_type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { + bool declared_only = source_type.IsStruct; + + var operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Implicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + + if (!implicitOnly) { + operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Explicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + } + } + + if ((target.Kind & user_conversion_kinds) != 0 && target_type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { + bool declared_only = target.IsStruct || implicitOnly; + + var operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Implicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + + if (!implicitOnly) { + operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Explicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + } + } + + if (candidates == null) + return null; + + // + // Find the most specific conversion operator + // + MethodSpec most_specific_operator; + TypeSpec s_x, t_x; + if (candidates.Count == 1) { + most_specific_operator = candidates[0]; + s_x = most_specific_operator.Parameters.Types[0]; + t_x = most_specific_operator.ReturnType; + } else { + // + // Pass original source type to find the best match against input type and + // not the unwrapped expression + // + s_x = FindMostSpecificSource (rc, candidates, source.Type, source_type_expr, !implicitOnly); + if (s_x == null) + return null; + + t_x = FindMostSpecificTarget (candidates, target, !implicitOnly); + if (t_x == null) + return null; + + most_specific_operator = null; + for (int i = 0; i < candidates.Count; ++i) { + if (candidates[i].ReturnType == t_x && candidates[i].Parameters.Types[0] == s_x) { + most_specific_operator = candidates[i]; + break; + } + } + + if (most_specific_operator == null) { + // + // Unless running in probing more + // + if ((restr & UserConversionRestriction.ProbingOnly) == 0) { + MethodSpec ambig_arg = candidates [0]; + most_specific_operator = candidates [1]; + /* + foreach (var candidate in candidates) { + if (candidate.ReturnType == t_x) + most_specific_operator = candidate; + else if (candidate.Parameters.Types[0] == s_x) + ambig_arg = candidate; + } + */ + rc.Report.Error (457, loc, + "Ambiguous user defined operators `{0}' and `{1}' when converting from `{2}' to `{3}'", + ambig_arg.GetSignatureForError (), most_specific_operator.GetSignatureForError (), + source.Type.GetSignatureForError (), target.GetSignatureForError ()); + } + + return ErrorExpression.Instance; + } + } + + // + // Convert input type when it's different to best operator argument + // + if (s_x != source_type) { + var c = source as Constant; + if (c != null) { + source = c.Reduce (rc, s_x); + if (source == null) + c = null; + } + + if (c == null) { + source = implicitOnly ? + ImplicitConversionStandard (rc, source_type_expr, s_x, loc) : + ExplicitConversionStandard (rc, source_type_expr, s_x, loc); + } + } else { + source = source_type_expr; + } + + source = new UserCast (most_specific_operator, source, loc).Resolve (rc); + + // + // Convert result type when it's different to best operator return type + // + if (t_x != target_type) { + // + // User operator is of T? + // + if (t_x.IsNullableType && (target.IsNullableType || !implicitOnly)) { + // + // User operator return type does not match target type we need + // yet another conversion. This should happen for promoted numeric + // types only + // + if (t_x != target) { + var unwrap = Nullable.Unwrap.CreateUnwrapped (source); + + source = implicitOnly ? + ImplicitConversionStandard (rc, unwrap, target_type, loc) : + ExplicitConversionStandard (rc, unwrap, target_type, loc); + + if (source == null) + return null; + + if (target.IsNullableType) + source = new Nullable.LiftedConversion (source, unwrap, target).Resolve (rc); + } + } else { + source = implicitOnly ? + ImplicitConversionStandard (rc, source, target_type, loc) : + ExplicitConversionStandard (rc, source, target_type, loc); + + if (source == null) + return null; + } + } + + + // + // Source expression is of nullable type and underlying conversion returns + // only non-nullable type we need to lift it manually + // + if (nullable_source && !s_x.IsNullableType) + return new Nullable.LiftedConversion (source, source_type_expr, target).Resolve (rc); + + // + // Target is of nullable type but source type is not, wrap the result expression + // + if (target.IsNullableType && !t_x.IsNullableType) + source = Nullable.Wrap.Create (source, target); + + return source; + } + + /// + /// Converts implicitly the resolved expression `expr' into the + /// `target_type'. It returns a new expression that can be used + /// in a context that expects a `target_type'. + /// + static public Expression ImplicitConversion (ResolveContext ec, Expression expr, + TypeSpec target_type, Location loc) + { + Expression e; + + if (target_type == null) + throw new Exception ("Target type is null"); + + e = ImplicitConversionStandard (ec, expr, target_type, loc); + if (e != null) + return e; + + e = ImplicitUserConversion (ec, expr, target_type, loc); + if (e != null) + return e; + + return null; + } + + + /// + /// Attempts to apply the `Standard Implicit + /// Conversion' rules to the expression `expr' into + /// the `target_type'. It returns a new expression + /// that can be used in a context that expects a + /// `target_type'. + /// + /// This is different from `ImplicitConversion' in that the + /// user defined implicit conversions are excluded. + /// + static public Expression ImplicitConversionStandard (ResolveContext ec, Expression expr, + TypeSpec target_type, Location loc) + { + return ImplicitConversionStandard (ec, expr, target_type, loc, false); + } + + static Expression ImplicitConversionStandard (ResolveContext ec, Expression expr, TypeSpec target_type, Location loc, bool explicit_cast) + { + if (expr.eclass == ExprClass.MethodGroup){ + if (!target_type.IsDelegate){ + return null; + } + + // + // Only allow anonymous method conversions on post ISO_1 + // + if (ec.Module.Compiler.Settings.Version != LanguageVersion.ISO_1){ + MethodGroupExpr mg = expr as MethodGroupExpr; + if (mg != null) + return new ImplicitDelegateCreation (target_type, mg, loc).Resolve (ec); + } + } + + TypeSpec expr_type = expr.Type; + Expression e; + + if (expr_type == target_type) { + if (expr_type != InternalType.NullLiteral && expr_type != InternalType.AnonymousMethod) + return expr; + return null; + } + + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + switch (target_type.Kind) { + case MemberKind.ArrayType: + case MemberKind.Class: + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Object) + return EmptyCast.Create (expr, target_type); + + goto case MemberKind.Struct; + case MemberKind.Struct: + case MemberKind.Delegate: + case MemberKind.Enum: + case MemberKind.Interface: + case MemberKind.TypeParameter: + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + return new DynamicConversion (target_type, explicit_cast ? CSharpBinderFlags.ConvertExplicit : 0, args, loc).Resolve (ec); + } + + return null; + } + + if (target_type.IsNullableType) + return ImplicitNulableConversion (ec, expr, target_type); + + // + // Attempt to do the implicit constant expression conversions + // + Constant c = expr as Constant; + if (c != null) { + try { + c = c.ConvertImplicitly (target_type); + } catch { + throw new InternalErrorException ("Conversion error", loc); + } + if (c != null) + return c; + } + + e = ImplicitNumericConversion (expr, expr_type, target_type); + if (e != null) + return e; + + e = ImplicitReferenceConversion (expr, target_type, explicit_cast); + if (e != null) + return e; + + e = ImplicitBoxingConversion (expr, expr_type, target_type); + if (e != null) + return e; + + if (expr is IntegralConstant && target_type.IsEnum){ + var i = (IntegralConstant) expr; + // + // LAMESPEC: csc allows any constant like 0 values to be converted, including const float f = 0.0 + // + // An implicit enumeration conversion permits the decimal-integer-literal 0 + // to be converted to any enum-type and to any nullable-type whose underlying + // type is an enum-type + // + if (i.IsZeroInteger) { + // Recreate 0 literal to remove any collected conversions + return new EnumConstant (new IntLiteral (ec.BuiltinTypes, 0, i.Location), target_type); + } + } + + var target_pc = target_type as PointerContainer; + if (target_pc != null) { + if (expr_type.IsPointer) { + // + // Pointer types are same when they have same element types + // + if (expr_type == target_pc) + return expr; + + if (target_pc.Element.Kind == MemberKind.Void) + return EmptyCast.Create (expr, target_type); + + //return null; + } + + if (expr_type == InternalType.NullLiteral) + return new NullPointer (target_type, loc); + } + + if (expr_type == InternalType.AnonymousMethod){ + AnonymousMethodExpression ame = (AnonymousMethodExpression) expr; + Expression am = ame.Compatible (ec, target_type); + if (am != null) + return am.Resolve (ec); + + // Avoid CS1503 after CS1661 + return ErrorExpression.Instance; + } + + if (expr_type == InternalType.Arglist && target_type == ec.Module.PredefinedTypes.ArgIterator.TypeSpec) + return expr; + + // + // dynamic erasure conversion on value types + // + if (expr_type.IsStruct && TypeSpecComparer.IsEqual (expr_type, target_type)) + return expr_type == target_type ? expr : EmptyCast.Create (expr, target_type); + + return null; + } + + /// + /// Attempts to implicitly convert `source' into `target_type', using + /// ImplicitConversion. If there is no implicit conversion, then + /// an error is signaled + /// + static public Expression ImplicitConversionRequired (ResolveContext ec, Expression source, + TypeSpec target_type, Location loc) + { + Expression e = ImplicitConversion (ec, source, target_type, loc); + if (e != null) + return e; + + source.Error_ValueCannotBeConverted (ec, target_type, false); + + return null; + } + + /// + /// Performs the explicit numeric conversions + /// + /// There are a few conversions that are not part of the C# standard, + /// they were interim hacks in the C# compiler that were supposed to + /// become explicit operators in the UIntPtr class and IntPtr class, + /// but for historical reasons it did not happen, so the C# compiler + /// ended up with these special hacks. + /// + /// See bug 59800 for details. + /// + /// The conversion are: + /// UIntPtr->SByte + /// UIntPtr->Int16 + /// UIntPtr->Int32 + /// IntPtr->UInt64 + /// UInt64->IntPtr + /// SByte->UIntPtr + /// Int16->UIntPtr + /// + /// + public static Expression ExplicitNumericConversion (ResolveContext rc, Expression expr, TypeSpec target_type) + { + // Not all predefined explicit numeric conversion are + // defined here, for some of them (mostly IntPtr/UIntPtr) we + // defer to user-operator handling which is now perfect but + // works for now + // + // LAMESPEC: Undocumented IntPtr/UIntPtr conversions + // IntPtr -> uint uses int + // UIntPtr -> long uses ulong + // + + switch (expr.Type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + // + // From sbyte to byte, ushort, uint, ulong, char, uintptr + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_U1); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_U2); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_U4); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_CH); + + // One of the built-in conversions that belonged in the class library + case BuiltinTypeSpec.Type.UIntPtr: + return new OperatorCast (new ConvCast (expr, rc.BuiltinTypes.ULong, ConvCast.Mode.I1_U8), target_type, target_type, true); + } + break; + case BuiltinTypeSpec.Type.Byte: + // + // From byte to sbyte and char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.U1_I1); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.U1_CH); + } + break; + case BuiltinTypeSpec.Type.Short: + // + // From short to sbyte, byte, ushort, uint, ulong, char, uintptr + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_U1); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_U2); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_U4); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_CH); + + // One of the built-in conversions that belonged in the class library + case BuiltinTypeSpec.Type.UIntPtr: + return new OperatorCast (new ConvCast (expr, rc.BuiltinTypes.ULong, ConvCast.Mode.I2_U8), target_type, target_type, true); + } + break; + case BuiltinTypeSpec.Type.UShort: + // + // From ushort to sbyte, byte, short, char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.U2_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.U2_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.U2_I2); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.U2_CH); + } + break; + case BuiltinTypeSpec.Type.Int: + // + // From int to sbyte, byte, short, ushort, uint, ulong, char, uintptr + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_U2); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_U4); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_CH); + + // One of the built-in conversions that belonged in the class library + case BuiltinTypeSpec.Type.UIntPtr: + return new OperatorCast (new ConvCast (expr, rc.BuiltinTypes.ULong, ConvCast.Mode.I2_U8), target_type, target_type, true); + } + break; + case BuiltinTypeSpec.Type.UInt: + // + // From uint to sbyte, byte, short, ushort, int, char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_I4); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_CH); + } + break; + case BuiltinTypeSpec.Type.Long: + // + // From long to sbyte, byte, short, ushort, int, uint, ulong, char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_I4); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_U4); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_CH); + } + break; + case BuiltinTypeSpec.Type.ULong: + // + // From ulong to sbyte, byte, short, ushort, int, uint, long, char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I4); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_U4); + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_CH); + + // One of the built-in conversions that belonged in the class library + case BuiltinTypeSpec.Type.IntPtr: + return new OperatorCast (EmptyCast.Create (expr, rc.BuiltinTypes.Long), target_type, true); + } + break; + case BuiltinTypeSpec.Type.Char: + // + // From char to sbyte, byte, short + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.CH_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.CH_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.CH_I2); + } + break; + case BuiltinTypeSpec.Type.Float: + // + // From float to sbyte, byte, short, + // ushort, int, uint, long, ulong, char + // or decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_I4); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_U4); + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_I8); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_CH); + case BuiltinTypeSpec.Type.Decimal: + return new OperatorCast (expr, target_type, true); + } + break; + case BuiltinTypeSpec.Type.Double: + // + // From double to sbyte, byte, short, + // ushort, int, uint, long, ulong, + // char, float or decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_I4); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_U4); + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_I8); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_CH); + case BuiltinTypeSpec.Type.Float: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_R4); + case BuiltinTypeSpec.Type.Decimal: + return new OperatorCast (expr, target_type, true); + } + break; + case BuiltinTypeSpec.Type.UIntPtr: + // + // Various built-in conversions that belonged in the class library + // + // from uintptr to sbyte, short, int32 + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.UInt, true), target_type, ConvCast.Mode.U4_I1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.UInt, true), target_type, ConvCast.Mode.U4_I2); + case BuiltinTypeSpec.Type.Int: + return EmptyCast.Create (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.UInt, true), target_type); + case BuiltinTypeSpec.Type.UInt: + return new OperatorCast (expr, expr.Type, target_type, true); + case BuiltinTypeSpec.Type.Long: + return EmptyCast.Create (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.ULong, true), target_type); + } + break; + case BuiltinTypeSpec.Type.IntPtr: + if (target_type.BuiltinType == BuiltinTypeSpec.Type.UInt) + return EmptyCast.Create (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.Int, true), target_type); + if (target_type.BuiltinType == BuiltinTypeSpec.Type.ULong) + return EmptyCast.Create (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.Long, true), target_type); + + break; + case BuiltinTypeSpec.Type.Decimal: + // From decimal to sbyte, byte, short, + // ushort, int, uint, long, ulong, char, + // float, or double + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.Double: + return new OperatorCast (expr, expr.Type, target_type, true); + } + + break; + } + + return null; + } + + /// + /// Returns whether an explicit reference conversion can be performed + /// from source_type to target_type + /// + public static bool ExplicitReferenceConversionExists (TypeSpec source_type, TypeSpec target_type) + { + Expression e = ExplicitReferenceConversion (null, source_type, target_type); + if (e == null) + return false; + + if (e == EmptyExpression.Null) + return true; + + throw new InternalErrorException ("Invalid probing conversion result"); + } + + /// + /// Implements Explicit Reference conversions + /// + static Expression ExplicitReferenceConversion (Expression source, TypeSpec source_type, TypeSpec target_type) + { + // + // From object to a generic parameter + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.Object && TypeManager.IsGenericParameter (target_type)) + return source == null ? EmptyExpression.Null : new UnboxCast (source, target_type); + + // + // Explicit type parameter conversion from T + // + if (source_type.Kind == MemberKind.TypeParameter) + return ExplicitTypeParameterConversionFromT (source, source_type, target_type); + + bool target_is_value_type = target_type.Kind == MemberKind.Struct || target_type.Kind == MemberKind.Enum; + + // + // Unboxing conversion from System.ValueType to any non-nullable-value-type + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.ValueType && target_is_value_type) + return source == null ? EmptyExpression.Null : new UnboxCast (source, target_type); + + // + // From object or dynamic to any reference type or value type (unboxing) + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.Object || source_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + if (target_type.IsPointer) + return null; + + return + source == null ? EmptyExpression.Null : + target_is_value_type ? new UnboxCast (source, target_type) : + source is Constant ? (Expression) new EmptyConstantCast ((Constant) source, target_type) : + new ClassCast (source, target_type); + } + + // + // From any class S to any class-type T, provided S is a base class of T + // + if (source_type.Kind == MemberKind.Class && TypeSpec.IsBaseClass (target_type, source_type, true)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + // + // From any interface-type S to to any class type T, provided T is not + // sealed, or provided T implements S. + // + // This also covers Explicit conversions involving type parameters + // section From any interface type to T + // + if (source_type.Kind == MemberKind.Interface) { + if (!target_type.IsSealed || target_type.ImplementsInterface (source_type, true)) { + if (source == null) + return EmptyExpression.Null; + + // + // Unboxing conversion from any interface-type to any non-nullable-value-type that + // implements the interface-type + // + return target_is_value_type ? new UnboxCast (source, target_type) : (Expression) new ClassCast (source, target_type); + } + + // + // From System.Collections.Generic.IList and its base interfaces to a one-dimensional + // array type S[], provided there is an implicit or explicit reference conversion from S to T. + // + var target_array = target_type as ArrayContainer; + if (target_array != null && IList_To_Array (source_type, target_array)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + return null; + } + + var source_array = source_type as ArrayContainer; + if (source_array != null) { + var target_array = target_type as ArrayContainer; + if (target_array != null) { + // + // From System.Array to any array-type + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.Array) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + // + // From an array type S with an element type Se to an array type T with an + // element type Te provided all the following are true: + // * S and T differe only in element type, in other words, S and T + // have the same number of dimensions. + // * Both Se and Te are reference types + // * An explicit reference conversions exist from Se to Te + // + if (source_array.Rank == target_array.Rank) { + + source_type = source_array.Element; + var target_element = target_array.Element; + + // + // LAMESPEC: Type parameters are special cased somehow but + // only when both source and target elements are type parameters + // + if ((source_type.Kind & target_element.Kind & MemberKind.TypeParameter) == MemberKind.TypeParameter) { + // + // Conversion is allowed unless source element type has struct constrain + // + if (TypeSpec.IsValueType (source_type)) + return null; + } else { + if (!TypeSpec.IsReferenceType (source_type)) + return null; + } + + if (!TypeSpec.IsReferenceType (target_element)) + return null; + + if (ExplicitReferenceConversionExists (source_type, target_element)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + return null; + } + } + + // + // From a single-dimensional array type S[] to System.Collections.Generic.IList and its base interfaces, + // provided that there is an explicit reference conversion from S to T + // + if (ArrayToIList (source_array, target_type, true)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + return null; + } + + // + // From any class type S to any interface T, provides S is not sealed + // and provided S does not implement T. + // + if (target_type.IsInterface && !source_type.IsSealed && !source_type.ImplementsInterface (target_type, true)) { + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + } + + // + // From System delegate to any delegate-type + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.Delegate && target_type.IsDelegate) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + // + // From variant generic delegate to same variant generic delegate type + // + if (source_type.IsDelegate && target_type.IsDelegate && source_type.MemberDefinition == target_type.MemberDefinition) { + var tparams = source_type.MemberDefinition.TypeParameters; + var targs_src = source_type.TypeArguments; + var targs_dst = target_type.TypeArguments; + int i; + for (i = 0; i < tparams.Length; ++i) { + // + // If TP is invariant, types have to be identical + // + if (TypeSpecComparer.IsEqual (targs_src[i], targs_dst[i])) + continue; + + if (tparams[i].Variance == Variance.Covariant) { + // + //If TP is covariant, an implicit or explicit identity or reference conversion is required + // + if (ImplicitReferenceConversionExists (targs_src[i], targs_dst[i])) + continue; + + if (ExplicitReferenceConversionExists (targs_src[i], targs_dst[i])) + continue; + + } else if (tparams[i].Variance == Variance.Contravariant) { + // + //If TP is contravariant, both are either identical or reference types + // + if (TypeSpec.IsReferenceType (targs_src[i]) && TypeSpec.IsReferenceType (targs_dst[i])) + continue; + } + + break; + } + + if (i == tparams.Length) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + } + + var tps = target_type as TypeParameterSpec; + if (tps != null) + return ExplicitTypeParameterConversionToT (source, source_type, tps); + + return null; + } + + /// + /// Performs an explicit conversion of the expression `expr' whose + /// type is expr.Type to `target_type'. + /// + static public Expression ExplicitConversionCore (ResolveContext ec, Expression expr, + TypeSpec target_type, Location loc) + { + TypeSpec expr_type = expr.Type; + + // Explicit conversion includes implicit conversion and it used for enum underlying types too + Expression ne = ImplicitConversionStandard (ec, expr, target_type, loc, true); + if (ne != null) + return ne; + + if (expr_type.IsEnum) { + TypeSpec real_target = target_type.IsEnum ? EnumSpec.GetUnderlyingType (target_type) : target_type; + Expression underlying = EmptyCast.Create (expr, EnumSpec.GetUnderlyingType (expr_type)); + if (underlying.Type == real_target) + ne = underlying; + + if (ne == null) + ne = ImplicitNumericConversion (underlying, real_target); + + if (ne == null) + ne = ExplicitNumericConversion (ec, underlying, real_target); + + // + // LAMESPEC: IntPtr and UIntPtr conversion to any Enum is allowed + // + if (ne == null && (real_target.BuiltinType == BuiltinTypeSpec.Type.IntPtr || real_target.BuiltinType == BuiltinTypeSpec.Type.UIntPtr)) + ne = ExplicitUserConversion (ec, underlying, real_target, loc); + + return ne != null ? EmptyCast.Create (ne, target_type) : null; + } + + if (target_type.IsEnum) { + // + // System.Enum can be unboxed to any enum-type + // + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Enum) + return new UnboxCast (expr, target_type); + + TypeSpec real_target = target_type.IsEnum ? EnumSpec.GetUnderlyingType (target_type) : target_type; + + if (expr_type == real_target) + return EmptyCast.Create (expr, target_type); + + Constant c = expr as Constant; + if (c != null) { + c = c.TryReduce (ec, real_target); + if (c != null) + return c; + } else { + ne = ImplicitNumericConversion (expr, real_target); + if (ne != null) + return EmptyCast.Create (ne, target_type); + + ne = ExplicitNumericConversion (ec, expr, real_target); + if (ne != null) + return EmptyCast.Create (ne, target_type); + + // + // LAMESPEC: IntPtr and UIntPtr conversion to any Enum is allowed + // + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.IntPtr || expr_type.BuiltinType == BuiltinTypeSpec.Type.UIntPtr) { + ne = ExplicitUserConversion (ec, expr, real_target, loc); + if (ne != null) + return ExplicitConversionCore (ec, ne, target_type, loc); + } + } + } else { + ne = ExplicitNumericConversion (ec, expr, target_type); + if (ne != null) + return ne; + } + + // + // Skip the ExplicitReferenceConversion because we can not convert + // from Null to a ValueType, and ExplicitReference wont check against + // null literal explicitly + // + if (expr_type != InternalType.NullLiteral) { + ne = ExplicitReferenceConversion (expr, expr_type, target_type); + if (ne != null) + return ne; + } + + if (ec.IsUnsafe){ + ne = ExplicitUnsafe (expr, target_type); + if (ne != null) + return ne; + } + + return null; + } + + public static Expression ExplicitUnsafe (Expression expr, TypeSpec target_type) + { + TypeSpec expr_type = expr.Type; + + if (target_type.IsPointer){ + if (expr_type.IsPointer) + return EmptyCast.Create (expr, target_type); + + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.Int: + return new OpcodeCast (expr, target_type, OpCodes.Conv_I); + + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Byte: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U); + + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_I); + + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I); + } + } + + if (expr_type.IsPointer){ + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new OpcodeCast (expr, target_type, OpCodes.Conv_I1); + case BuiltinTypeSpec.Type.Byte: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U1); + case BuiltinTypeSpec.Type.Short: + return new OpcodeCast (expr, target_type, OpCodes.Conv_I2); + case BuiltinTypeSpec.Type.UShort: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U2); + case BuiltinTypeSpec.Type.Int: + return new OpcodeCast (expr, target_type, OpCodes.Conv_I4); + case BuiltinTypeSpec.Type.UInt: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U4); + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.I_I8); + case BuiltinTypeSpec.Type.ULong: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + } + } + return null; + } + + /// + /// Same as ExplicitConversion, only it doesn't include user defined conversions + /// + static public Expression ExplicitConversionStandard (ResolveContext ec, Expression expr, + TypeSpec target_type, Location l) + { + int errors = ec.Report.Errors; + Expression ne = ImplicitConversionStandard (ec, expr, target_type, l); + if (ec.Report.Errors > errors) + return null; + + if (ne != null) + return ne; + + ne = ExplicitNumericConversion (ec, expr, target_type); + if (ne != null) + return ne; + + ne = ExplicitReferenceConversion (expr, expr.Type, target_type); + if (ne != null) + return ne; + + if (ec.IsUnsafe && expr.Type.IsPointer && target_type.IsPointer && ((PointerContainer)expr.Type).Element.Kind == MemberKind.Void) + return EmptyCast.Create (expr, target_type); + + expr.Error_ValueCannotBeConverted (ec, target_type, true); + return null; + } + + /// + /// Performs an explicit conversion of the expression `expr' whose + /// type is expr.Type to `target_type'. + /// + static public Expression ExplicitConversion (ResolveContext ec, Expression expr, + TypeSpec target_type, Location loc) + { + Expression e = ExplicitConversionCore (ec, expr, target_type, loc); + if (e != null) { + // + // Don't eliminate explicit precission casts + // + if (e == expr) { + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Float) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Double) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + } + + return e; + } + + TypeSpec expr_type = expr.Type; + if (target_type.IsNullableType) { + TypeSpec target; + + if (expr_type.IsNullableType) { + target = Nullable.NullableInfo.GetUnderlyingType (target_type); + Expression unwrap = Nullable.Unwrap.Create (expr); + e = ExplicitConversion (ec, unwrap, target, expr.Location); + if (e == null) + return null; + + return new Nullable.LiftedConversion (e, unwrap, target_type).Resolve (ec); + } + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Object) { + return new UnboxCast (expr, target_type); + } + + target = TypeManager.GetTypeArguments (target_type) [0]; + e = ExplicitConversionCore (ec, expr, target, loc); + if (e != null) + return TypeSpec.IsReferenceType (expr.Type) ? new UnboxCast (expr, target_type) : Nullable.Wrap.Create (e, target_type); + } else if (expr_type.IsNullableType) { + e = ImplicitBoxingConversion (expr, Nullable.NullableInfo.GetUnderlyingType (expr_type), target_type); + if (e != null) + return e; + + e = Nullable.Unwrap.Create (expr, false); + e = ExplicitConversionCore (ec, e, target_type, loc); + if (e != null) + return EmptyCast.Create (e, target_type); + } + + e = ExplicitUserConversion (ec, expr, target_type, loc); + + if (e != null) + return e; + + expr.Error_ValueCannotBeConverted (ec, target_type, true); + return null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.cs new file mode 100644 index 000000000..868c1394f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.cs @@ -0,0 +1,16238 @@ +// created by jay 0.7 (c) 1998 Axel.Schreiner@informatik.uni-osnabrueck.de + +#line 2 "cs-parser.jay" +// +// cs-parser.jay: The Parser for the C# compiler +// +// Authors: Miguel de Icaza (miguel@gnome.org) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual Licensed under the terms of the GNU GPL and the MIT X11 license +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// (C) 2004-2011 Novell, Inc +// Copyright 2011-2012 Xamarin Inc. +// + +using System.Text; +using System.IO; +using System; +using System.Collections.Generic; + +namespace Mono.CSharp +{ + /// + /// The C# Parser + /// + public class CSharpParser + { + [Flags] + enum ParameterModifierType + { + Ref = 1 << 1, + Out = 1 << 2, + This = 1 << 3, + Params = 1 << 4, + Arglist = 1 << 5, + DefaultValue = 1 << 6, + + All = Ref | Out | This | Params | Arglist | DefaultValue, + PrimaryConstructor = Ref | Out | Params | DefaultValue + } + + static readonly object ModifierNone = 0; + + NamespaceContainer current_namespace; + TypeContainer current_container; + TypeDefinition current_type; + PropertyBase current_property; + EventProperty current_event; + EventField current_event_field; + FieldBase current_field; + + /// + /// Current block is used to add statements as we find + /// them. + /// + Block current_block; + + BlockVariable current_variable; + + Delegate current_delegate; + + AnonymousMethodExpression current_anonymous_method; + + /// + /// This is used by the unary_expression code to resolve + /// a name against a parameter. + /// + + // FIXME: This is very ugly and it's very hard to reset it correctly + // on all places, especially when some parameters are autogenerated. + ParametersCompiled current_local_parameters; + + bool parsing_anonymous_method; + + bool async_block; + + /// + /// An out-of-band stack. + /// + Stack oob_stack; + + /// + /// Controls the verbosity of the errors produced by the parser + /// + int yacc_verbose_flag; + + /// + /// Used by the interactive shell, flags whether EOF was reached + /// and an error was produced + /// + public bool UnexpectedEOF; + + /// + /// The current file. + /// + readonly CompilationSourceFile file; + + /// + /// Temporary Xml documentation cache. + /// For enum types, we need one more temporary store. + /// + string tmpComment; + string enumTypeComment; + + /// Current attribute target + string current_attr_target; + + ParameterModifierType valid_param_mod; + + bool default_parameter_used; + + /// When using the interactive parser, this holds the + /// resulting expression + public Class InteractiveResult; + + // + // Keeps track of global data changes to undo on parser error + // + public Undo undo; + + bool? interactive_async; + + Stack linq_clause_blocks; + + ModuleContainer module; + + readonly CompilerContext compiler; + readonly LanguageVersion lang_version; + readonly bool doc_support; + readonly CompilerSettings settings; + readonly Report report; + + // + // Instead of allocating carrier array everytime we + // share the bucket for very common constructs which can never + // be recursive + // + List parameters_bucket; + + // + // Full AST support members + // + LocationsBag lbag; + List> mod_locations; + Location parameterModifierLocation, savedLocation, savedEventAssignLocation; + Location savedAttrParenOpenLocation, savedAttrParenCloseLocation, savedOperatorLocation; + Stack> locationListStack = new Stack> (); // used for type parameters + Stack opt_intoStack = new Stack (); + + bool HadAttributeParens; + List attributeArgumentCommas = new List (); + List parameterListCommas = new List (); + Stack location_stack; +#line default + + /** error output stream. + It should be changeable. + */ + public System.IO.TextWriter ErrorOutput = System.Console.Out; + + /** simplified error message. + @see yyerror + */ + public void yyerror (string message) { + yyerror(message, null); + } +#pragma warning disable 649 + /* An EOF token */ + public int eof_token; +#pragma warning restore 649 + /** (syntax) error message. + Can be overwritten to control message format. + @param message text to be displayed. + @param expected vector of acceptable tokens, if available. + */ + public void yyerror (string message, string[] expected) { + if ((yacc_verbose_flag > 0) && (expected != null) && (expected.Length > 0)) { + ErrorOutput.Write (message+", expecting"); + for (int n = 0; n < expected.Length; ++ n) + ErrorOutput.Write (" "+expected[n]); + ErrorOutput.WriteLine (); + } else + ErrorOutput.WriteLine (message); + } + + /** debugging support, requires the package jay.yydebug. + Set to null to suppress debugging messages. + */ +//t internal yydebug.yyDebug debug; + + protected const int yyFinal = 7; +//t // Put this array into a separate class so it is only initialized if debugging is actually used +//t // Use MarshalByRefObject to disable inlining +//t class YYRules : MarshalByRefObject { +//t public static readonly string [] yyRule = { +//t "$accept : compilation_unit", +//t "compilation_unit : outer_declaration opt_EOF", +//t "$$1 :", +//t "compilation_unit : interactive_parsing $$1 opt_EOF", +//t "compilation_unit : documentation_parsing", +//t "outer_declaration : opt_extern_alias_directives opt_using_directives", +//t "outer_declaration : opt_extern_alias_directives opt_using_directives namespace_or_type_declarations opt_attributes", +//t "outer_declaration : opt_extern_alias_directives opt_using_directives attribute_sections", +//t "outer_declaration : error", +//t "opt_EOF :", +//t "opt_EOF : EOF", +//t "extern_alias_directives : extern_alias_directive", +//t "extern_alias_directives : extern_alias_directives extern_alias_directive", +//t "extern_alias_directive : EXTERN_ALIAS IDENTIFIER IDENTIFIER SEMICOLON", +//t "extern_alias_directive : EXTERN_ALIAS error", +//t "using_directives : using_directive", +//t "using_directives : using_directives using_directive", +//t "using_directive : using_namespace", +//t "using_namespace : USING namespace_or_type_expr SEMICOLON", +//t "using_namespace : USING IDENTIFIER ASSIGN namespace_or_type_expr SEMICOLON", +//t "using_namespace : USING error", +//t "$$2 :", +//t "$$3 :", +//t "namespace_declaration : opt_attributes NAMESPACE namespace_name $$2 OPEN_BRACE $$3 opt_extern_alias_directives opt_using_directives opt_namespace_or_type_declarations CLOSE_BRACE opt_semicolon_error", +//t "namespace_declaration : opt_attributes NAMESPACE namespace_name", +//t "opt_semicolon_error :", +//t "opt_semicolon_error : SEMICOLON", +//t "opt_semicolon_error : error", +//t "namespace_name : IDENTIFIER", +//t "namespace_name : namespace_name DOT IDENTIFIER", +//t "namespace_name : error", +//t "opt_semicolon :", +//t "opt_semicolon : SEMICOLON", +//t "opt_comma :", +//t "opt_comma : COMMA", +//t "opt_using_directives :", +//t "opt_using_directives : using_directives", +//t "opt_extern_alias_directives :", +//t "opt_extern_alias_directives : extern_alias_directives", +//t "opt_namespace_or_type_declarations :", +//t "opt_namespace_or_type_declarations : namespace_or_type_declarations", +//t "namespace_or_type_declarations : namespace_or_type_declaration", +//t "namespace_or_type_declarations : namespace_or_type_declarations namespace_or_type_declaration", +//t "namespace_or_type_declaration : type_declaration", +//t "namespace_or_type_declaration : namespace_declaration", +//t "namespace_or_type_declaration : attribute_sections CLOSE_BRACE", +//t "type_declaration : class_declaration", +//t "type_declaration : struct_declaration", +//t "type_declaration : interface_declaration", +//t "type_declaration : enum_declaration", +//t "type_declaration : delegate_declaration", +//t "opt_attributes :", +//t "opt_attributes : attribute_sections", +//t "attribute_sections : attribute_section", +//t "attribute_sections : attribute_sections attribute_section", +//t "$$4 :", +//t "attribute_section : OPEN_BRACKET $$4 attribute_section_cont", +//t "$$5 :", +//t "attribute_section_cont : attribute_target COLON $$5 attribute_list opt_comma CLOSE_BRACKET", +//t "attribute_section_cont : attribute_list opt_comma CLOSE_BRACKET", +//t "attribute_section_cont : IDENTIFIER error", +//t "attribute_section_cont : error", +//t "attribute_target : IDENTIFIER", +//t "attribute_target : EVENT", +//t "attribute_target : RETURN", +//t "attribute_list : attribute", +//t "attribute_list : attribute_list COMMA attribute", +//t "$$6 :", +//t "attribute : attribute_name $$6 opt_attribute_arguments", +//t "attribute_name : namespace_or_type_expr", +//t "opt_attribute_arguments :", +//t "opt_attribute_arguments : OPEN_PARENS attribute_arguments CLOSE_PARENS", +//t "attribute_arguments :", +//t "attribute_arguments : positional_or_named_argument", +//t "attribute_arguments : named_attribute_argument", +//t "attribute_arguments : attribute_arguments COMMA positional_or_named_argument", +//t "attribute_arguments : attribute_arguments COMMA named_attribute_argument", +//t "positional_or_named_argument : expression", +//t "positional_or_named_argument : named_argument", +//t "positional_or_named_argument : error", +//t "$$7 :", +//t "named_attribute_argument : IDENTIFIER ASSIGN $$7 expression", +//t "named_argument : identifier_inside_body COLON opt_named_modifier expression_or_error", +//t "opt_named_modifier :", +//t "opt_named_modifier : REF", +//t "opt_named_modifier : OUT", +//t "opt_class_member_declarations :", +//t "opt_class_member_declarations : class_member_declarations", +//t "class_member_declarations : class_member_declaration", +//t "class_member_declarations : class_member_declarations class_member_declaration", +//t "class_member_declaration : constant_declaration", +//t "class_member_declaration : field_declaration", +//t "class_member_declaration : method_declaration", +//t "class_member_declaration : property_declaration", +//t "class_member_declaration : event_declaration", +//t "class_member_declaration : indexer_declaration", +//t "class_member_declaration : operator_declaration", +//t "class_member_declaration : constructor_declaration", +//t "class_member_declaration : primary_constructor_body", +//t "class_member_declaration : destructor_declaration", +//t "class_member_declaration : type_declaration", +//t "class_member_declaration : attributes_without_members", +//t "class_member_declaration : incomplete_member", +//t "class_member_declaration : error", +//t "$$8 :", +//t "primary_constructor_body : OPEN_BRACE $$8 opt_statement_list block_end", +//t "$$9 :", +//t "$$10 :", +//t "$$11 :", +//t "$$12 :", +//t "$$13 :", +//t "struct_declaration : opt_attributes opt_modifiers opt_partial STRUCT $$9 type_declaration_name $$10 opt_primary_parameters opt_class_base opt_type_parameter_constraints_clauses $$11 OPEN_BRACE $$12 opt_class_member_declarations CLOSE_BRACE $$13 opt_semicolon", +//t "struct_declaration : opt_attributes opt_modifiers opt_partial STRUCT error", +//t "$$14 :", +//t "constant_declaration : opt_attributes opt_modifiers CONST type IDENTIFIER $$14 constant_initializer opt_constant_declarators SEMICOLON", +//t "constant_declaration : opt_attributes opt_modifiers CONST type error", +//t "opt_constant_declarators :", +//t "opt_constant_declarators : constant_declarators", +//t "constant_declarators : constant_declarator", +//t "constant_declarators : constant_declarators constant_declarator", +//t "constant_declarator : COMMA IDENTIFIER constant_initializer", +//t "$$15 :", +//t "constant_initializer : ASSIGN $$15 constant_initializer_expr", +//t "constant_initializer : error", +//t "constant_initializer_expr : constant_expression", +//t "constant_initializer_expr : array_initializer", +//t "$$16 :", +//t "field_declaration : opt_attributes opt_modifiers member_type IDENTIFIER $$16 opt_field_initializer opt_field_declarators SEMICOLON", +//t "$$17 :", +//t "field_declaration : opt_attributes opt_modifiers FIXED simple_type IDENTIFIER $$17 fixed_field_size opt_fixed_field_declarators SEMICOLON", +//t "field_declaration : opt_attributes opt_modifiers FIXED simple_type error SEMICOLON", +//t "opt_field_initializer :", +//t "$$18 :", +//t "opt_field_initializer : ASSIGN $$18 variable_initializer", +//t "opt_field_declarators :", +//t "opt_field_declarators : field_declarators", +//t "field_declarators : field_declarator", +//t "field_declarators : field_declarators field_declarator", +//t "field_declarator : COMMA IDENTIFIER", +//t "$$19 :", +//t "field_declarator : COMMA IDENTIFIER ASSIGN $$19 variable_initializer", +//t "opt_fixed_field_declarators :", +//t "opt_fixed_field_declarators : fixed_field_declarators", +//t "fixed_field_declarators : fixed_field_declarator", +//t "fixed_field_declarators : fixed_field_declarators fixed_field_declarator", +//t "fixed_field_declarator : COMMA IDENTIFIER fixed_field_size", +//t "$$20 :", +//t "fixed_field_size : OPEN_BRACKET $$20 expression CLOSE_BRACKET", +//t "fixed_field_size : OPEN_BRACKET error", +//t "variable_initializer : expression", +//t "variable_initializer : array_initializer", +//t "variable_initializer : error", +//t "$$21 :", +//t "method_declaration : method_header $$21 method_body_expression_block", +//t "$$22 :", +//t "$$23 :", +//t "method_header : opt_attributes opt_modifiers member_type method_declaration_name OPEN_PARENS $$22 opt_formal_parameter_list CLOSE_PARENS $$23 opt_type_parameter_constraints_clauses", +//t "$$24 :", +//t "$$25 :", +//t "$$26 :", +//t "method_header : opt_attributes opt_modifiers PARTIAL VOID $$24 method_declaration_name OPEN_PARENS $$25 opt_formal_parameter_list CLOSE_PARENS $$26 opt_type_parameter_constraints_clauses", +//t "method_header : opt_attributes opt_modifiers member_type modifiers method_declaration_name OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS", +//t "method_header : opt_attributes opt_modifiers member_type method_declaration_name error", +//t "method_body_expression_block : method_body", +//t "method_body_expression_block : expression_block", +//t "method_body : block", +//t "method_body : SEMICOLON", +//t "$$27 :", +//t "expression_block : ARROW $$27 expression SEMICOLON", +//t "opt_formal_parameter_list :", +//t "opt_formal_parameter_list : formal_parameter_list", +//t "formal_parameter_list : fixed_parameters", +//t "formal_parameter_list : fixed_parameters COMMA parameter_array", +//t "formal_parameter_list : fixed_parameters COMMA arglist_modifier", +//t "formal_parameter_list : parameter_array COMMA error", +//t "formal_parameter_list : fixed_parameters COMMA parameter_array COMMA error", +//t "formal_parameter_list : arglist_modifier COMMA error", +//t "formal_parameter_list : fixed_parameters COMMA ARGLIST COMMA error", +//t "formal_parameter_list : parameter_array", +//t "formal_parameter_list : arglist_modifier", +//t "formal_parameter_list : error", +//t "fixed_parameters : fixed_parameter", +//t "fixed_parameters : fixed_parameters COMMA fixed_parameter", +//t "fixed_parameter : opt_attributes opt_parameter_modifier parameter_type identifier_inside_body", +//t "fixed_parameter : opt_attributes opt_parameter_modifier parameter_type identifier_inside_body OPEN_BRACKET CLOSE_BRACKET", +//t "fixed_parameter : attribute_sections error", +//t "fixed_parameter : opt_attributes opt_parameter_modifier parameter_type error", +//t "$$28 :", +//t "fixed_parameter : opt_attributes opt_parameter_modifier parameter_type identifier_inside_body ASSIGN $$28 constant_expression", +//t "opt_parameter_modifier :", +//t "opt_parameter_modifier : parameter_modifiers", +//t "parameter_modifiers : parameter_modifier", +//t "parameter_modifiers : parameter_modifiers parameter_modifier", +//t "parameter_modifier : REF", +//t "parameter_modifier : OUT", +//t "parameter_modifier : THIS", +//t "parameter_array : opt_attributes params_modifier type IDENTIFIER", +//t "parameter_array : opt_attributes params_modifier type IDENTIFIER ASSIGN constant_expression", +//t "parameter_array : opt_attributes params_modifier type error", +//t "params_modifier : PARAMS", +//t "params_modifier : PARAMS parameter_modifier", +//t "params_modifier : PARAMS params_modifier", +//t "arglist_modifier : ARGLIST", +//t "$$29 :", +//t "$$30 :", +//t "$$31 :", +//t "$$32 :", +//t "property_declaration : opt_attributes opt_modifiers member_type member_declaration_name $$29 OPEN_BRACE $$30 accessor_declarations $$31 CLOSE_BRACE $$32 opt_property_initializer", +//t "$$33 :", +//t "property_declaration : opt_attributes opt_modifiers member_type member_declaration_name $$33 expression_block", +//t "opt_property_initializer :", +//t "$$34 :", +//t "opt_property_initializer : ASSIGN $$34 property_initializer SEMICOLON", +//t "property_initializer : expression", +//t "property_initializer : array_initializer", +//t "$$35 :", +//t "$$36 :", +//t "indexer_declaration : opt_attributes opt_modifiers member_type indexer_declaration_name OPEN_BRACKET $$35 opt_formal_parameter_list CLOSE_BRACKET $$36 indexer_body", +//t "indexer_body : OPEN_BRACE accessor_declarations CLOSE_BRACE", +//t "indexer_body : expression_block", +//t "accessor_declarations : get_accessor_declaration", +//t "accessor_declarations : get_accessor_declaration accessor_declarations", +//t "accessor_declarations : set_accessor_declaration", +//t "accessor_declarations : set_accessor_declaration accessor_declarations", +//t "accessor_declarations : error", +//t "$$37 :", +//t "get_accessor_declaration : opt_attributes opt_modifiers GET $$37 accessor_body", +//t "$$38 :", +//t "set_accessor_declaration : opt_attributes opt_modifiers SET $$38 accessor_body", +//t "accessor_body : block", +//t "accessor_body : SEMICOLON", +//t "accessor_body : error", +//t "$$39 :", +//t "$$40 :", +//t "$$41 :", +//t "$$42 :", +//t "interface_declaration : opt_attributes opt_modifiers opt_partial INTERFACE $$39 type_declaration_name $$40 opt_class_base opt_type_parameter_constraints_clauses $$41 OPEN_BRACE opt_interface_member_declarations CLOSE_BRACE $$42 opt_semicolon", +//t "interface_declaration : opt_attributes opt_modifiers opt_partial INTERFACE error", +//t "opt_interface_member_declarations :", +//t "opt_interface_member_declarations : interface_member_declarations", +//t "interface_member_declarations : interface_member_declaration", +//t "interface_member_declarations : interface_member_declarations interface_member_declaration", +//t "interface_member_declaration : constant_declaration", +//t "interface_member_declaration : field_declaration", +//t "interface_member_declaration : method_declaration", +//t "interface_member_declaration : property_declaration", +//t "interface_member_declaration : event_declaration", +//t "interface_member_declaration : indexer_declaration", +//t "interface_member_declaration : operator_declaration", +//t "interface_member_declaration : constructor_declaration", +//t "interface_member_declaration : type_declaration", +//t "$$43 :", +//t "operator_declaration : opt_attributes opt_modifiers operator_declarator $$43 method_body_expression_block", +//t "operator_type : type_expression_or_array", +//t "operator_type : VOID", +//t "$$44 :", +//t "operator_declarator : operator_type OPERATOR overloadable_operator OPEN_PARENS $$44 opt_formal_parameter_list CLOSE_PARENS", +//t "operator_declarator : conversion_operator_declarator", +//t "overloadable_operator : BANG", +//t "overloadable_operator : TILDE", +//t "overloadable_operator : OP_INC", +//t "overloadable_operator : OP_DEC", +//t "overloadable_operator : TRUE", +//t "overloadable_operator : FALSE", +//t "overloadable_operator : PLUS", +//t "overloadable_operator : MINUS", +//t "overloadable_operator : STAR", +//t "overloadable_operator : DIV", +//t "overloadable_operator : PERCENT", +//t "overloadable_operator : BITWISE_AND", +//t "overloadable_operator : BITWISE_OR", +//t "overloadable_operator : CARRET", +//t "overloadable_operator : OP_SHIFT_LEFT", +//t "overloadable_operator : OP_SHIFT_RIGHT", +//t "overloadable_operator : OP_EQ", +//t "overloadable_operator : OP_NE", +//t "overloadable_operator : OP_GT", +//t "overloadable_operator : OP_LT", +//t "overloadable_operator : OP_GE", +//t "overloadable_operator : OP_LE", +//t "$$45 :", +//t "conversion_operator_declarator : IMPLICIT OPERATOR type OPEN_PARENS $$45 opt_formal_parameter_list CLOSE_PARENS", +//t "$$46 :", +//t "conversion_operator_declarator : EXPLICIT OPERATOR type OPEN_PARENS $$46 opt_formal_parameter_list CLOSE_PARENS", +//t "conversion_operator_declarator : IMPLICIT error", +//t "conversion_operator_declarator : EXPLICIT error", +//t "constructor_declaration : constructor_declarator constructor_body", +//t "$$47 :", +//t "$$48 :", +//t "constructor_declarator : opt_attributes opt_modifiers IDENTIFIER $$47 OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS $$48 opt_constructor_initializer", +//t "constructor_body : block_prepared", +//t "constructor_body : SEMICOLON", +//t "opt_constructor_initializer :", +//t "opt_constructor_initializer : constructor_initializer", +//t "$$49 :", +//t "constructor_initializer : COLON BASE OPEN_PARENS $$49 opt_argument_list CLOSE_PARENS", +//t "$$50 :", +//t "constructor_initializer : COLON THIS OPEN_PARENS $$50 opt_argument_list CLOSE_PARENS", +//t "constructor_initializer : COLON error", +//t "constructor_initializer : error", +//t "$$51 :", +//t "destructor_declaration : opt_attributes opt_modifiers TILDE $$51 IDENTIFIER OPEN_PARENS CLOSE_PARENS method_body", +//t "$$52 :", +//t "event_declaration : opt_attributes opt_modifiers EVENT type member_declaration_name $$52 opt_event_initializer opt_event_declarators SEMICOLON", +//t "$$53 :", +//t "$$54 :", +//t "event_declaration : opt_attributes opt_modifiers EVENT type member_declaration_name OPEN_BRACE $$53 event_accessor_declarations $$54 CLOSE_BRACE", +//t "event_declaration : opt_attributes opt_modifiers EVENT type error", +//t "opt_event_initializer :", +//t "$$55 :", +//t "opt_event_initializer : ASSIGN $$55 event_variable_initializer", +//t "opt_event_declarators :", +//t "opt_event_declarators : event_declarators", +//t "event_declarators : event_declarator", +//t "event_declarators : event_declarators event_declarator", +//t "event_declarator : COMMA IDENTIFIER", +//t "$$56 :", +//t "event_declarator : COMMA IDENTIFIER ASSIGN $$56 event_variable_initializer", +//t "$$57 :", +//t "event_variable_initializer : $$57 variable_initializer", +//t "event_accessor_declarations : add_accessor_declaration remove_accessor_declaration", +//t "event_accessor_declarations : remove_accessor_declaration add_accessor_declaration", +//t "event_accessor_declarations : add_accessor_declaration", +//t "event_accessor_declarations : remove_accessor_declaration", +//t "event_accessor_declarations : error", +//t "$$58 :", +//t "add_accessor_declaration : opt_attributes opt_modifiers ADD $$58 event_accessor_block", +//t "$$59 :", +//t "remove_accessor_declaration : opt_attributes opt_modifiers REMOVE $$59 event_accessor_block", +//t "event_accessor_block : opt_semicolon", +//t "event_accessor_block : block", +//t "attributes_without_members : attribute_sections CLOSE_BRACE", +//t "incomplete_member : opt_attributes opt_modifiers member_type CLOSE_BRACE", +//t "$$60 :", +//t "$$61 :", +//t "$$62 :", +//t "enum_declaration : opt_attributes opt_modifiers ENUM type_declaration_name opt_enum_base $$60 OPEN_BRACE $$61 opt_enum_member_declarations $$62 CLOSE_BRACE opt_semicolon", +//t "opt_enum_base :", +//t "opt_enum_base : COLON type", +//t "opt_enum_base : COLON error", +//t "opt_enum_member_declarations :", +//t "opt_enum_member_declarations : enum_member_declarations", +//t "opt_enum_member_declarations : enum_member_declarations COMMA", +//t "enum_member_declarations : enum_member_declaration", +//t "enum_member_declarations : enum_member_declarations COMMA enum_member_declaration", +//t "enum_member_declaration : opt_attributes IDENTIFIER", +//t "$$63 :", +//t "enum_member_declaration : opt_attributes IDENTIFIER $$63 ASSIGN constant_expression", +//t "enum_member_declaration : opt_attributes IDENTIFIER error", +//t "enum_member_declaration : attributes_without_members", +//t "$$64 :", +//t "$$65 :", +//t "$$66 :", +//t "delegate_declaration : opt_attributes opt_modifiers DELEGATE member_type type_declaration_name OPEN_PARENS $$64 opt_formal_parameter_list CLOSE_PARENS $$65 opt_type_parameter_constraints_clauses $$66 SEMICOLON", +//t "opt_nullable :", +//t "opt_nullable : INTERR_NULLABLE", +//t "namespace_or_type_expr : member_name", +//t "namespace_or_type_expr : qualified_alias_member IDENTIFIER opt_type_argument_list", +//t "namespace_or_type_expr : qualified_alias_member IDENTIFIER generic_dimension", +//t "member_name : simple_name_expr", +//t "member_name : namespace_or_type_expr DOT IDENTIFIER opt_type_argument_list", +//t "member_name : namespace_or_type_expr DOT IDENTIFIER generic_dimension", +//t "simple_name_expr : IDENTIFIER opt_type_argument_list", +//t "simple_name_expr : IDENTIFIER generic_dimension", +//t "opt_type_argument_list :", +//t "opt_type_argument_list : OP_GENERICS_LT type_arguments OP_GENERICS_GT", +//t "opt_type_argument_list : OP_GENERICS_LT error", +//t "type_arguments : type", +//t "type_arguments : type_arguments COMMA type", +//t "$$67 :", +//t "type_declaration_name : IDENTIFIER $$67 opt_type_parameter_list", +//t "member_declaration_name : method_declaration_name", +//t "method_declaration_name : type_declaration_name", +//t "method_declaration_name : explicit_interface IDENTIFIER opt_type_parameter_list", +//t "indexer_declaration_name : THIS", +//t "indexer_declaration_name : explicit_interface THIS", +//t "explicit_interface : IDENTIFIER opt_type_argument_list DOT", +//t "explicit_interface : qualified_alias_member IDENTIFIER opt_type_argument_list DOT", +//t "explicit_interface : explicit_interface IDENTIFIER opt_type_argument_list DOT", +//t "opt_type_parameter_list :", +//t "opt_type_parameter_list : OP_GENERICS_LT_DECL type_parameters OP_GENERICS_GT", +//t "type_parameters : type_parameter", +//t "type_parameters : type_parameters COMMA type_parameter", +//t "type_parameter : opt_attributes opt_type_parameter_variance IDENTIFIER", +//t "type_parameter : error", +//t "type_and_void : type_expression_or_array", +//t "type_and_void : VOID", +//t "member_type : type_and_void", +//t "type : type_expression_or_array", +//t "type : void_invalid", +//t "simple_type : type_expression", +//t "simple_type : void_invalid", +//t "parameter_type : type_expression_or_array", +//t "parameter_type : VOID", +//t "type_expression_or_array : type_expression", +//t "type_expression_or_array : type_expression rank_specifiers", +//t "type_expression : namespace_or_type_expr opt_nullable", +//t "type_expression : namespace_or_type_expr pointer_stars", +//t "type_expression : builtin_type_expression", +//t "void_invalid : VOID", +//t "builtin_type_expression : builtin_types opt_nullable", +//t "builtin_type_expression : builtin_types pointer_stars", +//t "builtin_type_expression : VOID pointer_stars", +//t "type_list : base_type_name", +//t "type_list : type_list COMMA base_type_name", +//t "base_type_name : type", +//t "builtin_types : OBJECT", +//t "builtin_types : STRING", +//t "builtin_types : BOOL", +//t "builtin_types : DECIMAL", +//t "builtin_types : FLOAT", +//t "builtin_types : DOUBLE", +//t "builtin_types : integral_type", +//t "integral_type : SBYTE", +//t "integral_type : BYTE", +//t "integral_type : SHORT", +//t "integral_type : USHORT", +//t "integral_type : INT", +//t "integral_type : UINT", +//t "integral_type : LONG", +//t "integral_type : ULONG", +//t "integral_type : CHAR", +//t "primary_expression : primary_expression_or_type", +//t "primary_expression : literal", +//t "primary_expression : array_creation_expression", +//t "primary_expression : parenthesized_expression", +//t "primary_expression : default_value_expression", +//t "primary_expression : invocation_expression", +//t "primary_expression : element_access", +//t "primary_expression : this_access", +//t "primary_expression : base_access", +//t "primary_expression : post_increment_expression", +//t "primary_expression : post_decrement_expression", +//t "primary_expression : object_or_delegate_creation_expression", +//t "primary_expression : anonymous_type_expression", +//t "primary_expression : typeof_expression", +//t "primary_expression : sizeof_expression", +//t "primary_expression : checked_expression", +//t "primary_expression : unchecked_expression", +//t "primary_expression : pointer_member_access", +//t "primary_expression : anonymous_method_expression", +//t "primary_expression : undocumented_expressions", +//t "primary_expression_or_type : simple_name_expr", +//t "primary_expression_or_type : IDENTIFIER GENERATE_COMPLETION", +//t "primary_expression_or_type : member_access", +//t "literal : boolean_literal", +//t "literal : LITERAL", +//t "literal : NULL", +//t "boolean_literal : TRUE", +//t "boolean_literal : FALSE", +//t "open_parens_any : OPEN_PARENS", +//t "open_parens_any : OPEN_PARENS_CAST", +//t "close_parens : CLOSE_PARENS", +//t "close_parens : COMPLETE_COMPLETION", +//t "parenthesized_expression : OPEN_PARENS expression CLOSE_PARENS", +//t "parenthesized_expression : OPEN_PARENS expression COMPLETE_COMPLETION", +//t "member_access : primary_expression DOT identifier_inside_body opt_type_argument_list", +//t "member_access : primary_expression DOT identifier_inside_body generic_dimension", +//t "member_access : primary_expression INTERR_OPERATOR DOT identifier_inside_body opt_type_argument_list", +//t "member_access : builtin_types DOT identifier_inside_body opt_type_argument_list", +//t "member_access : BASE DOT identifier_inside_body opt_type_argument_list", +//t "member_access : AWAIT DOT identifier_inside_body opt_type_argument_list", +//t "member_access : qualified_alias_member identifier_inside_body opt_type_argument_list", +//t "member_access : qualified_alias_member identifier_inside_body generic_dimension", +//t "member_access : primary_expression DOT GENERATE_COMPLETION", +//t "member_access : primary_expression DOT IDENTIFIER GENERATE_COMPLETION", +//t "member_access : builtin_types DOT GENERATE_COMPLETION", +//t "member_access : builtin_types DOT IDENTIFIER GENERATE_COMPLETION", +//t "invocation_expression : primary_expression open_parens_any opt_argument_list close_parens", +//t "invocation_expression : primary_expression open_parens_any argument_list error", +//t "invocation_expression : primary_expression open_parens_any error", +//t "opt_object_or_collection_initializer :", +//t "opt_object_or_collection_initializer : object_or_collection_initializer", +//t "object_or_collection_initializer : OPEN_BRACE opt_member_initializer_list close_brace_or_complete_completion", +//t "object_or_collection_initializer : OPEN_BRACE member_initializer_list COMMA CLOSE_BRACE", +//t "opt_member_initializer_list :", +//t "opt_member_initializer_list : member_initializer_list", +//t "member_initializer_list : member_initializer", +//t "member_initializer_list : member_initializer_list COMMA member_initializer", +//t "member_initializer_list : member_initializer_list error", +//t "member_initializer : IDENTIFIER ASSIGN initializer_value", +//t "member_initializer : AWAIT ASSIGN initializer_value", +//t "member_initializer : GENERATE_COMPLETION", +//t "member_initializer : non_assignment_expression opt_COMPLETE_COMPLETION", +//t "member_initializer : OPEN_BRACE expression_list CLOSE_BRACE", +//t "member_initializer : OPEN_BRACKET_EXPR expression_list CLOSE_BRACKET ASSIGN initializer_value", +//t "member_initializer : OPEN_BRACE CLOSE_BRACE", +//t "initializer_value : expression", +//t "initializer_value : object_or_collection_initializer", +//t "opt_argument_list :", +//t "opt_argument_list : argument_list", +//t "argument_list : argument_or_named_argument", +//t "argument_list : argument_list COMMA argument", +//t "argument_list : argument_list COMMA named_argument", +//t "argument_list : argument_list COMMA error", +//t "argument_list : COMMA error", +//t "argument : expression", +//t "argument : non_simple_argument", +//t "argument_or_named_argument : argument", +//t "argument_or_named_argument : named_argument", +//t "non_simple_argument : REF variable_reference", +//t "non_simple_argument : OUT variable_reference", +//t "non_simple_argument : ARGLIST OPEN_PARENS argument_list CLOSE_PARENS", +//t "non_simple_argument : ARGLIST OPEN_PARENS CLOSE_PARENS", +//t "variable_reference : expression", +//t "element_access : primary_expression OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET", +//t "element_access : primary_expression INTERR_OPERATOR OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET", +//t "element_access : primary_expression OPEN_BRACKET_EXPR expression_list_arguments error", +//t "element_access : primary_expression OPEN_BRACKET_EXPR error", +//t "expression_list : expression_or_error", +//t "expression_list : expression_list COMMA expression_or_error", +//t "expression_list_arguments : expression_list_argument", +//t "expression_list_arguments : expression_list_arguments COMMA expression_list_argument", +//t "expression_list_argument : expression", +//t "expression_list_argument : named_argument", +//t "this_access : THIS", +//t "base_access : BASE OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET", +//t "base_access : BASE OPEN_BRACKET error", +//t "post_increment_expression : primary_expression OP_INC", +//t "post_decrement_expression : primary_expression OP_DEC", +//t "object_or_delegate_creation_expression : NEW new_expr_type open_parens_any opt_argument_list CLOSE_PARENS opt_object_or_collection_initializer", +//t "object_or_delegate_creation_expression : NEW new_expr_type object_or_collection_initializer", +//t "array_creation_expression : NEW new_expr_type OPEN_BRACKET_EXPR expression_list CLOSE_BRACKET opt_rank_specifier opt_array_initializer", +//t "array_creation_expression : NEW new_expr_type rank_specifiers opt_array_initializer", +//t "array_creation_expression : NEW rank_specifier array_initializer", +//t "array_creation_expression : NEW new_expr_type OPEN_BRACKET CLOSE_BRACKET OPEN_BRACKET_EXPR error CLOSE_BRACKET", +//t "array_creation_expression : NEW new_expr_type error", +//t "$$68 :", +//t "new_expr_type : $$68 simple_type", +//t "anonymous_type_expression : NEW OPEN_BRACE anonymous_type_parameters_opt_comma CLOSE_BRACE", +//t "anonymous_type_expression : NEW OPEN_BRACE GENERATE_COMPLETION", +//t "anonymous_type_parameters_opt_comma : anonymous_type_parameters_opt", +//t "anonymous_type_parameters_opt_comma : anonymous_type_parameters COMMA", +//t "anonymous_type_parameters_opt :", +//t "anonymous_type_parameters_opt : anonymous_type_parameters", +//t "anonymous_type_parameters : anonymous_type_parameter", +//t "anonymous_type_parameters : anonymous_type_parameters COMMA anonymous_type_parameter", +//t "anonymous_type_parameters : COMPLETE_COMPLETION", +//t "anonymous_type_parameters : anonymous_type_parameter COMPLETE_COMPLETION", +//t "anonymous_type_parameter : identifier_inside_body ASSIGN variable_initializer", +//t "anonymous_type_parameter : identifier_inside_body", +//t "anonymous_type_parameter : member_access", +//t "anonymous_type_parameter : error", +//t "opt_rank_specifier :", +//t "opt_rank_specifier : rank_specifiers", +//t "rank_specifiers : rank_specifier", +//t "rank_specifiers : rank_specifier rank_specifiers", +//t "rank_specifier : OPEN_BRACKET CLOSE_BRACKET", +//t "rank_specifier : OPEN_BRACKET dim_separators CLOSE_BRACKET", +//t "dim_separators : COMMA", +//t "dim_separators : dim_separators COMMA", +//t "opt_array_initializer :", +//t "opt_array_initializer : array_initializer", +//t "array_initializer : OPEN_BRACE CLOSE_BRACE", +//t "array_initializer : OPEN_BRACE variable_initializer_list opt_comma CLOSE_BRACE", +//t "variable_initializer_list : variable_initializer", +//t "variable_initializer_list : variable_initializer_list COMMA variable_initializer", +//t "typeof_expression : TYPEOF open_parens_any typeof_type_expression CLOSE_PARENS", +//t "typeof_type_expression : type_and_void", +//t "typeof_type_expression : error", +//t "generic_dimension : GENERIC_DIMENSION", +//t "qualified_alias_member : IDENTIFIER DOUBLE_COLON", +//t "sizeof_expression : SIZEOF open_parens_any type CLOSE_PARENS", +//t "sizeof_expression : SIZEOF open_parens_any type error", +//t "checked_expression : CHECKED open_parens_any expression CLOSE_PARENS", +//t "checked_expression : CHECKED error", +//t "unchecked_expression : UNCHECKED open_parens_any expression CLOSE_PARENS", +//t "unchecked_expression : UNCHECKED error", +//t "pointer_member_access : primary_expression OP_PTR IDENTIFIER opt_type_argument_list", +//t "$$69 :", +//t "anonymous_method_expression : DELEGATE opt_anonymous_method_signature $$69 block", +//t "$$70 :", +//t "anonymous_method_expression : ASYNC DELEGATE opt_anonymous_method_signature $$70 block", +//t "opt_anonymous_method_signature :", +//t "opt_anonymous_method_signature : anonymous_method_signature", +//t "$$71 :", +//t "anonymous_method_signature : OPEN_PARENS $$71 opt_formal_parameter_list CLOSE_PARENS", +//t "default_value_expression : DEFAULT open_parens_any type CLOSE_PARENS", +//t "unary_expression : primary_expression", +//t "unary_expression : BANG prefixed_unary_expression", +//t "unary_expression : TILDE prefixed_unary_expression", +//t "unary_expression : OPEN_PARENS_CAST type CLOSE_PARENS prefixed_unary_expression", +//t "unary_expression : AWAIT prefixed_unary_expression", +//t "unary_expression : BANG error", +//t "unary_expression : TILDE error", +//t "unary_expression : OPEN_PARENS_CAST type CLOSE_PARENS error", +//t "unary_expression : AWAIT error", +//t "prefixed_unary_expression : unary_expression", +//t "prefixed_unary_expression : PLUS prefixed_unary_expression", +//t "prefixed_unary_expression : MINUS prefixed_unary_expression", +//t "prefixed_unary_expression : OP_INC prefixed_unary_expression", +//t "prefixed_unary_expression : OP_DEC prefixed_unary_expression", +//t "prefixed_unary_expression : STAR prefixed_unary_expression", +//t "prefixed_unary_expression : BITWISE_AND prefixed_unary_expression", +//t "prefixed_unary_expression : PLUS error", +//t "prefixed_unary_expression : MINUS error", +//t "prefixed_unary_expression : OP_INC error", +//t "prefixed_unary_expression : OP_DEC error", +//t "prefixed_unary_expression : STAR error", +//t "prefixed_unary_expression : BITWISE_AND error", +//t "multiplicative_expression : prefixed_unary_expression", +//t "multiplicative_expression : multiplicative_expression STAR prefixed_unary_expression", +//t "multiplicative_expression : multiplicative_expression DIV prefixed_unary_expression", +//t "multiplicative_expression : multiplicative_expression PERCENT prefixed_unary_expression", +//t "multiplicative_expression : multiplicative_expression STAR error", +//t "multiplicative_expression : multiplicative_expression DIV error", +//t "multiplicative_expression : multiplicative_expression PERCENT error", +//t "additive_expression : multiplicative_expression", +//t "additive_expression : additive_expression PLUS multiplicative_expression", +//t "additive_expression : additive_expression MINUS multiplicative_expression", +//t "additive_expression : additive_expression PLUS error", +//t "additive_expression : additive_expression MINUS error", +//t "additive_expression : additive_expression AS type", +//t "additive_expression : additive_expression IS is_match_expr opt_identifier", +//t "additive_expression : additive_expression AS error", +//t "additive_expression : additive_expression IS error", +//t "additive_expression : AWAIT IS type", +//t "additive_expression : AWAIT AS type", +//t "is_match_expr : match_type", +//t "is_match_expr : match_type rank_specifiers", +//t "is_match_expr : literal", +//t "is_match_expr : PLUS prefixed_unary_expression", +//t "is_match_expr : MINUS prefixed_unary_expression", +//t "match_type : primary_expression_or_type opt_nullable", +//t "match_type : primary_expression_or_type pointer_stars", +//t "match_type : builtin_type_expression", +//t "match_type : void_invalid", +//t "shift_expression : additive_expression", +//t "shift_expression : shift_expression OP_SHIFT_LEFT additive_expression", +//t "shift_expression : shift_expression OP_SHIFT_RIGHT additive_expression", +//t "shift_expression : shift_expression OP_SHIFT_LEFT error", +//t "shift_expression : shift_expression OP_SHIFT_RIGHT error", +//t "relational_expression : shift_expression", +//t "relational_expression : relational_expression OP_LT shift_expression", +//t "relational_expression : relational_expression OP_GT shift_expression", +//t "relational_expression : relational_expression OP_LE shift_expression", +//t "relational_expression : relational_expression OP_GE shift_expression", +//t "relational_expression : relational_expression OP_LT error", +//t "relational_expression : relational_expression OP_GT error", +//t "relational_expression : relational_expression OP_LE error", +//t "relational_expression : relational_expression OP_GE error", +//t "equality_expression : relational_expression", +//t "equality_expression : equality_expression OP_EQ relational_expression", +//t "equality_expression : equality_expression OP_NE relational_expression", +//t "equality_expression : equality_expression OP_EQ error", +//t "equality_expression : equality_expression OP_NE error", +//t "and_expression : equality_expression", +//t "and_expression : and_expression BITWISE_AND equality_expression", +//t "and_expression : and_expression BITWISE_AND error", +//t "exclusive_or_expression : and_expression", +//t "exclusive_or_expression : exclusive_or_expression CARRET and_expression", +//t "exclusive_or_expression : exclusive_or_expression CARRET error", +//t "inclusive_or_expression : exclusive_or_expression", +//t "inclusive_or_expression : inclusive_or_expression BITWISE_OR exclusive_or_expression", +//t "inclusive_or_expression : inclusive_or_expression BITWISE_OR error", +//t "conditional_and_expression : inclusive_or_expression", +//t "conditional_and_expression : conditional_and_expression OP_AND inclusive_or_expression", +//t "conditional_and_expression : conditional_and_expression OP_AND error", +//t "conditional_or_expression : conditional_and_expression", +//t "conditional_or_expression : conditional_or_expression OP_OR conditional_and_expression", +//t "conditional_or_expression : conditional_or_expression OP_OR error", +//t "null_coalescing_expression : conditional_or_expression", +//t "null_coalescing_expression : conditional_or_expression OP_COALESCING null_coalescing_expression", +//t "conditional_expression : null_coalescing_expression", +//t "conditional_expression : null_coalescing_expression INTERR expression COLON expression", +//t "conditional_expression : null_coalescing_expression INTERR expression error", +//t "conditional_expression : null_coalescing_expression INTERR expression COLON error", +//t "conditional_expression : null_coalescing_expression INTERR expression COLON CLOSE_BRACE", +//t "assignment_expression : prefixed_unary_expression ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_MULT_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_DIV_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_MOD_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_ADD_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_SUB_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_SHIFT_LEFT_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_SHIFT_RIGHT_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_AND_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_OR_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_XOR_ASSIGN expression", +//t "lambda_parameter_list : lambda_parameter", +//t "lambda_parameter_list : lambda_parameter_list COMMA lambda_parameter", +//t "lambda_parameter : parameter_modifier parameter_type identifier_inside_body", +//t "lambda_parameter : parameter_type identifier_inside_body", +//t "lambda_parameter : IDENTIFIER", +//t "lambda_parameter : AWAIT", +//t "opt_lambda_parameter_list :", +//t "opt_lambda_parameter_list : lambda_parameter_list", +//t "$$72 :", +//t "lambda_expression_body : $$72 expression", +//t "lambda_expression_body : block", +//t "lambda_expression_body : error", +//t "expression_or_error : expression", +//t "expression_or_error : error", +//t "$$73 :", +//t "lambda_expression : IDENTIFIER ARROW $$73 lambda_expression_body", +//t "$$74 :", +//t "lambda_expression : AWAIT ARROW $$74 lambda_expression_body", +//t "$$75 :", +//t "lambda_expression : ASYNC identifier_inside_body ARROW $$75 lambda_expression_body", +//t "$$76 :", +//t "$$77 :", +//t "lambda_expression : OPEN_PARENS_LAMBDA $$76 opt_lambda_parameter_list CLOSE_PARENS ARROW $$77 lambda_expression_body", +//t "$$78 :", +//t "$$79 :", +//t "lambda_expression : ASYNC OPEN_PARENS_LAMBDA $$78 opt_lambda_parameter_list CLOSE_PARENS ARROW $$79 lambda_expression_body", +//t "expression : assignment_expression", +//t "expression : non_assignment_expression", +//t "non_assignment_expression : conditional_expression", +//t "non_assignment_expression : lambda_expression", +//t "non_assignment_expression : query_expression", +//t "non_assignment_expression : ARGLIST", +//t "undocumented_expressions : REFVALUE OPEN_PARENS non_assignment_expression COMMA type CLOSE_PARENS", +//t "undocumented_expressions : REFTYPE open_parens_any expression CLOSE_PARENS", +//t "undocumented_expressions : MAKEREF open_parens_any expression CLOSE_PARENS", +//t "constant_expression : expression", +//t "boolean_expression : expression", +//t "opt_primary_parameters :", +//t "opt_primary_parameters : primary_parameters", +//t "primary_parameters : OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS", +//t "opt_primary_parameters_with_class_base :", +//t "opt_primary_parameters_with_class_base : class_base", +//t "opt_primary_parameters_with_class_base : primary_parameters", +//t "opt_primary_parameters_with_class_base : primary_parameters class_base", +//t "$$80 :", +//t "opt_primary_parameters_with_class_base : primary_parameters class_base OPEN_PARENS $$80 opt_argument_list CLOSE_PARENS", +//t "$$81 :", +//t "$$82 :", +//t "$$83 :", +//t "$$84 :", +//t "class_declaration : opt_attributes opt_modifiers opt_partial CLASS $$81 type_declaration_name $$82 opt_primary_parameters_with_class_base opt_type_parameter_constraints_clauses $$83 OPEN_BRACE opt_class_member_declarations CLOSE_BRACE $$84 opt_semicolon", +//t "opt_partial :", +//t "opt_partial : PARTIAL", +//t "opt_modifiers :", +//t "opt_modifiers : modifiers", +//t "modifiers : modifier", +//t "modifiers : modifiers modifier", +//t "modifier : NEW", +//t "modifier : PUBLIC", +//t "modifier : PROTECTED", +//t "modifier : INTERNAL", +//t "modifier : PRIVATE", +//t "modifier : ABSTRACT", +//t "modifier : SEALED", +//t "modifier : STATIC", +//t "modifier : READONLY", +//t "modifier : VIRTUAL", +//t "modifier : OVERRIDE", +//t "modifier : EXTERN", +//t "modifier : VOLATILE", +//t "modifier : UNSAFE", +//t "modifier : ASYNC", +//t "opt_class_base :", +//t "opt_class_base : class_base", +//t "class_base : COLON type_list", +//t "class_base : COLON type_list error", +//t "opt_type_parameter_constraints_clauses :", +//t "opt_type_parameter_constraints_clauses : type_parameter_constraints_clauses", +//t "type_parameter_constraints_clauses : type_parameter_constraints_clause", +//t "type_parameter_constraints_clauses : type_parameter_constraints_clauses type_parameter_constraints_clause", +//t "type_parameter_constraints_clause : WHERE IDENTIFIER COLON type_parameter_constraints", +//t "type_parameter_constraints_clause : WHERE IDENTIFIER error", +//t "type_parameter_constraints : type_parameter_constraint", +//t "type_parameter_constraints : type_parameter_constraints COMMA type_parameter_constraint", +//t "type_parameter_constraint : type", +//t "type_parameter_constraint : NEW OPEN_PARENS CLOSE_PARENS", +//t "type_parameter_constraint : CLASS", +//t "type_parameter_constraint : STRUCT", +//t "opt_type_parameter_variance :", +//t "opt_type_parameter_variance : type_parameter_variance", +//t "type_parameter_variance : OUT", +//t "type_parameter_variance : IN", +//t "$$85 :", +//t "block : OPEN_BRACE $$85 opt_statement_list block_end", +//t "block_end : CLOSE_BRACE", +//t "block_end : COMPLETE_COMPLETION", +//t "$$86 :", +//t "block_prepared : OPEN_BRACE $$86 opt_statement_list CLOSE_BRACE", +//t "block_prepared : CLOSE_BRACE", +//t "$$87 :", +//t "block_prepared_strict : OPEN_BRACE $$87 opt_statement_list CLOSE_BRACE", +//t "opt_statement_list :", +//t "opt_statement_list : statement_list", +//t "statement_list : statement", +//t "statement_list : statement_list statement", +//t "statement : block_variable_declaration", +//t "statement : valid_declaration_statement", +//t "statement : labeled_statement", +//t "statement : IDENTIFIER error", +//t "statement : error", +//t "interactive_statement_list : interactive_statement", +//t "interactive_statement_list : interactive_statement_list interactive_statement", +//t "interactive_statement : block_variable_declaration", +//t "interactive_statement : interactive_valid_declaration_statement", +//t "interactive_statement : labeled_statement", +//t "valid_declaration_statement : block", +//t "valid_declaration_statement : empty_statement", +//t "valid_declaration_statement : expression_statement", +//t "valid_declaration_statement : selection_statement", +//t "valid_declaration_statement : iteration_statement", +//t "valid_declaration_statement : jump_statement", +//t "valid_declaration_statement : try_statement", +//t "valid_declaration_statement : checked_statement", +//t "valid_declaration_statement : unchecked_statement", +//t "valid_declaration_statement : lock_statement", +//t "valid_declaration_statement : using_statement", +//t "valid_declaration_statement : unsafe_statement", +//t "valid_declaration_statement : fixed_statement", +//t "interactive_valid_declaration_statement : block", +//t "interactive_valid_declaration_statement : empty_statement", +//t "interactive_valid_declaration_statement : interactive_expression_statement", +//t "interactive_valid_declaration_statement : selection_statement", +//t "interactive_valid_declaration_statement : iteration_statement", +//t "interactive_valid_declaration_statement : jump_statement", +//t "interactive_valid_declaration_statement : try_statement", +//t "interactive_valid_declaration_statement : checked_statement", +//t "interactive_valid_declaration_statement : unchecked_statement", +//t "interactive_valid_declaration_statement : lock_statement", +//t "interactive_valid_declaration_statement : using_statement", +//t "interactive_valid_declaration_statement : unsafe_statement", +//t "interactive_valid_declaration_statement : fixed_statement", +//t "embedded_statement : valid_declaration_statement", +//t "embedded_statement : block_variable_declaration", +//t "embedded_statement : labeled_statement", +//t "embedded_statement : error", +//t "empty_statement : SEMICOLON", +//t "$$88 :", +//t "labeled_statement : identifier_inside_body COLON $$88 statement", +//t "variable_type : variable_type_simple", +//t "variable_type : variable_type_simple rank_specifiers", +//t "variable_type_simple : primary_expression_or_type opt_nullable", +//t "variable_type_simple : primary_expression_or_type pointer_stars", +//t "variable_type_simple : builtin_type_expression", +//t "variable_type_simple : void_invalid", +//t "pointer_stars : pointer_star", +//t "pointer_stars : pointer_star pointer_stars", +//t "pointer_star : STAR", +//t "identifier_inside_body : IDENTIFIER", +//t "identifier_inside_body : AWAIT", +//t "$$89 :", +//t "block_variable_declaration : variable_type identifier_inside_body $$89 opt_local_variable_initializer opt_variable_declarators semicolon_or_handle_error_close_brace", +//t "$$90 :", +//t "block_variable_declaration : CONST variable_type identifier_inside_body $$90 const_variable_initializer opt_const_declarators SEMICOLON", +//t "semicolon_or_handle_error_close_brace : SEMICOLON", +//t "semicolon_or_handle_error_close_brace : CLOSE_BRACE", +//t "opt_local_variable_initializer :", +//t "opt_local_variable_initializer : ASSIGN block_variable_initializer", +//t "opt_local_variable_initializer : error", +//t "opt_variable_declarators :", +//t "opt_variable_declarators : variable_declarators", +//t "opt_using_or_fixed_variable_declarators :", +//t "opt_using_or_fixed_variable_declarators : variable_declarators", +//t "variable_declarators : variable_declarator", +//t "variable_declarators : variable_declarators variable_declarator", +//t "variable_declarator : COMMA identifier_inside_body", +//t "variable_declarator : COMMA identifier_inside_body ASSIGN block_variable_initializer", +//t "const_variable_initializer :", +//t "const_variable_initializer : ASSIGN constant_initializer_expr", +//t "opt_const_declarators :", +//t "opt_const_declarators : const_declarators", +//t "const_declarators : const_declarator", +//t "const_declarators : const_declarators const_declarator", +//t "const_declarator : COMMA identifier_inside_body ASSIGN constant_initializer_expr", +//t "block_variable_initializer : variable_initializer", +//t "block_variable_initializer : STACKALLOC simple_type OPEN_BRACKET_EXPR expression CLOSE_BRACKET", +//t "block_variable_initializer : STACKALLOC simple_type", +//t "expression_statement : statement_expression SEMICOLON", +//t "expression_statement : statement_expression COMPLETE_COMPLETION", +//t "expression_statement : statement_expression CLOSE_BRACE", +//t "interactive_expression_statement : interactive_statement_expression SEMICOLON", +//t "interactive_expression_statement : interactive_statement_expression COMPLETE_COMPLETION", +//t "statement_expression : expression", +//t "interactive_statement_expression : expression", +//t "interactive_statement_expression : error", +//t "selection_statement : if_statement", +//t "selection_statement : switch_statement", +//t "if_statement : IF open_parens_any boolean_expression CLOSE_PARENS embedded_statement", +//t "if_statement : IF open_parens_any boolean_expression CLOSE_PARENS embedded_statement ELSE embedded_statement", +//t "if_statement : IF open_parens_any boolean_expression error", +//t "$$91 :", +//t "switch_statement : SWITCH open_parens_any expression CLOSE_PARENS OPEN_BRACE $$91 opt_switch_sections CLOSE_BRACE", +//t "switch_statement : SWITCH open_parens_any expression error", +//t "opt_switch_sections :", +//t "opt_switch_sections : switch_sections", +//t "switch_sections : switch_section", +//t "switch_sections : switch_sections switch_section", +//t "switch_sections : error", +//t "switch_section : switch_labels statement_list", +//t "switch_labels : switch_label", +//t "switch_labels : switch_labels switch_label", +//t "switch_label : CASE constant_expression COLON", +//t "switch_label : CASE constant_expression error", +//t "switch_label : DEFAULT_COLON", +//t "iteration_statement : while_statement", +//t "iteration_statement : do_statement", +//t "iteration_statement : for_statement", +//t "iteration_statement : foreach_statement", +//t "while_statement : WHILE open_parens_any boolean_expression CLOSE_PARENS embedded_statement", +//t "while_statement : WHILE open_parens_any boolean_expression error", +//t "do_statement : DO embedded_statement WHILE open_parens_any boolean_expression CLOSE_PARENS SEMICOLON", +//t "do_statement : DO embedded_statement error", +//t "do_statement : DO embedded_statement WHILE open_parens_any boolean_expression error", +//t "$$92 :", +//t "for_statement : FOR open_parens_any $$92 for_statement_cont", +//t "$$93 :", +//t "for_statement_cont : opt_for_initializer SEMICOLON $$93 for_statement_condition", +//t "for_statement_cont : opt_for_initializer CLOSE_PARENS", +//t "$$94 :", +//t "for_statement_condition : opt_for_condition SEMICOLON $$94 for_statement_end", +//t "for_statement_condition : boolean_expression CLOSE_PARENS", +//t "for_statement_end : opt_for_iterator CLOSE_PARENS embedded_statement", +//t "for_statement_end : error", +//t "opt_for_initializer :", +//t "opt_for_initializer : for_initializer", +//t "$$95 :", +//t "for_initializer : variable_type identifier_inside_body $$95 opt_local_variable_initializer opt_variable_declarators", +//t "for_initializer : statement_expression_list", +//t "opt_for_condition :", +//t "opt_for_condition : boolean_expression", +//t "opt_for_iterator :", +//t "opt_for_iterator : for_iterator", +//t "for_iterator : statement_expression_list", +//t "statement_expression_list : statement_expression", +//t "statement_expression_list : statement_expression_list COMMA statement_expression", +//t "foreach_statement : FOREACH open_parens_any type error", +//t "foreach_statement : FOREACH open_parens_any type identifier_inside_body error", +//t "$$96 :", +//t "foreach_statement : FOREACH open_parens_any type identifier_inside_body IN expression CLOSE_PARENS $$96 embedded_statement", +//t "foreach_statement : FOREACH open_parens_any type identifier_inside_body error", +//t "foreach_statement : FOREACH open_parens_any type error", +//t "jump_statement : break_statement", +//t "jump_statement : continue_statement", +//t "jump_statement : goto_statement", +//t "jump_statement : return_statement", +//t "jump_statement : throw_statement", +//t "jump_statement : yield_statement", +//t "break_statement : BREAK SEMICOLON", +//t "continue_statement : CONTINUE SEMICOLON", +//t "continue_statement : CONTINUE error", +//t "goto_statement : GOTO identifier_inside_body SEMICOLON", +//t "goto_statement : GOTO CASE constant_expression SEMICOLON", +//t "goto_statement : GOTO DEFAULT SEMICOLON", +//t "return_statement : RETURN opt_expression SEMICOLON", +//t "return_statement : RETURN expression error", +//t "return_statement : RETURN error", +//t "throw_statement : THROW opt_expression SEMICOLON", +//t "throw_statement : THROW expression error", +//t "throw_statement : THROW error", +//t "yield_statement : identifier_inside_body RETURN opt_expression SEMICOLON", +//t "yield_statement : identifier_inside_body RETURN expression error", +//t "yield_statement : identifier_inside_body BREAK SEMICOLON", +//t "opt_expression :", +//t "opt_expression : expression", +//t "try_statement : TRY block catch_clauses", +//t "try_statement : TRY block FINALLY block", +//t "try_statement : TRY block catch_clauses FINALLY block", +//t "try_statement : TRY block error", +//t "catch_clauses : catch_clause", +//t "catch_clauses : catch_clauses catch_clause", +//t "opt_identifier :", +//t "opt_identifier : identifier_inside_body", +//t "catch_clause : CATCH opt_catch_filter block", +//t "$$97 :", +//t "catch_clause : CATCH open_parens_any type opt_identifier CLOSE_PARENS $$97 opt_catch_filter block_prepared", +//t "catch_clause : CATCH open_parens_any error", +//t "catch_clause : CATCH open_parens_any type opt_identifier CLOSE_PARENS error", +//t "opt_catch_filter :", +//t "opt_catch_filter : IF open_parens_any expression CLOSE_PARENS", +//t "checked_statement : CHECKED block", +//t "unchecked_statement : UNCHECKED block", +//t "$$98 :", +//t "unsafe_statement : UNSAFE $$98 block", +//t "lock_statement : LOCK open_parens_any expression CLOSE_PARENS embedded_statement", +//t "lock_statement : LOCK open_parens_any expression error", +//t "$$99 :", +//t "$$100 :", +//t "fixed_statement : FIXED open_parens_any variable_type identifier_inside_body $$99 using_or_fixed_variable_initializer opt_using_or_fixed_variable_declarators CLOSE_PARENS $$100 embedded_statement", +//t "$$101 :", +//t "$$102 :", +//t "using_statement : USING open_parens_any variable_type identifier_inside_body $$101 using_initialization CLOSE_PARENS $$102 embedded_statement", +//t "using_statement : USING open_parens_any expression CLOSE_PARENS embedded_statement", +//t "using_statement : USING open_parens_any expression error", +//t "using_initialization : using_or_fixed_variable_initializer opt_using_or_fixed_variable_declarators", +//t "using_initialization : error", +//t "using_or_fixed_variable_initializer :", +//t "using_or_fixed_variable_initializer : ASSIGN variable_initializer", +//t "query_expression : first_from_clause query_body", +//t "query_expression : nested_from_clause query_body", +//t "query_expression : first_from_clause COMPLETE_COMPLETION", +//t "query_expression : nested_from_clause COMPLETE_COMPLETION", +//t "first_from_clause : FROM_FIRST identifier_inside_body IN expression", +//t "first_from_clause : FROM_FIRST type identifier_inside_body IN expression", +//t "nested_from_clause : FROM identifier_inside_body IN expression", +//t "nested_from_clause : FROM type identifier_inside_body IN expression", +//t "$$103 :", +//t "from_clause : FROM identifier_inside_body IN $$103 expression_or_error", +//t "$$104 :", +//t "from_clause : FROM type identifier_inside_body IN $$104 expression_or_error", +//t "query_body : query_body_clauses select_or_group_clause opt_query_continuation", +//t "query_body : select_or_group_clause opt_query_continuation", +//t "query_body : query_body_clauses COMPLETE_COMPLETION", +//t "query_body : query_body_clauses error", +//t "query_body : error", +//t "$$105 :", +//t "select_or_group_clause : SELECT $$105 expression_or_error", +//t "$$106 :", +//t "$$107 :", +//t "select_or_group_clause : GROUP $$106 expression_or_error $$107 by_expression", +//t "by_expression : BY expression_or_error", +//t "by_expression : error", +//t "query_body_clauses : query_body_clause", +//t "query_body_clauses : query_body_clauses query_body_clause", +//t "query_body_clause : from_clause", +//t "query_body_clause : let_clause", +//t "query_body_clause : where_clause", +//t "query_body_clause : join_clause", +//t "query_body_clause : orderby_clause", +//t "$$108 :", +//t "let_clause : LET identifier_inside_body ASSIGN $$108 expression_or_error", +//t "$$109 :", +//t "where_clause : WHERE $$109 expression_or_error", +//t "$$110 :", +//t "$$111 :", +//t "$$112 :", +//t "join_clause : JOIN identifier_inside_body IN $$110 expression_or_error ON $$111 expression_or_error EQUALS $$112 expression_or_error opt_join_into", +//t "$$113 :", +//t "$$114 :", +//t "$$115 :", +//t "join_clause : JOIN type identifier_inside_body IN $$113 expression_or_error ON $$114 expression_or_error EQUALS $$115 expression_or_error opt_join_into", +//t "opt_join_into :", +//t "opt_join_into : INTO identifier_inside_body", +//t "$$116 :", +//t "orderby_clause : ORDERBY $$116 orderings", +//t "orderings : order_by", +//t "$$117 :", +//t "orderings : order_by COMMA $$117 orderings_then_by", +//t "orderings_then_by : then_by", +//t "$$118 :", +//t "orderings_then_by : orderings_then_by COMMA $$118 then_by", +//t "order_by : expression", +//t "order_by : expression ASCENDING", +//t "order_by : expression DESCENDING", +//t "then_by : expression", +//t "then_by : expression ASCENDING", +//t "then_by : expression DESCENDING", +//t "opt_query_continuation :", +//t "$$119 :", +//t "opt_query_continuation : INTO identifier_inside_body $$119 query_body", +//t "interactive_parsing : EVAL_STATEMENT_PARSER EOF", +//t "interactive_parsing : EVAL_USING_DECLARATIONS_UNIT_PARSER using_directives opt_COMPLETE_COMPLETION", +//t "$$120 :", +//t "interactive_parsing : EVAL_STATEMENT_PARSER $$120 interactive_statement_list opt_COMPLETE_COMPLETION", +//t "interactive_parsing : EVAL_COMPILATION_UNIT_PARSER interactive_compilation_unit", +//t "interactive_compilation_unit : opt_extern_alias_directives opt_using_directives", +//t "interactive_compilation_unit : opt_extern_alias_directives opt_using_directives namespace_or_type_declarations", +//t "opt_COMPLETE_COMPLETION :", +//t "opt_COMPLETE_COMPLETION : COMPLETE_COMPLETION", +//t "close_brace_or_complete_completion : CLOSE_BRACE", +//t "close_brace_or_complete_completion : COMPLETE_COMPLETION", +//t "documentation_parsing : DOC_SEE doc_cref", +//t "doc_cref : doc_type_declaration_name opt_doc_method_sig", +//t "doc_cref : builtin_types opt_doc_method_sig", +//t "doc_cref : VOID opt_doc_method_sig", +//t "doc_cref : builtin_types DOT IDENTIFIER opt_doc_method_sig", +//t "doc_cref : doc_type_declaration_name DOT THIS", +//t "$$121 :", +//t "doc_cref : doc_type_declaration_name DOT THIS OPEN_BRACKET $$121 opt_doc_parameters CLOSE_BRACKET", +//t "doc_cref : EXPLICIT OPERATOR type opt_doc_method_sig", +//t "doc_cref : IMPLICIT OPERATOR type opt_doc_method_sig", +//t "doc_cref : OPERATOR overloadable_operator opt_doc_method_sig", +//t "doc_type_declaration_name : type_declaration_name", +//t "doc_type_declaration_name : doc_type_declaration_name DOT type_declaration_name", +//t "opt_doc_method_sig :", +//t "$$122 :", +//t "opt_doc_method_sig : OPEN_PARENS $$122 opt_doc_parameters CLOSE_PARENS", +//t "opt_doc_parameters :", +//t "opt_doc_parameters : doc_parameters", +//t "doc_parameters : doc_parameter", +//t "doc_parameters : doc_parameters COMMA doc_parameter", +//t "doc_parameter : opt_parameter_modifier parameter_type", +//t }; +//t public static string getRule (int index) { +//t return yyRule [index]; +//t } +//t} + protected static readonly string [] yyNames = { + "end-of-file",null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,"EOF","NONE","ERROR", + "FIRST_KEYWORD","ABSTRACT","AS","ADD","BASE","BOOL","BREAK","BYTE", + "CASE","CATCH","CHAR","CHECKED","CLASS","CONST","CONTINUE","DECIMAL", + "DEFAULT","DELEGATE","DO","DOUBLE","ELSE","ENUM","EVENT","EXPLICIT", + "EXTERN","FALSE","FINALLY","FIXED","FLOAT","FOR","FOREACH","GOTO", + "IF","IMPLICIT","IN","INT","INTERFACE","INTERNAL","IS","LOCK","LONG", + "NAMESPACE","NEW","NULL","OBJECT","OPERATOR","OUT","OVERRIDE", + "PARAMS","PRIVATE","PROTECTED","PUBLIC","READONLY","REF","RETURN", + "REMOVE","SBYTE","SEALED","SHORT","SIZEOF","STACKALLOC","STATIC", + "STRING","STRUCT","SWITCH","THIS","THROW","TRUE","TRY","TYPEOF", + "UINT","ULONG","UNCHECKED","UNSAFE","USHORT","USING","VIRTUAL","VOID", + "VOLATILE","WHERE","WHILE","ARGLIST","PARTIAL","ARROW","FROM", + "FROM_FIRST","JOIN","ON","EQUALS","SELECT","GROUP","BY","LET", + "ORDERBY","ASCENDING","DESCENDING","INTO","INTERR_NULLABLE", + "EXTERN_ALIAS","REFVALUE","REFTYPE","MAKEREF","ASYNC","AWAIT", + "INTERR_OPERATOR","GET","SET","LAST_KEYWORD","OPEN_BRACE", + "CLOSE_BRACE","OPEN_BRACKET","CLOSE_BRACKET","OPEN_PARENS", + "CLOSE_PARENS","DOT","COMMA","COLON","SEMICOLON","TILDE","PLUS", + "MINUS","BANG","ASSIGN","OP_LT","OP_GT","BITWISE_AND","BITWISE_OR", + "STAR","PERCENT","DIV","CARRET","INTERR","DOUBLE_COLON","OP_INC", + "OP_DEC","OP_SHIFT_LEFT","OP_SHIFT_RIGHT","OP_LE","OP_GE","OP_EQ", + "OP_NE","OP_AND","OP_OR","OP_MULT_ASSIGN","OP_DIV_ASSIGN", + "OP_MOD_ASSIGN","OP_ADD_ASSIGN","OP_SUB_ASSIGN", + "OP_SHIFT_LEFT_ASSIGN","OP_SHIFT_RIGHT_ASSIGN","OP_AND_ASSIGN", + "OP_XOR_ASSIGN","OP_OR_ASSIGN","OP_PTR","OP_COALESCING", + "OP_GENERICS_LT","OP_GENERICS_LT_DECL","OP_GENERICS_GT","LITERAL", + "IDENTIFIER","OPEN_PARENS_LAMBDA","OPEN_PARENS_CAST", + "GENERIC_DIMENSION","DEFAULT_COLON","OPEN_BRACKET_EXPR", + "EVAL_STATEMENT_PARSER","EVAL_COMPILATION_UNIT_PARSER", + "EVAL_USING_DECLARATIONS_UNIT_PARSER","DOC_SEE","GENERATE_COMPLETION", + "COMPLETE_COMPLETION","UMINUS", + }; + + /** index-checked interface to yyNames[]. + @param token single character or %token value. + @return token name or [illegal] or [unknown]. + */ +//t public static string yyname (int token) { +//t if ((token < 0) || (token > yyNames.Length)) return "[illegal]"; +//t string name; +//t if ((name = yyNames[token]) != null) return name; +//t return "[unknown]"; +//t } + +#pragma warning disable 414 + int yyExpectingState; +#pragma warning restore 414 + /** computes list of expected tokens on error by tracing the tables. + @param state for which to compute the list. + @return list of token names. + */ + protected int [] yyExpectingTokens (int state){ + int token, n, len = 0; + bool[] ok = new bool[yyNames.Length]; + if ((n = yySindex[state]) != 0) + for (token = n < 0 ? -n : 0; + (token < yyNames.Length) && (n+token < yyTable.Length); ++ token) + if (yyCheck[n+token] == token && !ok[token] && yyNames[token] != null) { + ++ len; + ok[token] = true; + } + if ((n = yyRindex[state]) != 0) + for (token = n < 0 ? -n : 0; + (token < yyNames.Length) && (n+token < yyTable.Length); ++ token) + if (yyCheck[n+token] == token && !ok[token] && yyNames[token] != null) { + ++ len; + ok[token] = true; + } + int [] result = new int [len]; + for (n = token = 0; n < len; ++ token) + if (ok[token]) result[n++] = token; + return result; + } + protected string[] yyExpecting (int state) { + int [] tokens = yyExpectingTokens (state); + string [] result = new string[tokens.Length]; + for (int n = 0; n < tokens.Length; n++) + result[n++] = yyNames[tokens [n]]; + return result; + } + + /** the generated parser, with debugging messages. + Maintains a state and a value stack, currently with fixed maximum size. + @param yyLex scanner. + @param yydebug debug message writer implementing yyDebug, or null. + @return result of the last reduction, if any. + @throws yyException on irrecoverable parse error. + */ + internal Object yyparse (yyParser.yyInput yyLex, Object yyd) + { +//t this.debug = (yydebug.yyDebug)yyd; + return yyparse(yyLex); + } + + /** initial size and increment of the state/value stack [default 256]. + This is not final so that it can be overwritten outside of invocations + of yyparse(). + */ + protected int yyMax; + + /** executed at the beginning of a reduce action. + Used as $$ = yyDefault($1), prior to the user-specified action, if any. + Can be overwritten to provide deep copy, etc. + @param first value for $1, or null. + @return first. + */ + protected Object yyDefault (Object first) { + return first; + } + + static int[] global_yyStates; + static object[] global_yyVals; +#pragma warning disable 649 + protected bool use_global_stacks; +#pragma warning restore 649 + object[] yyVals; // value stack + object yyVal; // value stack ptr + int yyToken; // current input + int yyTop; + + /** the generated parser. + Maintains a state and a value stack, currently with fixed maximum size. + @param yyLex scanner. + @return result of the last reduction, if any. + @throws yyException on irrecoverable parse error. + */ + internal Object yyparse (yyParser.yyInput yyLex) + { + if (yyMax <= 0) yyMax = 256; // initial size + int yyState = 0; // state stack ptr + int [] yyStates; // state stack + yyVal = null; + yyToken = -1; + int yyErrorFlag = 0; // #tks to shift + if (use_global_stacks && global_yyStates != null) { + yyVals = global_yyVals; + yyStates = global_yyStates; + } else { + yyVals = new object [yyMax]; + yyStates = new int [yyMax]; + if (use_global_stacks) { + global_yyVals = yyVals; + global_yyStates = yyStates; + } + } + + /*yyLoop:*/ for (yyTop = 0;; ++ yyTop) { + if (yyTop >= yyStates.Length) { // dynamically increase + global::System.Array.Resize (ref yyStates, yyStates.Length+yyMax); + global::System.Array.Resize (ref yyVals, yyVals.Length+yyMax); + } + yyStates[yyTop] = yyState; + yyVals[yyTop] = yyVal; +//t if (debug != null) debug.push(yyState, yyVal); + + /*yyDiscarded:*/ while (true) { // discarding a token does not change stack + int yyN; + if ((yyN = yyDefRed[yyState]) == 0) { // else [default] reduce (yyN) + if (yyToken < 0) { + yyToken = yyLex.advance() ? yyLex.token() : 0; +//t if (debug != null) +//t debug.lex(yyState, yyToken, yyname(yyToken), yyLex.value()); + } + if ((yyN = yySindex[yyState]) != 0 && ((yyN += yyToken) >= 0) + && (yyN < yyTable.Length) && (yyCheck[yyN] == yyToken)) { +//t if (debug != null) +//t debug.shift(yyState, yyTable[yyN], yyErrorFlag-1); + yyState = yyTable[yyN]; // shift to yyN + yyVal = yyLex.value(); + yyToken = -1; + if (yyErrorFlag > 0) -- yyErrorFlag; + goto continue_yyLoop; + } + if ((yyN = yyRindex[yyState]) != 0 && (yyN += yyToken) >= 0 + && yyN < yyTable.Length && yyCheck[yyN] == yyToken) + yyN = yyTable[yyN]; // reduce (yyN) + else + switch (yyErrorFlag) { + + case 0: + yyExpectingState = yyState; + // yyerror(String.Format ("syntax error, got token `{0}'", yyname (yyToken)), yyExpecting(yyState)); +//t if (debug != null) debug.error("syntax error"); + if (yyToken == 0 /*eof*/ || yyToken == eof_token) throw new yyParser.yyUnexpectedEof (); + goto case 1; + case 1: case 2: + yyErrorFlag = 3; + do { + if ((yyN = yySindex[yyStates[yyTop]]) != 0 + && (yyN += Token.yyErrorCode) >= 0 && yyN < yyTable.Length + && yyCheck[yyN] == Token.yyErrorCode) { +//t if (debug != null) +//t debug.shift(yyStates[yyTop], yyTable[yyN], 3); + yyState = yyTable[yyN]; + yyVal = yyLex.value(); + goto continue_yyLoop; + } +//t if (debug != null) debug.pop(yyStates[yyTop]); + } while (-- yyTop >= 0); +//t if (debug != null) debug.reject(); + throw new yyParser.yyException("irrecoverable syntax error"); + + case 3: + if (yyToken == 0) { +//t if (debug != null) debug.reject(); + throw new yyParser.yyException("irrecoverable syntax error at end-of-file"); + } +//t if (debug != null) +//t debug.discard(yyState, yyToken, yyname(yyToken), +//t yyLex.value()); + yyToken = -1; + goto continue_yyDiscarded; // leave stack alone + } + } + int yyV = yyTop + 1-yyLen[yyN]; +//t if (debug != null) +//t debug.reduce(yyState, yyStates[yyV-1], yyN, YYRules.getRule (yyN), yyLen[yyN]); + yyVal = yyV > yyTop ? null : yyVals[yyV]; // yyVal = yyDefault(yyV > yyTop ? null : yyVals[yyV]); + switch (yyN) { +case 1: +#line 389 "cs-parser.jay" + { + Lexer.check_incorrect_doc_comment (); + } + break; +case 2: +#line 390 "cs-parser.jay" + { Lexer.CompleteOnEOF = false; } + break; +case 6: + case_6(); + break; +case 7: +#line 409 "cs-parser.jay" + { + module.AddAttributes ((Attributes) yyVals[0+yyTop], current_namespace); + } + break; +case 8: + case_8(); + break; +case 13: + case_13(); + break; +case 14: +#line 454 "cs-parser.jay" + { + Error_SyntaxError (yyToken); + } + break; +case 17: + case_17(); + break; +case 18: + case_18(); + break; +case 19: + case_19(); + break; +case 20: + case_20(); + break; +case 21: + case_21(); + break; +case 22: + case_22(); + break; +case 23: + case_23(); + break; +case 24: + case_24(); + break; +case 27: + case_27(); + break; +case 28: + case_28(); + break; +case 29: + case_29(); + break; +case 30: + case_30(); + break; +case 43: + case_43(); + break; +case 44: +#line 638 "cs-parser.jay" + { + current_namespace.DeclarationFound = true; + } + break; +case 45: + case_45(); + break; +case 53: + case_53(); + break; +case 54: + case_54(); + break; +case 55: + case_55(); + break; +case 56: + case_56(); + break; +case 57: + case_57(); + break; +case 58: + case_58(); + break; +case 59: + case_59(); + break; +case 60: + case_60(); + break; +case 61: + case_61(); + break; +case 62: + case_62(); + break; +case 63: +#line 763 "cs-parser.jay" + { yyVal = "event"; PushLocation (GetLocation (yyVals[0+yyTop])); } + break; +case 64: +#line 764 "cs-parser.jay" + { yyVal = "return"; PushLocation (GetLocation (yyVals[0+yyTop])); } + break; +case 65: +#line 771 "cs-parser.jay" + { + yyVal = new List (4) { (Attribute) yyVals[0+yyTop] }; + } + break; +case 66: + case_66(); + break; +case 67: +#line 788 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 68: + case_68(); + break; +case 70: +#line 816 "cs-parser.jay" + { yyVal = null; HadAttributeParens = false; } + break; +case 71: + case_71(); + break; +case 72: +#line 828 "cs-parser.jay" + { yyVal = null; } + break; +case 73: + case_73(); + break; +case 74: + case_74(); + break; +case 75: + case_75(); + break; +case 76: + case_76(); + break; +case 77: +#line 872 "cs-parser.jay" + { + yyVal = new Argument ((Expression) yyVals[0+yyTop]); + } + break; +case 79: + case_79(); + break; +case 80: +#line 885 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 81: + case_81(); + break; +case 82: + case_82(); + break; +case 83: +#line 911 "cs-parser.jay" + { yyVal = null; } + break; +case 84: +#line 915 "cs-parser.jay" + { + yyVal = Argument.AType.Ref; + } + break; +case 85: +#line 919 "cs-parser.jay" + { + yyVal = Argument.AType.Out; + } + break; +case 88: + case_88(); + break; +case 89: + case_89(); + break; +case 103: + case_103(); + break; +case 104: + case_104(); + break; +case 105: + case_105(); + break; +case 106: +#line 996 "cs-parser.jay" + { + } + break; +case 107: + case_107(); + break; +case 108: + case_108(); + break; +case 109: + case_109(); + break; +case 110: + case_110(); + break; +case 111: + case_111(); + break; +case 112: +#line 1046 "cs-parser.jay" + { + Error_SyntaxError (yyToken); + } + break; +case 113: + case_113(); + break; +case 114: + case_114(); + break; +case 115: + case_115(); + break; +case 118: +#line 1095 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 119: +#line 1099 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 120: + case_120(); + break; +case 121: +#line 1115 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 122: + case_122(); + break; +case 123: + case_123(); + break; +case 126: + case_126(); + break; +case 127: + case_127(); + break; +case 128: + case_128(); + break; +case 129: + case_129(); + break; +case 130: +#line 1194 "cs-parser.jay" + { + report.Error (1641, GetLocation (yyVals[-1+yyTop]), "A fixed size buffer field must have the array size specifier after the field name"); + } + break; +case 132: + case_132(); + break; +case 133: + case_133(); + break; +case 136: +#line 1224 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 137: +#line 1228 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 138: + case_138(); + break; +case 139: +#line 1241 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 140: + case_140(); + break; +case 143: +#line 1260 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 144: +#line 1264 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 145: + case_145(); + break; +case 146: +#line 1280 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 147: + case_147(); + break; +case 148: + case_148(); + break; +case 151: + case_151(); + break; +case 152: + case_152(); + break; +case 153: + case_153(); + break; +case 154: +#line 1348 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.All; + } + break; +case 155: + case_155(); + break; +case 156: + case_156(); + break; +case 157: +#line 1387 "cs-parser.jay" + { + lexer.parsing_generic_declaration = true; + } + break; +case 158: + case_158(); + break; +case 159: +#line 1397 "cs-parser.jay" + { + lexer.ConstraintsParsing = true; + } + break; +case 160: + case_160(); + break; +case 161: + case_161(); + break; +case 162: + case_162(); + break; +case 166: +#line 1475 "cs-parser.jay" + { savedLocation = GetLocation (yyVals[0+yyTop]); yyVal = null; } + break; +case 167: + case_167(); + break; +case 168: + case_168(); + break; +case 169: +#line 1499 "cs-parser.jay" + { yyVal = ParametersCompiled.EmptyReadOnlyParameters; } + break; +case 171: + case_171(); + break; +case 172: + case_172(); + break; +case 173: + case_173(); + break; +case 174: + case_174(); + break; +case 175: + case_175(); + break; +case 176: + case_176(); + break; +case 177: + case_177(); + break; +case 178: +#line 1571 "cs-parser.jay" + { + yyVal = new ParametersCompiled (new Parameter[] { (Parameter) yyVals[0+yyTop] } ); + } + break; +case 179: +#line 1575 "cs-parser.jay" + { + yyVal = new ParametersCompiled (new Parameter [] { new ArglistParameter (GetLocation (yyVals[0+yyTop])) }, true); + } + break; +case 180: + case_180(); + break; +case 181: + case_181(); + break; +case 182: + case_182(); + break; +case 183: + case_183(); + break; +case 184: + case_184(); + break; +case 185: + case_185(); + break; +case 186: + case_186(); + break; +case 187: +#line 1656 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 188: + case_188(); + break; +case 189: +#line 1697 "cs-parser.jay" + { yyVal = Parameter.Modifier.NONE; } + break; +case 191: +#line 1705 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 192: + case_192(); + break; +case 193: + case_193(); + break; +case 194: + case_194(); + break; +case 195: + case_195(); + break; +case 196: + case_196(); + break; +case 197: + case_197(); + break; +case 198: + case_198(); + break; +case 199: + case_199(); + break; +case 200: + case_200(); + break; +case 201: +#line 1799 "cs-parser.jay" + { + Error_DuplicateParameterModifier (GetLocation (yyVals[-1+yyTop]), Parameter.Modifier.PARAMS); + } + break; +case 202: + case_202(); + break; +case 203: + case_203(); + break; +case 204: + case_204(); + break; +case 205: + case_205(); + break; +case 206: + case_206(); + break; +case 207: +#line 1849 "cs-parser.jay" + { + current_property = null; + } + break; +case 208: + case_208(); + break; +case 209: + case_209(); + break; +case 211: + case_211(); + break; +case 212: + case_212(); + break; +case 215: +#line 1911 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Params | ParameterModifierType.DefaultValue; + } + break; +case 216: + case_216(); + break; +case 217: + case_217(); + break; +case 218: +#line 1957 "cs-parser.jay" + { + lbag.AppendToMember (current_property, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + break; +case 219: + case_219(); + break; +case 224: + case_224(); + break; +case 225: + case_225(); + break; +case 226: + case_226(); + break; +case 227: + case_227(); + break; +case 228: + case_228(); + break; +case 230: + case_230(); + break; +case 231: + case_231(); + break; +case 232: +#line 2098 "cs-parser.jay" + { + } + break; +case 233: + case_233(); + break; +case 234: + case_234(); + break; +case 235: + case_235(); + break; +case 236: + case_236(); + break; +case 237: +#line 2138 "cs-parser.jay" + { + Error_SyntaxError (yyToken); + } + break; +case 240: + case_240(); + break; +case 241: + case_241(); + break; +case 242: +#line 2163 "cs-parser.jay" + { + report.Error (525, GetLocation (yyVals[0+yyTop]), "Interfaces cannot contain fields or constants"); + } + break; +case 243: +#line 2167 "cs-parser.jay" + { + report.Error (525, GetLocation (yyVals[0+yyTop]), "Interfaces cannot contain fields or constants"); + } + break; +case 248: +#line 2175 "cs-parser.jay" + { + report.Error (567, GetLocation (yyVals[0+yyTop]), "Interfaces cannot contain operators"); + } + break; +case 249: +#line 2179 "cs-parser.jay" + { + report.Error (526, GetLocation (yyVals[0+yyTop]), "Interfaces cannot contain contructors"); + } + break; +case 250: +#line 2183 "cs-parser.jay" + { + report.Error (524, GetLocation (yyVals[0+yyTop]), "Interfaces cannot declare classes, structs, interfaces, delegates, or enumerations"); + } + break; +case 251: +#line 2189 "cs-parser.jay" + { + } + break; +case 252: + case_252(); + break; +case 254: + case_254(); + break; +case 255: +#line 2233 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + break; +case 256: + case_256(); + break; +case 258: +#line 2279 "cs-parser.jay" + { yyVal = Operator.OpType.LogicalNot; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 259: +#line 2280 "cs-parser.jay" + { yyVal = Operator.OpType.OnesComplement; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 260: +#line 2281 "cs-parser.jay" + { yyVal = Operator.OpType.Increment; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 261: +#line 2282 "cs-parser.jay" + { yyVal = Operator.OpType.Decrement; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 262: +#line 2283 "cs-parser.jay" + { yyVal = Operator.OpType.True; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 263: +#line 2284 "cs-parser.jay" + { yyVal = Operator.OpType.False; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 264: +#line 2286 "cs-parser.jay" + { yyVal = Operator.OpType.Addition; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 265: +#line 2287 "cs-parser.jay" + { yyVal = Operator.OpType.Subtraction; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 266: +#line 2289 "cs-parser.jay" + { yyVal = Operator.OpType.Multiply; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 267: +#line 2290 "cs-parser.jay" + { yyVal = Operator.OpType.Division; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 268: +#line 2291 "cs-parser.jay" + { yyVal = Operator.OpType.Modulus; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 269: +#line 2292 "cs-parser.jay" + { yyVal = Operator.OpType.BitwiseAnd; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 270: +#line 2293 "cs-parser.jay" + { yyVal = Operator.OpType.BitwiseOr; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 271: +#line 2294 "cs-parser.jay" + { yyVal = Operator.OpType.ExclusiveOr; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 272: +#line 2295 "cs-parser.jay" + { yyVal = Operator.OpType.LeftShift; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 273: +#line 2296 "cs-parser.jay" + { yyVal = Operator.OpType.RightShift; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 274: +#line 2297 "cs-parser.jay" + { yyVal = Operator.OpType.Equality; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 275: +#line 2298 "cs-parser.jay" + { yyVal = Operator.OpType.Inequality; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 276: +#line 2299 "cs-parser.jay" + { yyVal = Operator.OpType.GreaterThan; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 277: +#line 2300 "cs-parser.jay" + { yyVal = Operator.OpType.LessThan; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 278: +#line 2301 "cs-parser.jay" + { yyVal = Operator.OpType.GreaterThanOrEqual; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 279: +#line 2302 "cs-parser.jay" + { yyVal = Operator.OpType.LessThanOrEqual; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 280: +#line 2309 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + break; +case 281: + case_281(); + break; +case 282: +#line 2332 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + break; +case 283: + case_283(); + break; +case 284: + case_284(); + break; +case 285: + case_285(); + break; +case 286: + case_286(); + break; +case 287: + case_287(); + break; +case 288: + case_288(); + break; +case 289: + case_289(); + break; +case 291: +#line 2442 "cs-parser.jay" + { current_block = null; yyVal = null; } + break; +case 294: +#line 2454 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 295: + case_295(); + break; +case 296: +#line 2464 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 297: + case_297(); + break; +case 298: + case_298(); + break; +case 299: + case_299(); + break; +case 300: + case_300(); + break; +case 301: + case_301(); + break; +case 302: + case_302(); + break; +case 303: + case_303(); + break; +case 304: + case_304(); + break; +case 305: + case_305(); + break; +case 306: + case_306(); + break; +case 307: + case_307(); + break; +case 309: +#line 2591 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 310: + case_310(); + break; +case 313: +#line 2609 "cs-parser.jay" + { + current_event_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 314: +#line 2613 "cs-parser.jay" + { + current_event_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 315: + case_315(); + break; +case 316: +#line 2626 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 317: + case_317(); + break; +case 318: + case_318(); + break; +case 319: +#line 2651 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 322: + case_322(); + break; +case 323: + case_323(); + break; +case 324: + case_324(); + break; +case 325: + case_325(); + break; +case 326: + case_326(); + break; +case 327: + case_327(); + break; +case 328: + case_328(); + break; +case 329: + case_329(); + break; +case 331: + case_331(); + break; +case 332: + case_332(); + break; +case 333: + case_333(); + break; +case 334: + case_334(); + break; +case 335: + case_335(); + break; +case 336: + case_336(); + break; +case 338: + case_338(); + break; +case 339: + case_339(); + break; +case 342: +#line 2839 "cs-parser.jay" + { + lbag.AppendToMember (current_container, GetLocation (yyVals[0+yyTop])); + } + break; +case 344: + case_344(); + break; +case 345: + case_345(); + break; +case 346: + case_346(); + break; +case 347: + case_347(); + break; +case 348: + case_348(); + break; +case 350: +#line 2913 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out | ParameterModifierType.Params | ParameterModifierType.DefaultValue; + } + break; +case 351: + case_351(); + break; +case 352: +#line 2932 "cs-parser.jay" + { + lexer.ConstraintsParsing = false; + } + break; +case 353: + case_353(); + break; +case 355: + case_355(); + break; +case 357: + case_357(); + break; +case 358: + case_358(); + break; +case 360: + case_360(); + break; +case 361: + case_361(); + break; +case 362: + case_362(); + break; +case 363: + case_363(); + break; +case 365: + case_365(); + break; +case 366: + case_366(); + break; +case 367: + case_367(); + break; +case 368: + case_368(); + break; +case 369: +#line 3062 "cs-parser.jay" + { + lexer.parsing_generic_declaration = true; + } + break; +case 370: + case_370(); + break; +case 371: + case_371(); + break; +case 373: + case_373(); + break; +case 374: + case_374(); + break; +case 375: + case_375(); + break; +case 376: + case_376(); + break; +case 377: + case_377(); + break; +case 378: + case_378(); + break; +case 380: + case_380(); + break; +case 381: + case_381(); + break; +case 382: + case_382(); + break; +case 383: + case_383(); + break; +case 384: + case_384(); + break; +case 386: +#line 3187 "cs-parser.jay" + { + yyVal = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[0+yyTop])); + } + break; +case 387: +#line 3194 "cs-parser.jay" + { + lexer.parsing_generic_declaration = true; + } + break; +case 393: + case_393(); + break; +case 395: +#line 3224 "cs-parser.jay" + { + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + break; +case 396: + case_396(); + break; +case 397: +#line 3243 "cs-parser.jay" + { + yyVal = new ComposedCast ((ATypeNameExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + break; +case 399: + case_399(); + break; +case 400: + case_400(); + break; +case 401: +#line 3264 "cs-parser.jay" + { + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + break; +case 402: +#line 3268 "cs-parser.jay" + { + yyVal = new ComposedCast (new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[-1+yyTop])), (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + break; +case 403: + case_403(); + break; +case 404: + case_404(); + break; +case 405: + case_405(); + break; +case 406: +#line 3302 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Object, GetLocation (yyVals[0+yyTop])); } + break; +case 407: +#line 3303 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.String, GetLocation (yyVals[0+yyTop])); } + break; +case 408: +#line 3304 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Bool, GetLocation (yyVals[0+yyTop])); } + break; +case 409: +#line 3305 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Decimal, GetLocation (yyVals[0+yyTop])); } + break; +case 410: +#line 3306 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Float, GetLocation (yyVals[0+yyTop])); } + break; +case 411: +#line 3307 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Double, GetLocation (yyVals[0+yyTop])); } + break; +case 413: +#line 3312 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.SByte, GetLocation (yyVals[0+yyTop])); } + break; +case 414: +#line 3313 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Byte, GetLocation (yyVals[0+yyTop])); } + break; +case 415: +#line 3314 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Short, GetLocation (yyVals[0+yyTop])); } + break; +case 416: +#line 3315 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.UShort, GetLocation (yyVals[0+yyTop])); } + break; +case 417: +#line 3316 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Int, GetLocation (yyVals[0+yyTop])); } + break; +case 418: +#line 3317 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.UInt, GetLocation (yyVals[0+yyTop])); } + break; +case 419: +#line 3318 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Long, GetLocation (yyVals[0+yyTop])); } + break; +case 420: +#line 3319 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.ULong, GetLocation (yyVals[0+yyTop])); } + break; +case 421: +#line 3320 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Char, GetLocation (yyVals[0+yyTop])); } + break; +case 443: + case_443(); + break; +case 447: +#line 3363 "cs-parser.jay" + { yyVal = new NullLiteral (GetLocation (yyVals[0+yyTop])); } + break; +case 448: +#line 3367 "cs-parser.jay" + { yyVal = new BoolLiteral (compiler.BuiltinTypes, true, GetLocation (yyVals[0+yyTop])); } + break; +case 449: +#line 3368 "cs-parser.jay" + { yyVal = new BoolLiteral (compiler.BuiltinTypes, false, GetLocation (yyVals[0+yyTop])); } + break; +case 454: + case_454(); + break; +case 455: +#line 3401 "cs-parser.jay" + { + yyVal = new ParenthesizedExpression ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + break; +case 456: + case_456(); + break; +case 457: + case_457(); + break; +case 458: + case_458(); + break; +case 459: + case_459(); + break; +case 460: + case_460(); + break; +case 461: + case_461(); + break; +case 462: + case_462(); + break; +case 463: + case_463(); + break; +case 464: +#line 3465 "cs-parser.jay" + { + yyVal = new CompletionMemberAccess ((Expression) yyVals[-2+yyTop], null,GetLocation (yyVals[0+yyTop])); + } + break; +case 465: + case_465(); + break; +case 466: +#line 3473 "cs-parser.jay" + { + yyVal = new CompletionMemberAccess ((Expression) yyVals[-2+yyTop], null, lexer.Location); + } + break; +case 467: + case_467(); + break; +case 468: + case_468(); + break; +case 469: + case_469(); + break; +case 470: + case_470(); + break; +case 471: +#line 3503 "cs-parser.jay" + { yyVal = null; } + break; +case 473: + case_473(); + break; +case 474: + case_474(); + break; +case 475: +#line 3525 "cs-parser.jay" + { yyVal = null; } + break; +case 476: +#line 3529 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 477: + case_477(); + break; +case 478: + case_478(); + break; +case 479: + case_479(); + break; +case 480: + case_480(); + break; +case 481: + case_481(); + break; +case 482: +#line 3568 "cs-parser.jay" + { + yyVal = new CompletionElementInitializer (null, GetLocation (yyVals[0+yyTop])); + } + break; +case 483: + case_483(); + break; +case 484: + case_484(); + break; +case 485: + case_485(); + break; +case 486: + case_486(); + break; +case 489: +#line 3608 "cs-parser.jay" + { yyVal = null; } + break; +case 491: + case_491(); + break; +case 492: + case_492(); + break; +case 493: + case_493(); + break; +case 494: + case_494(); + break; +case 495: + case_495(); + break; +case 496: +#line 3662 "cs-parser.jay" + { + yyVal = new Argument ((Expression) yyVals[0+yyTop]); + } + break; +case 500: + case_500(); + break; +case 501: + case_501(); + break; +case 502: + case_502(); + break; +case 503: + case_503(); + break; +case 505: + case_505(); + break; +case 506: + case_506(); + break; +case 507: + case_507(); + break; +case 508: + case_508(); + break; +case 509: + case_509(); + break; +case 510: + case_510(); + break; +case 511: + case_511(); + break; +case 512: + case_512(); + break; +case 513: +#line 3766 "cs-parser.jay" + { + yyVal = new Argument ((Expression) yyVals[0+yyTop]); + } + break; +case 515: +#line 3774 "cs-parser.jay" + { + yyVal = new This (GetLocation (yyVals[0+yyTop])); + } + break; +case 516: + case_516(); + break; +case 517: + case_517(); + break; +case 518: +#line 3794 "cs-parser.jay" + { + yyVal = new UnaryMutator (UnaryMutator.Mode.PostIncrement, (Expression) yyVals[-1+yyTop], GetLocation (yyVals[0+yyTop])); + } + break; +case 519: +#line 3801 "cs-parser.jay" + { + yyVal = new UnaryMutator (UnaryMutator.Mode.PostDecrement, (Expression) yyVals[-1+yyTop], GetLocation (yyVals[0+yyTop])); + } + break; +case 520: + case_520(); + break; +case 521: + case_521(); + break; +case 522: + case_522(); + break; +case 523: + case_523(); + break; +case 524: + case_524(); + break; +case 525: + case_525(); + break; +case 526: + case_526(); + break; +case 527: +#line 3868 "cs-parser.jay" + { + ++lexer.parsing_type; + } + break; +case 528: + case_528(); + break; +case 529: + case_529(); + break; +case 530: +#line 3890 "cs-parser.jay" + { + yyVal = new EmptyCompletion (); + } + break; +case 533: +#line 3899 "cs-parser.jay" + { yyVal = null; } + break; +case 535: + case_535(); + break; +case 536: + case_536(); + break; +case 537: +#line 3921 "cs-parser.jay" + { + yyVal = new EmptyCompletion (); + } + break; +case 538: +#line 3925 "cs-parser.jay" + { + yyVal = yyVals[-1+yyTop]; + } + break; +case 539: + case_539(); + break; +case 540: + case_540(); + break; +case 541: + case_541(); + break; +case 542: + case_542(); + break; +case 546: + case_546(); + break; +case 547: + case_547(); + break; +case 548: + case_548(); + break; +case 549: +#line 3985 "cs-parser.jay" + { + yyVal = 2; + } + break; +case 550: +#line 3989 "cs-parser.jay" + { + yyVal = ((int) yyVals[-1+yyTop]) + 1; + } + break; +case 551: +#line 3996 "cs-parser.jay" + { + yyVal = null; + } + break; +case 552: +#line 4000 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 553: + case_553(); + break; +case 554: + case_554(); + break; +case 555: + case_555(); + break; +case 556: + case_556(); + break; +case 557: + case_557(); + break; +case 559: + case_559(); + break; +case 560: + case_560(); + break; +case 561: + case_561(); + break; +case 562: + case_562(); + break; +case 563: + case_563(); + break; +case 564: + case_564(); + break; +case 565: + case_565(); + break; +case 566: + case_566(); + break; +case 567: + case_567(); + break; +case 568: + case_568(); + break; +case 569: +#line 4133 "cs-parser.jay" + { + start_anonymous (false, (ParametersCompiled) yyVals[0+yyTop], false, GetLocation (yyVals[-1+yyTop])); + } + break; +case 570: + case_570(); + break; +case 571: +#line 4146 "cs-parser.jay" + { + start_anonymous (false, (ParametersCompiled) yyVals[0+yyTop], true, GetLocation (yyVals[-2+yyTop])); + } + break; +case 572: + case_572(); + break; +case 573: +#line 4163 "cs-parser.jay" + { + yyVal = ParametersCompiled.Undefined; + } + break; +case 575: +#line 4171 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 576: + case_576(); + break; +case 577: + case_577(); + break; +case 579: +#line 4197 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.LogicalNot, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 580: +#line 4201 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.OnesComplement, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 581: + case_581(); + break; +case 582: + case_582(); + break; +case 583: + case_583(); + break; +case 584: + case_584(); + break; +case 585: + case_585(); + break; +case 586: + case_586(); + break; +case 588: +#line 4265 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.UnaryPlus, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 589: +#line 4269 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.UnaryNegation, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 590: +#line 4273 "cs-parser.jay" + { + yyVal = new UnaryMutator (UnaryMutator.Mode.PreIncrement, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 591: +#line 4277 "cs-parser.jay" + { + yyVal = new UnaryMutator (UnaryMutator.Mode.PreDecrement, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 592: +#line 4281 "cs-parser.jay" + { + yyVal = new Indirection ((Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 593: +#line 4285 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.AddressOf, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 594: + case_594(); + break; +case 595: + case_595(); + break; +case 596: + case_596(); + break; +case 597: + case_597(); + break; +case 598: + case_598(); + break; +case 599: + case_599(); + break; +case 601: + case_601(); + break; +case 602: + case_602(); + break; +case 603: + case_603(); + break; +case 604: + case_604(); + break; +case 605: + case_605(); + break; +case 606: + case_606(); + break; +case 608: + case_608(); + break; +case 609: + case_609(); + break; +case 610: + case_610(); + break; +case 611: + case_611(); + break; +case 612: +#line 4393 "cs-parser.jay" + { + yyVal = new As ((Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 613: + case_613(); + break; +case 614: + case_614(); + break; +case 615: + case_615(); + break; +case 616: + case_616(); + break; +case 617: + case_617(); + break; +case 619: + case_619(); + break; +case 621: +#line 4445 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.UnaryPlus, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 622: +#line 4449 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.UnaryNegation, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 623: + case_623(); + break; +case 624: + case_624(); + break; +case 628: + case_628(); + break; +case 629: + case_629(); + break; +case 630: + case_630(); + break; +case 631: + case_631(); + break; +case 633: + case_633(); + break; +case 634: + case_634(); + break; +case 635: + case_635(); + break; +case 636: + case_636(); + break; +case 637: + case_637(); + break; +case 638: + case_638(); + break; +case 639: + case_639(); + break; +case 640: + case_640(); + break; +case 642: + case_642(); + break; +case 643: + case_643(); + break; +case 644: + case_644(); + break; +case 645: + case_645(); + break; +case 647: + case_647(); + break; +case 648: + case_648(); + break; +case 650: + case_650(); + break; +case 651: + case_651(); + break; +case 653: + case_653(); + break; +case 654: + case_654(); + break; +case 656: + case_656(); + break; +case 657: + case_657(); + break; +case 659: + case_659(); + break; +case 660: + case_660(); + break; +case 662: + case_662(); + break; +case 664: + case_664(); + break; +case 665: + case_665(); + break; +case 666: + case_666(); + break; +case 667: + case_667(); + break; +case 668: + case_668(); + break; +case 669: + case_669(); + break; +case 670: + case_670(); + break; +case 671: + case_671(); + break; +case 672: + case_672(); + break; +case 673: + case_673(); + break; +case 674: + case_674(); + break; +case 675: + case_675(); + break; +case 676: + case_676(); + break; +case 677: + case_677(); + break; +case 678: + case_678(); + break; +case 679: + case_679(); + break; +case 680: + case_680(); + break; +case 681: + case_681(); + break; +case 682: + case_682(); + break; +case 683: + case_683(); + break; +case 684: + case_684(); + break; +case 685: +#line 4821 "cs-parser.jay" + { yyVal = ParametersCompiled.EmptyReadOnlyParameters; } + break; +case 686: + case_686(); + break; +case 687: +#line 4832 "cs-parser.jay" + { + start_block (Location.Null); + } + break; +case 688: + case_688(); + break; +case 690: + case_690(); + break; +case 692: + case_692(); + break; +case 693: + case_693(); + break; +case 694: + case_694(); + break; +case 695: + case_695(); + break; +case 696: + case_696(); + break; +case 697: + case_697(); + break; +case 698: + case_698(); + break; +case 699: +#line 4899 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 700: + case_700(); + break; +case 701: + case_701(); + break; +case 702: +#line 4913 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 703: + case_703(); + break; +case 704: + case_704(); + break; +case 710: +#line 4938 "cs-parser.jay" + { + yyVal = new ArglistAccess (GetLocation (yyVals[0+yyTop])); + } + break; +case 711: + case_711(); + break; +case 712: + case_712(); + break; +case 713: + case_713(); + break; +case 715: +#line 4967 "cs-parser.jay" + { + yyVal = new BooleanExpression ((Expression) yyVals[0+yyTop]); + } + break; +case 716: +#line 4974 "cs-parser.jay" + { + yyVal = null; + } + break; +case 718: + case_718(); + break; +case 719: +#line 4995 "cs-parser.jay" + { + yyVal = null; + } + break; +case 720: +#line 4999 "cs-parser.jay" + { + yyVal = null; + } + break; +case 721: +#line 5003 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 722: +#line 5007 "cs-parser.jay" + { + yyVal = yyVals[-1+yyTop]; + } + break; +case 723: + case_723(); + break; +case 724: + case_724(); + break; +case 725: +#line 5032 "cs-parser.jay" + { + } + break; +case 726: + case_726(); + break; +case 727: + case_727(); + break; +case 728: + case_728(); + break; +case 729: + case_729(); + break; +case 730: +#line 5084 "cs-parser.jay" + { yyVal = null; } + break; +case 731: +#line 5086 "cs-parser.jay" + { yyVal = yyVals[0+yyTop]; StoreModifierLocation (Modifiers.PARTIAL, GetLocation (yyVals[0+yyTop])); } + break; +case 732: + case_732(); + break; +case 733: +#line 5099 "cs-parser.jay" + { + lexer.parsing_modifiers = false; + } + break; +case 735: + case_735(); + break; +case 736: + case_736(); + break; +case 737: + case_737(); + break; +case 738: + case_738(); + break; +case 739: + case_739(); + break; +case 740: + case_740(); + break; +case 741: + case_741(); + break; +case 742: + case_742(); + break; +case 743: + case_743(); + break; +case 744: + case_744(); + break; +case 745: + case_745(); + break; +case 746: + case_746(); + break; +case 747: + case_747(); + break; +case 748: + case_748(); + break; +case 749: + case_749(); + break; +case 750: + case_750(); + break; +case 753: + case_753(); + break; +case 754: + case_754(); + break; +case 756: +#line 5229 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 757: + case_757(); + break; +case 758: + case_758(); + break; +case 759: + case_759(); + break; +case 760: + case_760(); + break; +case 761: + case_761(); + break; +case 762: + case_762(); + break; +case 763: + case_763(); + break; +case 764: + case_764(); + break; +case 765: +#line 5322 "cs-parser.jay" + { + yyVal = new SpecialContraintExpr (SpecialConstraint.Class, GetLocation (yyVals[0+yyTop])); + } + break; +case 766: +#line 5326 "cs-parser.jay" + { + yyVal = new SpecialContraintExpr (SpecialConstraint.Struct, GetLocation (yyVals[0+yyTop])); + } + break; +case 767: +#line 5333 "cs-parser.jay" + { + yyVal = null; + } + break; +case 768: + case_768(); + break; +case 769: + case_769(); + break; +case 770: + case_770(); + break; +case 771: + case_771(); + break; +case 772: +#line 5378 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 773: + case_773(); + break; +case 774: + case_774(); + break; +case 775: + case_775(); + break; +case 776: + case_776(); + break; +case 777: + case_777(); + break; +case 778: + case_778(); + break; +case 779: + case_779(); + break; +case 784: +#line 5440 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 785: +#line 5444 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 787: + case_787(); + break; +case 788: + case_788(); + break; +case 791: +#line 5478 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 792: +#line 5482 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 821: + case_821(); + break; +case 822: + case_822(); + break; +case 823: + case_823(); + break; +case 824: + case_824(); + break; +case 825: + case_825(); + break; +case 828: + case_828(); + break; +case 829: + case_829(); + break; +case 830: + case_830(); + break; +case 834: + case_834(); + break; +case 835: +#line 5633 "cs-parser.jay" + { + yyVal = ComposedTypeSpecifier.CreatePointer (GetLocation (yyVals[0+yyTop])); + } + break; +case 837: +#line 5641 "cs-parser.jay" + { + yyVal = Error_AwaitAsIdentifier (yyVals[0+yyTop]); + } + break; +case 838: + case_838(); + break; +case 839: + case_839(); + break; +case 840: + case_840(); + break; +case 841: + case_841(); + break; +case 843: + case_843(); + break; +case 845: + case_845(); + break; +case 846: + case_846(); + break; +case 850: + case_850(); + break; +case 853: + case_853(); + break; +case 854: + case_854(); + break; +case 855: +#line 5755 "cs-parser.jay" + { + report.Error (145, lexer.Location, "A const field requires a value to be provided"); + } + break; +case 856: + case_856(); + break; +case 861: + case_861(); + break; +case 863: + case_863(); + break; +case 864: + case_864(); + break; +case 865: + case_865(); + break; +case 866: +#line 5805 "cs-parser.jay" + { yyVal = yyVals[-1+yyTop]; } + break; +case 867: + case_867(); + break; +case 868: +#line 5815 "cs-parser.jay" + { yyVal = yyVals[-1+yyTop]; } + break; +case 869: +#line 5816 "cs-parser.jay" + { yyVal = yyVals[-1+yyTop]; } + break; +case 870: + case_870(); + break; +case 871: + case_871(); + break; +case 872: + case_872(); + break; +case 875: + case_875(); + break; +case 876: + case_876(); + break; +case 877: + case_877(); + break; +case 878: +#line 5888 "cs-parser.jay" + { + start_block (GetLocation (yyVals[0+yyTop])); + } + break; +case 879: + case_879(); + break; +case 880: + case_880(); + break; +case 881: +#line 5908 "cs-parser.jay" + { + report.Warning (1522, 1, current_block.StartLocation, "Empty switch block"); + } + break; +case 885: +#line 5918 "cs-parser.jay" + { + Error_SyntaxError (yyToken); + } + break; +case 887: + case_887(); + break; +case 888: +#line 5935 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 889: + case_889(); + break; +case 890: + case_890(); + break; +case 891: +#line 5952 "cs-parser.jay" + { + yyVal = new SwitchLabel (null, GetLocation (yyVals[0+yyTop])); + } + break; +case 896: + case_896(); + break; +case 897: + case_897(); + break; +case 898: + case_898(); + break; +case 899: + case_899(); + break; +case 900: + case_900(); + break; +case 901: + case_901(); + break; +case 902: +#line 6013 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 903: + case_903(); + break; +case 904: +#line 6028 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 905: + case_905(); + break; +case 906: + case_906(); + break; +case 907: +#line 6049 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 908: + case_908(); + break; +case 909: + case_909(); + break; +case 910: + case_910(); + break; +case 911: +#line 6083 "cs-parser.jay" + { yyVal = new EmptyStatement (lexer.Location); } + break; +case 913: + case_913(); + break; +case 914: + case_914(); + break; +case 916: +#line 6107 "cs-parser.jay" + { yyVal = null; } + break; +case 918: +#line 6112 "cs-parser.jay" + { yyVal = new EmptyStatement (lexer.Location); } + break; +case 922: + case_922(); + break; +case 923: + case_923(); + break; +case 924: + case_924(); + break; +case 925: + case_925(); + break; +case 926: + case_926(); + break; +case 927: + case_927(); + break; +case 928: + case_928(); + break; +case 935: + case_935(); + break; +case 936: + case_936(); + break; +case 937: + case_937(); + break; +case 938: + case_938(); + break; +case 939: + case_939(); + break; +case 940: + case_940(); + break; +case 941: + case_941(); + break; +case 942: + case_942(); + break; +case 943: + case_943(); + break; +case 944: + case_944(); + break; +case 945: + case_945(); + break; +case 946: + case_946(); + break; +case 947: + case_947(); + break; +case 948: + case_948(); + break; +case 949: + case_949(); + break; +case 952: +#line 6358 "cs-parser.jay" + { + yyVal = new TryCatch ((Block) yyVals[-1+yyTop], (List) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop]), false); + } + break; +case 953: + case_953(); + break; +case 954: + case_954(); + break; +case 955: + case_955(); + break; +case 956: + case_956(); + break; +case 957: + case_957(); + break; +case 960: + case_960(); + break; +case 961: + case_961(); + break; +case 962: + case_962(); + break; +case 963: + case_963(); + break; +case 964: + case_964(); + break; +case 966: + case_966(); + break; +case 967: +#line 6483 "cs-parser.jay" + { + yyVal = new Checked ((Block) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 968: +#line 6490 "cs-parser.jay" + { + yyVal = new Unchecked ((Block) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 969: + case_969(); + break; +case 970: +#line 6500 "cs-parser.jay" + { + yyVal = new Unsafe ((Block) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + } + break; +case 971: + case_971(); + break; +case 972: + case_972(); + break; +case 973: + case_973(); + break; +case 974: + case_974(); + break; +case 975: + case_975(); + break; +case 976: + case_976(); + break; +case 977: + case_977(); + break; +case 978: + case_978(); + break; +case 979: + case_979(); + break; +case 980: + case_980(); + break; +case 982: + case_982(); + break; +case 983: +#line 6605 "cs-parser.jay" + { + Error_MissingInitializer (lexer.Location); + } + break; +case 984: + case_984(); + break; +case 985: + case_985(); + break; +case 986: + case_986(); + break; +case 987: + case_987(); + break; +case 988: + case_988(); + break; +case 989: + case_989(); + break; +case 990: + case_990(); + break; +case 991: + case_991(); + break; +case 992: + case_992(); + break; +case 993: +#line 6710 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 994: + case_994(); + break; +case 995: +#line 6725 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 996: + case_996(); + break; +case 997: + case_997(); + break; +case 998: + case_998(); + break; +case 1000: + case_1000(); + break; +case 1001: + case_1001(); + break; +case 1002: +#line 6789 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 1003: + case_1003(); + break; +case 1004: + case_1004(); + break; +case 1005: + case_1005(); + break; +case 1006: + case_1006(); + break; +case 1007: +#line 6828 "cs-parser.jay" + { + yyVal = new object[] { yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop]) }; + } + break; +case 1008: + case_1008(); + break; +case 1010: + case_1010(); + break; +case 1016: +#line 6857 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 1017: + case_1017(); + break; +case 1018: +#line 6876 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 1019: + case_1019(); + break; +case 1020: + case_1020(); + break; +case 1021: + case_1021(); + break; +case 1022: + case_1022(); + break; +case 1023: + case_1023(); + break; +case 1024: + case_1024(); + break; +case 1025: + case_1025(); + break; +case 1026: + case_1026(); + break; +case 1027: + case_1027(); + break; +case 1029: + case_1029(); + break; +case 1030: + case_1030(); + break; +case 1031: + case_1031(); + break; +case 1033: + case_1033(); + break; +case 1034: + case_1034(); + break; +case 1036: + case_1036(); + break; +case 1037: + case_1037(); + break; +case 1038: +#line 7077 "cs-parser.jay" + { + yyVal = new Linq.OrderByAscending ((Linq.QueryBlock) current_block, (Expression)yyVals[0+yyTop]); + } + break; +case 1039: + case_1039(); + break; +case 1040: + case_1040(); + break; +case 1041: +#line 7094 "cs-parser.jay" + { + yyVal = new Linq.ThenByAscending ((Linq.QueryBlock) current_block, (Expression)yyVals[0+yyTop]); + } + break; +case 1042: + case_1042(); + break; +case 1043: + case_1043(); + break; +case 1045: + case_1045(); + break; +case 1046: + case_1046(); + break; +case 1049: + case_1049(); + break; +case 1050: + case_1050(); + break; +case 1058: +#line 7219 "cs-parser.jay" + { + module.DocumentationBuilder.ParsedName = (MemberName) yyVals[0+yyTop]; + } + break; +case 1059: +#line 7226 "cs-parser.jay" + { + module.DocumentationBuilder.ParsedParameters = (List)yyVals[0+yyTop]; + } + break; +case 1060: + case_1060(); + break; +case 1061: + case_1061(); + break; +case 1062: + case_1062(); + break; +case 1063: +#line 7249 "cs-parser.jay" + { + yyVal = new MemberName ((MemberName) yyVals[-2+yyTop], MemberCache.IndexerNameAlias, Location.Null); + } + break; +case 1064: +#line 7253 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 1065: + case_1065(); + break; +case 1066: + case_1066(); + break; +case 1067: + case_1067(); + break; +case 1068: + case_1068(); + break; +case 1070: +#line 7289 "cs-parser.jay" + { + yyVal = new MemberName (((MemberName) yyVals[-2+yyTop]), (MemberName) yyVals[0+yyTop]); + } + break; +case 1072: +#line 7297 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 1073: +#line 7301 "cs-parser.jay" + { + yyVal = yyVals[-1+yyTop]; + } + break; +case 1074: +#line 7308 "cs-parser.jay" + { + yyVal = new List (0); + } + break; +case 1076: + case_1076(); + break; +case 1077: + case_1077(); + break; +case 1078: + case_1078(); + break; +#line default + } + yyTop -= yyLen[yyN]; + yyState = yyStates[yyTop]; + int yyM = yyLhs[yyN]; + if (yyState == 0 && yyM == 0) { +//t if (debug != null) debug.shift(0, yyFinal); + yyState = yyFinal; + if (yyToken < 0) { + yyToken = yyLex.advance() ? yyLex.token() : 0; +//t if (debug != null) +//t debug.lex(yyState, yyToken,yyname(yyToken), yyLex.value()); + } + if (yyToken == 0) { +//t if (debug != null) debug.accept(yyVal); + return yyVal; + } + goto continue_yyLoop; + } + if (((yyN = yyGindex[yyM]) != 0) && ((yyN += yyState) >= 0) + && (yyN < yyTable.Length) && (yyCheck[yyN] == yyState)) + yyState = yyTable[yyN]; + else + yyState = yyDgoto[yyM]; +//t if (debug != null) debug.shift(yyStates[yyTop], yyState); + goto continue_yyLoop; + continue_yyDiscarded: ; // implements the named-loop continue: 'continue yyDiscarded' + } + continue_yyLoop: ; // implements the named-loop continue: 'continue yyLoop' + } + } + +/* + All more than 3 lines long rules are wrapped into a method +*/ +void case_6() +#line 397 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + Attributes attrs = (Attributes) yyVals[0+yyTop]; + report.Error (1730, attrs.Attrs [0].Location, + "Assembly and module attributes must precede all other elements except using clauses and extern alias declarations"); + + current_namespace.UnattachedAttributes = attrs; + } + } + +void case_8() +#line 411 "cs-parser.jay" +{ + if (yyToken == Token.EXTERN_ALIAS) + report.Error (439, lexer.Location, "An extern alias declaration must precede all other elements"); + else + Error_SyntaxError (yyToken); + } + +void case_13() +#line 431 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + string s = lt.Value; + if (s != "alias") { + syntax_error (lt.Location, "`alias' expected"); + } else { + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (lt.Location, "external alias"); + + lt = (LocatedToken) yyVals[-1+yyTop]; + if (lt.Value == QualifiedAliasMember.GlobalAlias) { + RootNamespace.Error_GlobalNamespaceRedefined (report, lt.Location); + } + + var na = new UsingExternAlias (new SimpleMemberName (lt.Value, lt.Location), GetLocation (yyVals[-3+yyTop])); + current_namespace.AddUsing (na); + + lbag.AddLocation (na, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + } + +void case_17() +#line 464 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_18() +#line 472 "cs-parser.jay" +{ + var un = new UsingNamespace ((ATypeNameExpression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + current_namespace.AddUsing (un); + + lbag.AddLocation (un, GetLocation (yyVals[0+yyTop])); + } + +void case_19() +#line 479 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + if (lang_version != LanguageVersion.ISO_1 && lt.Value == "global") { + report.Warning (440, 2, lt.Location, + "An alias named `global' will not be used when resolving `global::'. The global namespace will be used instead"); + } + + var un = new UsingAliasNamespace (new SimpleMemberName (lt.Value, lt.Location), (ATypeNameExpression) yyVals[-1+yyTop], GetLocation (yyVals[-4+yyTop])); + current_namespace.AddUsing (un); + lbag.AddLocation (un, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_20() +#line 491 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_21() +#line 504 "cs-parser.jay" +{ + Attributes attrs = (Attributes) yyVals[-2+yyTop]; + var name = (MemberName) yyVals[0+yyTop]; + if (attrs != null) { + bool valid_global_attrs = true; + if ((current_namespace.DeclarationFound || current_namespace != file)) { + valid_global_attrs = false; + } else { + foreach (var a in attrs.Attrs) { + if (a.ExplicitTarget == "assembly" || a.ExplicitTarget == "module") + continue; + + valid_global_attrs = false; + break; + } + } + + if (!valid_global_attrs) + report.Error (1671, name.Location, "A namespace declaration cannot have modifiers or attributes"); + } + + module.AddAttributes (attrs, current_namespace); + + var ns = new NamespaceContainer (name, current_namespace); + current_namespace.AddTypeContainer (ns); + current_container = current_namespace = ns; + } + +void case_22() +#line 532 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_23() +#line 537 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) + lbag.AddLocation (current_container, GetLocation (yyVals[-9+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + else + lbag.AddLocation (current_container, GetLocation (yyVals[-9+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-1+yyTop])); + + current_container = current_namespace = current_namespace.Parent; + } + +void case_24() +#line 546 "cs-parser.jay" +{ + report.Error (1514, lexer.Location, "Unexpected symbol `{0}', expecting `.' or `{{'", GetSymbolName (yyToken)); + + var name = (MemberName) yyVals[0+yyTop]; + var ns = new NamespaceContainer (name, current_namespace); + lbag.AddLocation (ns, GetLocation (yyVals[-1+yyTop])); + current_namespace.AddTypeContainer (ns); + } + +void case_27() +#line 560 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_28() +#line 568 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new MemberName (lt.Value, lt.Location); + } + +void case_29() +#line 573 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new MemberName ((MemberName) yyVals[-2+yyTop], lt.Value, lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_30() +#line 579 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new MemberName ("", lexer.Location); + } + +void case_43() +#line 617 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + TypeContainer ds = (TypeContainer)yyVals[0+yyTop]; + + if ((ds.ModFlags & (Modifiers.PRIVATE | Modifiers.PROTECTED)) != 0){ + report.Error (1527, ds.Location, + "Namespace elements cannot be explicitly declared as private, protected or protected internal"); + } + + /* Here is a trick, for explicit attributes we don't know where they belong to until*/ + /* we parse succeeding declaration hence we parse them as normal and re-attach them*/ + /* when we know whether they are global (assembly:, module:) or local (type:).*/ + if (ds.OptAttributes != null) { + ds.OptAttributes.ConvertGlobalAttributes (ds, current_namespace, !current_namespace.DeclarationFound && current_namespace == file); + } + } + current_namespace.DeclarationFound = true; + } + +void case_45() +#line 639 "cs-parser.jay" +{ + current_namespace.UnattachedAttributes = (Attributes) yyVals[-1+yyTop]; + report.Error (1518, lexer.Location, "Attributes must be attached to class, delegate, enum, interface or struct"); + lexer.putback ('}'); + } + +void case_53() +#line 672 "cs-parser.jay" +{ + var sect = (List) yyVals[0+yyTop]; + yyVal = new Attributes (sect); + } + +void case_54() +#line 677 "cs-parser.jay" +{ + Attributes attrs = yyVals[-1+yyTop] as Attributes; + var sect = (List) yyVals[0+yyTop]; + if (attrs == null) + attrs = new Attributes (sect); + else if (sect != null) + attrs.AddAttributes (sect); + yyVal = attrs; + } + +void case_55() +#line 690 "cs-parser.jay" +{ + PushLocation (GetLocation (yyVals[0+yyTop])); + lexer.parsing_attribute_section = true; + } + +void case_56() +#line 695 "cs-parser.jay" +{ + lexer.parsing_attribute_section = false; + yyVal = yyVals[0+yyTop]; + } + +void case_57() +#line 703 "cs-parser.jay" +{ + current_attr_target = (string) yyVals[-1+yyTop]; + if (current_attr_target == "assembly" || current_attr_target == "module") { + Lexer.check_incorrect_doc_comment (); + } + } + +void case_58() +#line 710 "cs-parser.jay" +{ + /* when attribute target is invalid*/ + if (current_attr_target == string.Empty) + yyVal = new List (0); + else + yyVal = yyVals[-2+yyTop]; + lbag.InsertLocation (yyVal, 0, GetLocation (yyVals[-4+yyTop])); + lbag.InsertLocation (yyVal, 0, PopLocation ()); + lbag.InsertLocation (yyVal, 0, PopLocation ()); + if (yyVals[-1+yyTop] != null) { + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + + current_attr_target = null; + lexer.parsing_attribute_section = false; + } + +void case_59() +#line 729 "cs-parser.jay" +{ + yyVal = yyVals[-2+yyTop]; + lbag.InsertLocation (yyVal, 0, PopLocation ()); + if (yyVals[-1+yyTop] != null) { + lbag.AddLocation (yyVal, GetLocation(yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddLocation (yyVal, GetLocation(yyVals[0+yyTop])); + } + } + +void case_60() +#line 739 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) yyVals[-1+yyTop]; + var tne = new SimpleName (lt.Value, null, lt.Location); + + yyVal = new List () { + new Attribute (null, tne, null, GetLocation (yyVals[-1+yyTop]), false) + }; + } + +void case_61() +#line 750 "cs-parser.jay" +{ + CheckAttributeTarget (yyToken, GetTokenName (yyToken), GetLocation (yyVals[0+yyTop])); + yyVal = null; + } + +void case_62() +#line 758 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = CheckAttributeTarget (yyToken, lt.Value, lt.Location); + PushLocation (GetLocation (yyVals[0+yyTop])); + } + +void case_66() +#line 773 "cs-parser.jay" +{ + var attrs = (List) yyVals[-2+yyTop]; + if (attrs != null) { + attrs.Add ((Attribute) yyVals[0+yyTop]); + lbag.AddLocation (attrs, GetLocation (yyVals[-1+yyTop])); + } + + yyVal = attrs; + } + +void case_68() +#line 790 "cs-parser.jay" +{ + --lexer.parsing_block; + + var tne = (ATypeNameExpression) yyVals[-2+yyTop]; + if (tne.HasTypeArguments) { + report.Error (404, tne.Location, "Attributes cannot be generic"); + } + Arguments [] arguments = (Arguments []) yyVals[0+yyTop]; + + yyVal = new Attribute (current_attr_target, tne, (Arguments[]) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop]), lexer.IsEscapedIdentifier (tne)); + if (arguments != null) { + attributeArgumentCommas.Insert (0, savedAttrParenOpenLocation); + attributeArgumentCommas.Add (savedAttrParenCloseLocation); + lbag.AddLocation (yyVal, attributeArgumentCommas); + attributeArgumentCommas.Clear (); + } else if (HadAttributeParens) { + lbag.AddLocation (yyVal, savedAttrParenOpenLocation, savedAttrParenCloseLocation); + } + } + +void case_71() +#line 818 "cs-parser.jay" +{ + savedAttrParenOpenLocation = GetLocation (yyVals[-2+yyTop]); + savedAttrParenCloseLocation = GetLocation (yyVals[0+yyTop]); + yyVal = yyVals[-1+yyTop]; + HadAttributeParens = true; + } + +void case_73() +#line 830 "cs-parser.jay" +{ + Arguments a = new Arguments (4); + a.Add ((Argument) yyVals[0+yyTop]); + yyVal = new Arguments [] { a, null }; + } + +void case_74() +#line 836 "cs-parser.jay" +{ + Arguments a = new Arguments (4); + a.Add ((Argument) yyVals[0+yyTop]); + yyVal = new Arguments [] { null, a }; + } + +void case_75() +#line 842 "cs-parser.jay" +{ + Arguments[] o = (Arguments[]) yyVals[-2+yyTop]; + if (o [1] != null) { + report.Error (1016, ((Argument) yyVals[0+yyTop]).Expr.Location, "Named attribute arguments must appear after the positional arguments"); + o [0] = new Arguments (4); + } + + Arguments args = ((Arguments) o [0]); + if (args.Count > 0 && !(yyVals[0+yyTop] is NamedArgument) && args [args.Count - 1] is NamedArgument) + Error_NamedArgumentExpected ((NamedArgument) args [args.Count - 1]); + + args.Add ((Argument) yyVals[0+yyTop]); + attributeArgumentCommas.Add (GetLocation (yyVals[-1+yyTop])); + } + +void case_76() +#line 857 "cs-parser.jay" +{ + Arguments[] o = (Arguments[]) yyVals[-2+yyTop]; + if (o [1] == null) { + o [1] = new Arguments (4); + } + + ((Arguments) o [1]).Add ((Argument) yyVals[0+yyTop]); + attributeArgumentCommas.Add (GetLocation (yyVals[-1+yyTop])); + } + +void case_79() +#line 875 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_81() +#line 887 "cs-parser.jay" +{ + --lexer.parsing_block; + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new NamedArgument (lt.Value, lt.Location, (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation(yyVals[-2+yyTop])); + } + +void case_82() +#line 897 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.V_3) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "named argument"); + + /* Avoid boxing in common case (no modifier)*/ + var arg_mod = yyVals[-1+yyTop] == null ? Argument.AType.None : (Argument.AType) yyVals[-1+yyTop]; + + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new NamedArgument (lt.Value, lt.Location, (Expression) yyVals[0+yyTop], arg_mod); + lbag.AddLocation (yyVal, GetLocation(yyVals[-2+yyTop])); + } + +void case_88() +#line 929 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + +void case_89() +#line 934 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + +void case_103() +#line 955 "cs-parser.jay" +{ + report.Error (1519, lexer.Location, "Unexpected symbol `{0}' in class, struct, or interface member declaration", + GetSymbolName (yyToken)); + yyVal = null; + lexer.parsing_generic_declaration = false; + } + +void case_104() +#line 965 "cs-parser.jay" +{ + current_local_parameters = current_type.PrimaryConstructorParameters; + if (current_local_parameters == null) { + report.Error (9010, GetLocation (yyVals[0+yyTop]), "Primary constructor body is not allowed"); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + + ++lexer.parsing_block; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_105() +#line 976 "cs-parser.jay" +{ + current_local_parameters = null; + var t = current_type as ClassOrStruct; + if (t != null) { + var b = (ToplevelBlock) yyVals[0+yyTop]; + if (t.PrimaryConstructorBlock != null) { + report.Error (8041, b.StartLocation, "Primary constructor already has a body"); + } else { + t.PrimaryConstructorBlock = b; + } + } + } + +void case_107() +#line 998 "cs-parser.jay" +{ + lexer.ConstraintsParsing = true; + valid_param_mod = ParameterModifierType.PrimaryConstructor; + push_current_container (new Struct (current_container, (MemberName) yyVals[0+yyTop], (Modifiers) yyVals[-4+yyTop], (Attributes) yyVals[-5+yyTop]), yyVals[-3+yyTop]); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-2+yyTop])); + } + +void case_108() +#line 1007 "cs-parser.jay" +{ + valid_param_mod = 0; + lexer.ConstraintsParsing = false; + + if (yyVals[-2+yyTop] != null) + current_type.PrimaryConstructorParameters = (ParametersCompiled) yyVals[-2+yyTop]; + + if (yyVals[0+yyTop] != null) + current_container.SetConstraints ((List) yyVals[0+yyTop]); + + if (doc_support) + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + + + lexer.parsing_modifiers = true; + } + +void case_109() +#line 1024 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_110() +#line 1029 "cs-parser.jay" +{ + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_111() +#line 1035 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] == null) { + lbag.AppendToMember (current_container, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-2+yyTop])); + } else { + lbag.AppendToMember (current_container, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + yyVal = pop_current_class (); + } + +void case_113() +#line 1053 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var mod = (Modifiers) yyVals[-3+yyTop]; + current_field = new Const (current_type, (FullNamedExpression) yyVals[-1+yyTop], mod, new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-4+yyTop]); + current_type.AddMember (current_field); + + if ((mod & Modifiers.STATIC) != 0) { + report.Error (504, current_field.Location, "The constant `{0}' cannot be marked static", current_field.GetSignatureForError ()); + } + + yyVal = current_field; + } + +void case_114() +#line 1066 "cs-parser.jay" +{ + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + current_field.Initializer = (ConstInitializer) yyVals[-2+yyTop]; + lbag.AddMember (current_field, GetModifierLocations (), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[0+yyTop])); + current_field = null; + } + +void case_115() +#line 1079 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + current_type.AddMember (new Const (current_type, (FullNamedExpression) yyVals[-1+yyTop], (Modifiers) yyVals[-3+yyTop], MemberName.Null, (Attributes) yyVals[-4+yyTop])); + } + +void case_120() +#line 1104 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (ConstInitializer) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_122() +#line 1117 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = new ConstInitializer (current_field, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_123() +#line 1123 "cs-parser.jay" +{ + report.Error (145, lexer.Location, "A const field requires a value to be provided"); + yyVal = null; + } + +void case_126() +#line 1138 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + + FullNamedExpression type = (FullNamedExpression) yyVals[-1+yyTop]; + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (670, GetLocation (yyVals[-1+yyTop]), "Fields cannot have void type"); + + var lt = (LocatedToken) yyVals[0+yyTop]; + current_field = new Field (current_type, type, (Modifiers) yyVals[-2+yyTop], new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-3+yyTop]); + current_type.AddField (current_field); + yyVal = current_field; + } + +void case_127() +#line 1153 "cs-parser.jay" +{ + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lbag.AddMember (current_field, GetModifierLocations (), GetLocation (yyVals[0+yyTop])); + yyVal = current_field; + current_field = null; + } + +void case_128() +#line 1166 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "fixed size buffers"); + + var lt = (LocatedToken) yyVals[0+yyTop]; + current_field = new FixedField (current_type, (FullNamedExpression) yyVals[-1+yyTop], (Modifiers) yyVals[-3+yyTop], + new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-4+yyTop]); + + current_type.AddField (current_field); + } + +void case_129() +#line 1177 "cs-parser.jay" +{ + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + current_field.Initializer = (ConstInitializer) yyVals[-2+yyTop]; + lbag.AddMember (current_field, GetModifierLocations (), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[0+yyTop])); + yyVal = current_field; + current_field = null; + } + +void case_132() +#line 1200 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_133() +#line 1206 "cs-parser.jay" +{ + --lexer.parsing_block; + current_field.Initializer = (Expression) yyVals[0+yyTop]; + lbag.AppendToMember (current_field, GetLocation (yyVals[-2+yyTop])); + end_block (lexer.Location); + current_local_parameters = null; + } + +void case_138() +#line 1233 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_140() +#line 1243 "cs-parser.jay" +{ + --lexer.parsing_block; + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_145() +#line 1269 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (ConstInitializer) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_147() +#line 1282 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = new ConstInitializer (current_field, (Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_148() +#line 1288 "cs-parser.jay" +{ + report.Error (443, lexer.Location, "Value or constant expected"); + yyVal = null; + } + +void case_151() +#line 1298 "cs-parser.jay" +{ + /* It has to be here for the parent to safely restore artificial block*/ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_152() +#line 1307 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.NotAllowed; + + /* Was added earlier in the case of body being eof for full ast*/ + } + +void case_153() +#line 1314 "cs-parser.jay" +{ + Method method = (Method) yyVals[-2+yyTop]; + method.Block = (ToplevelBlock) yyVals[0+yyTop]; + async_block = false; + + if (method.Block == null) { + lbag.AppendToMember (method, savedLocation); /* semicolon*/ + method.ParameterInfo.CheckParameters (method); + + if ((method.ModFlags & Modifiers.ASYNC) != 0) { + report.Error (1994, method.Location, "`{0}': The async modifier can only be used with methods that have a body", + method.GetSignatureForError ()); + } + } else { + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, method.Location, "`{0}': interface members cannot have a definition", + method.GetSignatureForError ()); + } + } + + current_local_parameters = null; + + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_155() +#line 1350 "cs-parser.jay" +{ + valid_param_mod = 0; + MemberName name = (MemberName) yyVals[-4+yyTop]; + current_local_parameters = (ParametersCompiled) yyVals[-1+yyTop]; + + var method = Method.Create (current_type, (FullNamedExpression) yyVals[-5+yyTop], (Modifiers) yyVals[-6+yyTop], + name, current_local_parameters, (Attributes) yyVals[-7+yyTop]); + + current_type.AddMember (method); + + async_block = (method.ModFlags & Modifiers.ASYNC) != 0; + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + lbag.AddMember (method, GetModifierLocations (), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + yyVal = method; + + lexer.ConstraintsParsing = true; + } + +void case_156() +#line 1371 "cs-parser.jay" +{ + lexer.ConstraintsParsing = false; + + if (yyVals[0+yyTop] != null) { + var method = (Method) yyVals[-1+yyTop]; + method.SetConstraints ((List) yyVals[0+yyTop]); + } + + yyVal = yyVals[-1+yyTop]; + } + +void case_158() +#line 1390 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + valid_param_mod = ParameterModifierType.All; + } + +void case_160() +#line 1399 "cs-parser.jay" +{ + lexer.ConstraintsParsing = false; + valid_param_mod = 0; + + MemberName name = (MemberName) yyVals[-6+yyTop]; + current_local_parameters = (ParametersCompiled) yyVals[-3+yyTop]; + + var modifiers = (Modifiers) yyVals[-10+yyTop]; + modifiers |= Modifiers.PARTIAL; + + var method = Method.Create (current_type, new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[-8+yyTop])), + modifiers, name, current_local_parameters, (Attributes) yyVals[-11+yyTop]); + + current_type.AddMember (method); + + async_block = (method.ModFlags & Modifiers.ASYNC) != 0; + + if (yyVals[0+yyTop] != null) + method.SetConstraints ((List) yyVals[0+yyTop]); + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + StoreModifierLocation (Modifiers.PARTIAL, GetLocation (yyVals[-9+yyTop])); + lbag.AddMember (method, GetModifierLocations (), GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-2+yyTop])); + yyVal = method; + } + +void case_161() +#line 1430 "cs-parser.jay" +{ + MemberName name = (MemberName) yyVals[-3+yyTop]; + report.Error (1585, name.Location, + "Member modifier `{0}' must precede the member type and name", ModifiersExtensions.Name ((Modifiers) yyVals[-4+yyTop])); + + var method = Method.Create (current_type, (FullNamedExpression) yyVals[-5+yyTop], + 0, name, (ParametersCompiled) yyVals[-1+yyTop], (Attributes) yyVals[-7+yyTop]); + + current_type.AddMember (method); + + current_local_parameters = (ParametersCompiled) yyVals[-1+yyTop]; + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + yyVal = method; + } + +void case_162() +#line 1451 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.Undefined; + + MemberName name = (MemberName) yyVals[-1+yyTop]; + var method = Method.Create (current_type, (FullNamedExpression) yyVals[-2+yyTop], (Modifiers) yyVals[-3+yyTop], + name, current_local_parameters, (Attributes) yyVals[-4+yyTop]); + + current_type.AddMember (method); + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + yyVal = method; + } + +void case_167() +#line 1480 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.V_6) { + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "expression bodied members"); + } + + ++lexer.parsing_block; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_168() +#line 1489 "cs-parser.jay" +{ + lexer.parsing_block = 0; + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + var b = end_block (GetLocation (yyVals[0+yyTop])); + b.IsCompilerGenerated = true; + yyVal = b; + } + +void case_171() +#line 1505 "cs-parser.jay" +{ + var pars_list = (List) yyVals[0+yyTop]; + yyVal = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_172() +#line 1511 "cs-parser.jay" +{ + var pars_list = (List) yyVals[-2+yyTop]; + pars_list.Add ((Parameter) yyVals[0+yyTop]); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_173() +#line 1520 "cs-parser.jay" +{ + var pars_list = (List) yyVals[-2+yyTop]; + pars_list.Add (new ArglistParameter (GetLocation (yyVals[0+yyTop]))); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_174() +#line 1529 "cs-parser.jay" +{ + if (yyVals[-2+yyTop] != null) + report.Error (231, ((Parameter) yyVals[-2+yyTop]).Location, "A params parameter must be the last parameter in a formal parameter list"); + + yyVal = new ParametersCompiled (new Parameter[] { (Parameter) yyVals[-2+yyTop] } ); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_175() +#line 1537 "cs-parser.jay" +{ + if (yyVals[-2+yyTop] != null) + report.Error (231, ((Parameter) yyVals[-2+yyTop]).Location, "A params parameter must be the last parameter in a formal parameter list"); + + var pars_list = (List) yyVals[-4+yyTop]; + pars_list.Add (new ArglistParameter (GetLocation (yyVals[-2+yyTop]))); + parameterListCommas.Add (GetLocation (yyVals[-3+yyTop])); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_176() +#line 1550 "cs-parser.jay" +{ + report.Error (257, GetLocation (yyVals[-2+yyTop]), "An __arglist parameter must be the last parameter in a formal parameter list"); + + yyVal = new ParametersCompiled (new Parameter [] { new ArglistParameter (GetLocation (yyVals[-2+yyTop])) }, true); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_177() +#line 1557 "cs-parser.jay" +{ + report.Error (257, GetLocation (yyVals[-2+yyTop]), "An __arglist parameter must be the last parameter in a formal parameter list"); + + var pars_list = (List) yyVals[-4+yyTop]; + pars_list.Add (new ArglistParameter (GetLocation (yyVals[-2+yyTop]))); + parameterListCommas.Add (GetLocation (yyVals[-3+yyTop])); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_180() +#line 1577 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = ParametersCompiled.EmptyReadOnlyParameters; + } + +void case_181() +#line 1585 "cs-parser.jay" +{ + parameters_bucket.Clear (); + Parameter p = (Parameter) yyVals[0+yyTop]; + parameters_bucket.Add (p); + parameterListCommas.Clear (); + default_parameter_used = p.HasDefaultValue; + yyVal = parameters_bucket; + } + +void case_182() +#line 1594 "cs-parser.jay" +{ + var pars = (List) yyVals[-2+yyTop]; + Parameter p = (Parameter) yyVals[0+yyTop]; + if (p != null) { + if (p.HasExtensionMethodModifier) + report.Error (1100, p.Location, "The parameter modifier `this' can only be used on the first parameter"); + else if (!p.HasDefaultValue && default_parameter_used) + report.Error (1737, p.Location, "Optional parameter cannot precede required parameters"); + + default_parameter_used |= p.HasDefaultValue; + pars.Add (p); + + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + } + + yyVal = yyVals[-2+yyTop]; + } + +void case_183() +#line 1618 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new Parameter ((FullNamedExpression) yyVals[-1+yyTop], lt.Value, (Parameter.Modifier) yyVals[-2+yyTop], (Attributes) yyVals[-3+yyTop], lt.Location); + lbag.AddLocation (yyVal, parameterModifierLocation); + } + +void case_184() +#line 1627 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + report.Error (1552, lt.Location, "Array type specifier, [], must appear before parameter name"); + yyVal = new Parameter ((FullNamedExpression) yyVals[-3+yyTop], lt.Value, (Parameter.Modifier) yyVals[-4+yyTop], (Attributes) yyVals[-5+yyTop], lt.Location); + lbag.AddLocation (yyVal, parameterModifierLocation); + } + +void case_185() +#line 1634 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + Location l = GetLocation (yyVals[0+yyTop]); + yyVal = new Parameter (null, null, Parameter.Modifier.NONE, (Attributes) yyVals[-1+yyTop], l); + } + +void case_186() +#line 1643 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + Location l = GetLocation (yyVals[0+yyTop]); + yyVal = new Parameter ((FullNamedExpression) yyVals[-1+yyTop], null, (Parameter.Modifier) yyVals[-2+yyTop], (Attributes) yyVals[-3+yyTop], l); + lbag.AddLocation (yyVal, parameterModifierLocation); + } + +void case_188() +#line 1658 "cs-parser.jay" +{ + --lexer.parsing_block; + if (lang_version <= LanguageVersion.V_3) { + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "optional parameter"); + } + + Parameter.Modifier mod = (Parameter.Modifier) yyVals[-5+yyTop]; + if (mod != Parameter.Modifier.NONE) { + switch (mod) { + case Parameter.Modifier.REF: + case Parameter.Modifier.OUT: + report.Error (1741, GetLocation (yyVals[-5+yyTop]), "Cannot specify a default value for the `{0}' parameter", + Parameter.GetModifierSignature (mod)); + break; + + case Parameter.Modifier.This: + report.Error (1743, GetLocation (yyVals[-5+yyTop]), "Cannot specify a default value for the `{0}' parameter", + Parameter.GetModifierSignature (mod)); + break; + default: + throw new NotImplementedException (mod.ToString ()); + } + + mod = Parameter.Modifier.NONE; + } + + if ((valid_param_mod & ParameterModifierType.DefaultValue) == 0) + report.Error (1065, GetLocation (yyVals[-2+yyTop]), "Optional parameter is not valid in this context"); + + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new Parameter ((FullNamedExpression) yyVals[-4+yyTop], lt.Value, mod, (Attributes) yyVals[-6+yyTop], lt.Location); + lbag.AddLocation (yyVal, parameterModifierLocation, GetLocation (yyVals[-2+yyTop])); /* parameterModifierLocation should be ignored when mod == NONE*/ + + if (yyVals[0+yyTop] != null) + ((Parameter) yyVal).DefaultValue = new DefaultParameterValueExpression ((Expression) yyVals[0+yyTop]); + } + +void case_192() +#line 1707 "cs-parser.jay" +{ + Parameter.Modifier p2 = (Parameter.Modifier)yyVals[0+yyTop]; + Parameter.Modifier mod = (Parameter.Modifier)yyVals[-1+yyTop] | p2; + if (((Parameter.Modifier)yyVals[-1+yyTop] & p2) == p2) { + Error_DuplicateParameterModifier (lexer.Location, p2); + } else { + switch (mod & ~Parameter.Modifier.This) { + case Parameter.Modifier.REF: + report.Error (1101, lexer.Location, "The parameter modifiers `this' and `ref' cannot be used altogether"); + break; + case Parameter.Modifier.OUT: + report.Error (1102, lexer.Location, "The parameter modifiers `this' and `out' cannot be used altogether"); + break; + default: + report.Error (1108, lexer.Location, "A parameter cannot have specified more than one modifier"); + break; + } + } + yyVal = mod; + } + +void case_193() +#line 1731 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.Ref) == 0) + Error_ParameterModifierNotValid ("ref", GetLocation (yyVals[0+yyTop])); + parameterModifierLocation = GetLocation (yyVals[0+yyTop]); + yyVal = Parameter.Modifier.REF; + } + +void case_194() +#line 1738 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.Out) == 0) + Error_ParameterModifierNotValid ("out", GetLocation (yyVals[0+yyTop])); + parameterModifierLocation = GetLocation (yyVals[0+yyTop]); + yyVal = Parameter.Modifier.OUT; + } + +void case_195() +#line 1745 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.This) == 0) + Error_ParameterModifierNotValid ("this", GetLocation (yyVals[0+yyTop])); + + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "extension methods"); + parameterModifierLocation = GetLocation (yyVals[0+yyTop]); + yyVal = Parameter.Modifier.This; + } + +void case_196() +#line 1758 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new ParamsParameter ((FullNamedExpression) yyVals[-1+yyTop], lt.Value, (Attributes) yyVals[-3+yyTop], lt.Location); + lbag.AddLocation (yyVal, savedLocation); + } + +void case_197() +#line 1764 "cs-parser.jay" +{ + report.Error (1751, GetLocation (yyVals[-4+yyTop]), "Cannot specify a default value for a parameter array"); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new ParamsParameter ((FullNamedExpression) yyVals[-3+yyTop], lt.Value, (Attributes) yyVals[-5+yyTop], lt.Location); + lbag.AddLocation (yyVal, savedLocation); + } + +void case_198() +#line 1772 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new ParamsParameter ((FullNamedExpression) yyVals[-1+yyTop], null, (Attributes) yyVals[-3+yyTop], Location.Null); + } + +void case_199() +#line 1781 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.Params) == 0) + report.Error (1670, (GetLocation (yyVals[0+yyTop])), "The `params' modifier is not allowed in current context"); + savedLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_200() +#line 1787 "cs-parser.jay" +{ + Parameter.Modifier mod = (Parameter.Modifier)yyVals[0+yyTop]; + if ((mod & Parameter.Modifier.This) != 0) { + report.Error (1104, GetLocation (yyVals[-1+yyTop]), "The parameter modifiers `this' and `params' cannot be used altogether"); + } else { + report.Error (1611, GetLocation (yyVals[-1+yyTop]), "The params parameter cannot be declared as ref or out"); + } + savedLocation = GetLocation (yyVals[-1+yyTop]); + } + +void case_202() +#line 1804 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.Arglist) == 0) + report.Error (1669, GetLocation (yyVals[0+yyTop]), "__arglist is not valid in this context"); + } + +void case_203() +#line 1815 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + if (doc_support) + tmpComment = Lexer.consume_doc_comment (); + } + +void case_204() +#line 1821 "cs-parser.jay" +{ + var type = (FullNamedExpression) yyVals[-3+yyTop]; + current_property = new Property (current_type, type, (Modifiers) yyVals[-4+yyTop], + (MemberName) yyVals[-2+yyTop], (Attributes) yyVals[-5+yyTop]); + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (547, GetLocation (yyVals[-3+yyTop]), "`{0}': property or indexer cannot have void type", current_property.GetSignatureForError ()); + + current_type.AddMember (current_property); + lbag.AddMember (current_property, GetModifierLocations (), GetLocation (yyVals[0+yyTop])); + + lexer.PropertyParsing = true; + } + +void case_205() +#line 1835 "cs-parser.jay" +{ + lexer.PropertyParsing = false; + + if (doc_support) + current_property.DocComment = ConsumeStoredComment (); + } + +void case_206() +#line 1842 "cs-parser.jay" +{ + lbag.AppendToMember (current_property, GetLocation (yyVals[0+yyTop])); + lexer.parsing_modifiers = true; + } + +void case_208() +#line 1854 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + if (doc_support) + tmpComment = Lexer.consume_doc_comment (); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + +void case_209() +#line 1861 "cs-parser.jay" +{ + var type = (FullNamedExpression) yyVals[-3+yyTop]; + var property = new Property (current_type, type, (Modifiers) yyVals[-4+yyTop], + (MemberName) yyVals[-2+yyTop], (Attributes) yyVals[-5+yyTop]); + + property.Get = new Property.GetMethod (property, Modifiers.COMPILER_GENERATED, null, property.Location); + property.Get.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, property.Get.Block.StartLocation, + "`{0}': interface members cannot have a definition", property.GetSignatureForError ()); + } + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (547, GetLocation (yyVals[-3+yyTop]), "`{0}': property or indexer cannot have void type", property.GetSignatureForError ()); + + current_type.AddMember (property); + + current_local_parameters = null; + } + +void case_211() +#line 1886 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_212() +#line 1892 "cs-parser.jay" +{ + --lexer.parsing_block; + ((Property)current_property).Initializer = (Expression) yyVals[-1+yyTop]; + lbag.AppendToMember (current_property, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + end_block (GetLocation (yyVals[0+yyTop])); + current_local_parameters = null; + } + +void case_216() +#line 1913 "cs-parser.jay" +{ + valid_param_mod = 0; + var type = (FullNamedExpression) yyVals[-5+yyTop]; + Indexer indexer = new Indexer (current_type, type, (MemberName) yyVals[-4+yyTop], (Modifiers) yyVals[-6+yyTop], (ParametersCompiled) yyVals[-1+yyTop], (Attributes) yyVals[-7+yyTop]); + + current_property = indexer; + + current_type.AddIndexer (indexer); + lbag.AddMember (current_property, GetModifierLocations (), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (620, GetLocation (yyVals[-5+yyTop]), "`{0}': indexer return type cannot be `void'", indexer.GetSignatureForError ()); + + if (indexer.ParameterInfo.IsEmpty) { + report.Error (1551, GetLocation (yyVals[-3+yyTop]), "Indexers must have at least one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.PropertyParsing = true; + current_local_parameters = (ParametersCompiled) yyVals[-1+yyTop]; + } + +void case_217() +#line 1939 "cs-parser.jay" +{ + lexer.PropertyParsing = false; + current_local_parameters = null; + + if (current_property.AccessorFirst != null && current_property.AccessorFirst.Block == null) + ((Indexer) current_property).ParameterInfo.CheckParameters (current_property); + + if (doc_support) + current_property.DocComment = ConsumeStoredComment (); + + current_property = null; + } + +void case_219() +#line 1959 "cs-parser.jay" +{ + current_property.Get = new Indexer.GetIndexerMethod (current_property, Modifiers.COMPILER_GENERATED, current_local_parameters, null, current_property.Location); + current_property.Get.Block = (ToplevelBlock) yyVals[0+yyTop]; + } + +void case_224() +#line 1971 "cs-parser.jay" +{ + if (yyToken == Token.CLOSE_BRACE) { + report.Error (548, lexer.Location, "`{0}': property or indexer must have at least one accessor", current_property.GetSignatureForError ()); + } else { + if (yyToken == Token.SEMICOLON) + report.Error (1597, lexer.Location, "Semicolon after method or accessor block is not valid"); + else + report.Error (1014, GetLocation (yyVals[0+yyTop]), "A get or set accessor expected"); + } + } + +void case_225() +#line 1985 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != ModifierNone && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (GetLocation (yyVals[-1+yyTop]), "access modifiers on properties"); + } + + if (current_property.Get != null) { + report.Error (1007, GetLocation (yyVals[0+yyTop]), "Property accessor already defined"); + } + + if (current_property is Indexer) { + current_property.Get = new Indexer.GetIndexerMethod (current_property, (Modifiers) yyVals[-1+yyTop], ((Indexer)current_property).ParameterInfo.Clone (), + (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + } else { + current_property.Get = new Property.GetMethod (current_property, + (Modifiers) yyVals[-1+yyTop], (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + } + + current_local_parameters = current_property.Get.ParameterInfo; + lexer.PropertyParsing = false; + } + +void case_226() +#line 2006 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + current_property.Get.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_property.Get.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_property.Get.GetSignatureForError ()); + } + lbag.AddMember (current_property.Get, GetModifierLocations ()); + } else { + lbag.AddMember (current_property.Get, GetModifierLocations (), savedLocation); + } + + current_local_parameters = null; + lexer.PropertyParsing = true; + + if (doc_support) + if (Lexer.doc_state == XmlCommentState.Error) + Lexer.doc_state = XmlCommentState.NotAllowed; + } + +void case_227() +#line 2030 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != ModifierNone && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (GetLocation (yyVals[-1+yyTop]), "access modifiers on properties"); + } + + if (current_property.Set != null) { + report.Error (1007, GetLocation (yyVals[0+yyTop]), "Property accessor already defined"); + } + + if (current_property is Indexer) { + current_property.Set = new Indexer.SetIndexerMethod (current_property, (Modifiers) yyVals[-1+yyTop], + ParametersCompiled.MergeGenerated (compiler, + ((Indexer)current_property).ParameterInfo, true, new Parameter ( + current_property.TypeExpression, "value", Parameter.Modifier.NONE, null, GetLocation (yyVals[0+yyTop])), + null), + (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + } else { + current_property.Set = new Property.SetMethod (current_property, (Modifiers) yyVals[-1+yyTop], + ParametersCompiled.CreateImplicitParameter (current_property.TypeExpression, GetLocation (yyVals[0+yyTop])), + (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + } + + current_local_parameters = current_property.Set.ParameterInfo; + lexer.PropertyParsing = false; + } + +void case_228() +#line 2056 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + current_property.Set.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_property.Set.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_property.Set.GetSignatureForError ()); + } + lbag.AddMember (current_property.Set, GetModifierLocations ()); + } else { + lbag.AddMember (current_property.Set, GetModifierLocations (), savedLocation); + } + + current_local_parameters = null; + lexer.PropertyParsing = true; + + if (doc_support + && Lexer.doc_state == XmlCommentState.Error) + Lexer.doc_state = XmlCommentState.NotAllowed; + } + +void case_230() +#line 2081 "cs-parser.jay" +{ + savedLocation = GetLocation (yyVals[0+yyTop]); + yyVal = null; + } + +void case_231() +#line 2086 "cs-parser.jay" +{ + Error_SyntaxError (1043, yyToken, "Invalid accessor body"); + yyVal = null; + } + +void case_233() +#line 2100 "cs-parser.jay" +{ + lexer.ConstraintsParsing = true; + push_current_container (new Interface (current_container, (MemberName) yyVals[0+yyTop], (Modifiers) yyVals[-4+yyTop], (Attributes) yyVals[-5+yyTop]), yyVals[-3+yyTop]); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-2+yyTop])); + } + +void case_234() +#line 2107 "cs-parser.jay" +{ + lexer.ConstraintsParsing = false; + + if (yyVals[0+yyTop] != null) + current_container.SetConstraints ((List) yyVals[0+yyTop]); + + if (doc_support) { + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.parsing_modifiers = true; + } + +void case_235() +#line 2121 "cs-parser.jay" +{ + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_236() +#line 2127 "cs-parser.jay" +{ + if (yyVals[0+yyTop] == null) { + lbag.AppendToMember (current_container, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } else { + lbag.AppendToMember (current_container, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + yyVal = pop_current_class (); + } + +void case_240() +#line 2148 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + +void case_241() +#line 2153 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + +void case_252() +#line 2191 "cs-parser.jay" +{ + OperatorDeclaration decl = (OperatorDeclaration) yyVals[-2+yyTop]; + if (decl != null) { + Operator op = new Operator ( + current_type, decl.optype, decl.ret_type, (Modifiers) yyVals[-3+yyTop], + current_local_parameters, + (ToplevelBlock) yyVals[0+yyTop], (Attributes) yyVals[-4+yyTop], decl.location); + + if (op.Block == null) + op.ParameterInfo.CheckParameters (op); + + if (doc_support) { + op.DocComment = tmpComment; + Lexer.doc_state = XmlCommentState.Allowed; + } + + /* Note again, checking is done in semantic analysis*/ + current_type.AddOperator (op); + + lbag.AddMember (op, GetModifierLocations (), lbag.GetLocations (decl)); + if (yyVals[0+yyTop] == null) { /* Semicolon*/ + lbag.AddLocation (op, savedLocation); + } + } + + current_local_parameters = null; + } + +void case_254() +#line 2223 "cs-parser.jay" +{ + report.Error (590, GetLocation (yyVals[0+yyTop]), "User-defined operators cannot return void"); + yyVal = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[0+yyTop])); + } + +void case_256() +#line 2235 "cs-parser.jay" +{ + valid_param_mod = 0; + + Location loc = GetLocation (yyVals[-5+yyTop]); + Operator.OpType op = (Operator.OpType) yyVals[-4+yyTop]; + current_local_parameters = (ParametersCompiled)yyVals[-1+yyTop]; + + int p_count = current_local_parameters.Count; + if (p_count == 1) { + if (op == Operator.OpType.Addition) + op = Operator.OpType.UnaryPlus; + else if (op == Operator.OpType.Subtraction) + op = Operator.OpType.UnaryNegation; + } + + if (IsUnaryOperator (op)) { + if (p_count == 2) { + report.Error (1020, loc, "Overloadable binary operator expected"); + } else if (p_count != 1) { + report.Error (1535, loc, "Overloaded unary operator `{0}' takes one parameter", + Operator.GetName (op)); + } + } else { + if (p_count == 1) { + report.Error (1019, loc, "Overloadable unary operator expected"); + } else if (p_count != 2) { + report.Error (1534, loc, "Overloaded binary operator `{0}' takes two parameters", + Operator.GetName (op)); + } + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + yyVal = new OperatorDeclaration (op, (FullNamedExpression) yyVals[-6+yyTop], loc); + lbag.AddLocation (yyVal, GetLocation (yyVals[-5+yyTop]), savedOperatorLocation, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_281() +#line 2311 "cs-parser.jay" +{ + valid_param_mod = 0; + + Location loc = GetLocation (yyVals[-5+yyTop]); + current_local_parameters = (ParametersCompiled)yyVals[-1+yyTop]; + + if (current_local_parameters.Count != 1) { + report.Error (1535, loc, "Overloaded unary operator `implicit' takes one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + yyVal = new OperatorDeclaration (Operator.OpType.Implicit, (FullNamedExpression) yyVals[-4+yyTop], loc); + lbag.AddLocation (yyVal, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_283() +#line 2334 "cs-parser.jay" +{ + valid_param_mod = 0; + + Location loc = GetLocation (yyVals[-5+yyTop]); + current_local_parameters = (ParametersCompiled)yyVals[-1+yyTop]; + + if (current_local_parameters.Count != 1) { + report.Error (1535, loc, "Overloaded unary operator `explicit' takes one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + yyVal = new OperatorDeclaration (Operator.OpType.Explicit, (FullNamedExpression) yyVals[-4+yyTop], loc); + lbag.AddLocation (yyVal, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_284() +#line 2353 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + yyVal = new OperatorDeclaration (Operator.OpType.Implicit, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_285() +#line 2359 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + yyVal = new OperatorDeclaration (Operator.OpType.Explicit, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_286() +#line 2369 "cs-parser.jay" +{ + Constructor c = (Constructor) yyVals[-1+yyTop]; + c.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (doc_support) + c.DocComment = ConsumeStoredComment (); + + current_local_parameters = null; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_287() +#line 2386 "cs-parser.jay" +{ + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + valid_param_mod = ParameterModifierType.All; + } + +void case_288() +#line 2395 "cs-parser.jay" +{ + valid_param_mod = 0; + current_local_parameters = (ParametersCompiled) yyVals[-1+yyTop]; + + var lt = (LocatedToken) yyVals[-4+yyTop]; + var mods = (Modifiers) yyVals[-5+yyTop]; + var c = new Constructor (current_type, lt.Value, mods, (Attributes) yyVals[-6+yyTop], current_local_parameters, lt.Location); + + if (lt.Value != current_container.MemberName.Name) { + report.Error (1520, c.Location, "Class, struct, or interface method must have a return type"); + } else if ((mods & Modifiers.STATIC) != 0) { + if ((mods & Modifiers.AccessibilityMask) != 0){ + report.Error (515, c.Location, + "`{0}': static constructor cannot have an access modifier", + c.GetSignatureForError ()); + } + } + + current_type.AddConstructor (c); + lbag.AddMember (c, GetModifierLocations (), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + yyVal = c; + + /**/ + /* start block here, so possible anonymous methods inside*/ + /* constructor initializer can get correct parent block*/ + /**/ + start_block (lexer.Location); + } + +void case_289() +#line 2424 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + var c = (Constructor) yyVals[-1+yyTop]; + c.Initializer = (ConstructorInitializer) yyVals[0+yyTop]; + + if (c.IsStatic) { + report.Error (514, c.Location, + "`{0}': static constructor cannot have an explicit `this' or `base' constructor call", + c.GetSignatureForError ()); + } + } + + yyVal = yyVals[-1+yyTop]; + } + +void case_295() +#line 2456 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = new ConstructorBaseInitializer ((Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_297() +#line 2466 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = new ConstructorThisInitializer ((Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_298() +#line 2472 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new ConstructorThisInitializer (null, GetLocation (yyVals[0+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_299() +#line 2478 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_300() +#line 2486 "cs-parser.jay" +{ + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + +void case_301() +#line 2495 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + if (lt.Value != current_container.MemberName.Name){ + report.Error (574, lt.Location, "Name of destructor must match name of class"); + } else if (current_container.Kind != MemberKind.Class){ + report.Error (575, lt.Location, "Only class types can contain destructor"); + } + + Destructor d = new Destructor (current_type, (Modifiers) yyVals[-6+yyTop], + ParametersCompiled.EmptyReadOnlyParameters, (Attributes) yyVals[-7+yyTop], lt.Location); + d.Identifier = lt.Value; + if (doc_support) + d.DocComment = ConsumeStoredComment (); + + d.Block = (ToplevelBlock) yyVals[0+yyTop]; + current_type.AddMember (d); + lbag.AddMember (d, GetModifierLocations (), GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[-1+yyTop])); + + current_local_parameters = null; + } + +void case_302() +#line 2521 "cs-parser.jay" +{ + current_event_field = new EventField (current_type, (FullNamedExpression) yyVals[-1+yyTop], (Modifiers) yyVals[-3+yyTop], (MemberName) yyVals[0+yyTop], (Attributes) yyVals[-4+yyTop]); + current_type.AddMember (current_event_field); + + if (current_event_field.MemberName.ExplicitInterface != null) { + report.Error (71, current_event_field.Location, "`{0}': An explicit interface implementation of an event must use property syntax", + current_event_field.GetSignatureForError ()); + } + + yyVal = current_event_field; + } + +void case_303() +#line 2535 "cs-parser.jay" +{ + if (doc_support) { + current_event_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + if (current_event_field.Initializer != null) { + lbag.AddMember (current_event_field, GetModifierLocations (), GetLocation (yyVals[-6+yyTop]), savedEventAssignLocation, GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddMember (current_event_field, GetModifierLocations (), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[0+yyTop])); + } + current_event_field = null; + } + +void case_304() +#line 2551 "cs-parser.jay" +{ + current_event = new EventProperty (current_type, (FullNamedExpression) yyVals[-2+yyTop], (Modifiers) yyVals[-4+yyTop], (MemberName) yyVals[-1+yyTop], (Attributes) yyVals[-5+yyTop]); + current_type.AddMember (current_event); + lbag.AddMember (current_event, GetModifierLocations (), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + + lexer.EventParsing = true; + } + +void case_305() +#line 2559 "cs-parser.jay" +{ + if (current_container.Kind == MemberKind.Interface) + report.Error (69, GetLocation (yyVals[-2+yyTop]), "Event in interface cannot have add or remove accessors"); + + lexer.EventParsing = false; + } + +void case_306() +#line 2566 "cs-parser.jay" +{ + if (doc_support) { + current_event.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lbag.AppendToMember (current_event, GetLocation (yyVals[-1+yyTop])); + current_event = null; + current_local_parameters = null; + } + +void case_307() +#line 2579 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + current_type.AddMember (new EventField (current_type, (FullNamedExpression) yyVals[-1+yyTop], (Modifiers) yyVals[-3+yyTop], MemberName.Null, (Attributes) yyVals[-4+yyTop])); + } + +void case_310() +#line 2593 "cs-parser.jay" +{ + --lexer.parsing_block; + savedEventAssignLocation = GetLocation (yyVals[-2+yyTop]); + current_event_field.Initializer = (Expression) yyVals[0+yyTop]; + } + +void case_315() +#line 2618 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_317() +#line 2628 "cs-parser.jay" +{ + --lexer.parsing_block; + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_318() +#line 2637 "cs-parser.jay" +{ + if (current_container.Kind == MemberKind.Interface) { + report.Error (68, lexer.Location, "`{0}': event in interface cannot have an initializer", + current_event_field.GetSignatureForError ()); + } + + if ((current_event_field.ModFlags & Modifiers.ABSTRACT) != 0) { + report.Error (74, lexer.Location, "`{0}': abstract event cannot have an initializer", + current_event_field.GetSignatureForError ()); + } + } + +void case_322() +#line 2658 "cs-parser.jay" +{ + report.Error (65, lexer.Location, "`{0}': event property must have both add and remove accessors", + current_event.GetSignatureForError ()); + } + +void case_323() +#line 2663 "cs-parser.jay" +{ + report.Error (65, lexer.Location, "`{0}': event property must have both add and remove accessors", + current_event.GetSignatureForError ()); + } + +void case_324() +#line 2668 "cs-parser.jay" +{ + report.Error (1055, GetLocation (yyVals[0+yyTop]), "An add or remove accessor expected"); + yyVal = null; + } + +void case_325() +#line 2676 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != ModifierNone) { + report.Error (1609, GetLocation (yyVals[-1+yyTop]), "Modifiers cannot be placed on event accessor declarations"); + } + + current_event.Add = new EventProperty.AddDelegateMethod (current_event, (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + current_local_parameters = current_event.Add.ParameterInfo; + + lbag.AddMember (current_event.Add, GetModifierLocations ()); + lexer.EventParsing = false; + } + +void case_326() +#line 2688 "cs-parser.jay" +{ + lexer.EventParsing = true; + + current_event.Add.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_event.Add.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_event.Add.GetSignatureForError ()); + } + + current_local_parameters = null; + } + +void case_327() +#line 2704 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != ModifierNone) { + report.Error (1609, GetLocation (yyVals[-1+yyTop]), "Modifiers cannot be placed on event accessor declarations"); + } + + current_event.Remove = new EventProperty.RemoveDelegateMethod (current_event, (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + current_local_parameters = current_event.Remove.ParameterInfo; + + lbag.AddMember (current_event.Remove, GetModifierLocations ()); + lexer.EventParsing = false; + } + +void case_328() +#line 2716 "cs-parser.jay" +{ + lexer.EventParsing = true; + + current_event.Remove.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_event.Remove.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_event.Remove.GetSignatureForError ()); + } + + current_local_parameters = null; + } + +void case_329() +#line 2732 "cs-parser.jay" +{ + report.Error (73, lexer.Location, "An add or remove accessor must have a body"); + yyVal = null; + } + +void case_331() +#line 2741 "cs-parser.jay" +{ + current_type.UnattachedAttributes = (Attributes) yyVals[-1+yyTop]; + report.Error (1519, GetLocation (yyVals[-1+yyTop]), "An attribute is missing member declaration"); + lexer.putback ('}'); + } + +void case_332() +#line 2752 "cs-parser.jay" +{ + report.Error (1519, lexer.Location, "Unexpected symbol `}' in class, struct, or interface member declaration"); + + lexer.putback ('}'); + + lexer.parsing_generic_declaration = false; + FullNamedExpression type = (FullNamedExpression) yyVals[-1+yyTop]; + current_field = new Field (current_type, type, (Modifiers) yyVals[-2+yyTop], MemberName.Null, (Attributes) yyVals[-3+yyTop]); + current_type.AddField (current_field); + lbag.AddMember (current_field, GetModifierLocations ()); + yyVal = current_field; + } + +void case_333() +#line 2772 "cs-parser.jay" +{ + if (doc_support) + enumTypeComment = Lexer.consume_doc_comment (); + } + +void case_334() +#line 2777 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + + MemberName name = (MemberName) yyVals[-3+yyTop]; + if (name.IsGeneric) { + report.Error (1675, name.Location, "Enums cannot have type parameters"); + } + + push_current_container (new Enum (current_container, (FullNamedExpression) yyVals[-2+yyTop], (Modifiers) yyVals[-5+yyTop], name, (Attributes) yyVals[-6+yyTop]), null); + if (yyVals[-2+yyTop] != null) { + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-4+yyTop]), savedLocation, GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[0+yyTop])); + } + } + +void case_335() +#line 2794 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + + /* here will be evaluated after CLOSE_BLACE is consumed.*/ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_336() +#line 2802 "cs-parser.jay" +{ + lbag.AppendToMember (current_container, GetLocation (yyVals[-1+yyTop])); + if (yyVals[0+yyTop] != null) { + lbag.AppendToMember (current_container, GetLocation (yyVals[0+yyTop])); + } + if (doc_support) + current_container.DocComment = enumTypeComment; + + --lexer.parsing_declaration; + +/* if (doc_support)*/ +/* em.DocComment = ev.DocComment;*/ + + yyVal = pop_current_class (); + } + +void case_338() +#line 2822 "cs-parser.jay" +{ + savedLocation = GetLocation (yyVals[-1+yyTop]); + yyVal = yyVals[0+yyTop]; + } + +void case_339() +#line 2827 "cs-parser.jay" +{ + Error_TypeExpected (GetLocation (yyVals[-1+yyTop])); + yyVal = null; + } + +void case_344() +#line 2845 "cs-parser.jay" +{ + lbag.AppendToMember (current_container, GetLocation (yyVals[-1+yyTop])); + yyVal = yyVals[0+yyTop]; + } + +void case_345() +#line 2853 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-1+yyTop]); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) { + em.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + yyVal = em; + } + +void case_346() +#line 2866 "cs-parser.jay" +{ + ++lexer.parsing_block; + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + } + +void case_347() +#line 2874 "cs-parser.jay" +{ + --lexer.parsing_block; + + var lt = (LocatedToken) yyVals[-3+yyTop]; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-4+yyTop]); + em.Initializer = new ConstInitializer (em, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) + em.DocComment = ConsumeStoredComment (); + + yyVal = em; + } + +void case_348() +#line 2888 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) yyVals[-1+yyTop]; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-2+yyTop]); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) { + em.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + yyVal = em; + } + +void case_351() +#line 2915 "cs-parser.jay" +{ + valid_param_mod = 0; + + ParametersCompiled p = (ParametersCompiled) yyVals[-1+yyTop]; + + Delegate del = new Delegate (current_container, (FullNamedExpression) yyVals[-5+yyTop], (Modifiers) yyVals[-7+yyTop], (MemberName) yyVals[-4+yyTop], p, (Attributes) yyVals[-8+yyTop]); + + p.CheckParameters (del); + + current_container.AddTypeContainer (del); + + current_delegate = del; + lexer.ConstraintsParsing = true; + } + +void case_353() +#line 2934 "cs-parser.jay" +{ + if (doc_support) { + current_delegate.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + if (yyVals[-2+yyTop] != null) + current_delegate.SetConstraints ((List) yyVals[-2+yyTop]); + lbag.AddMember (current_delegate, GetModifierLocations (), GetLocation (yyVals[-10+yyTop]), GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[0+yyTop])); + + yyVal = current_delegate; + + current_delegate = null; + } + +void case_355() +#line 2953 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "nullable types"); + + yyVal = ComposedTypeSpecifier.CreateNullable (GetLocation (yyVals[0+yyTop])); + } + +void case_357() +#line 2964 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-2+yyTop]; + var lt2 = (LocatedToken) yyVals[-1+yyTop]; + + yyVal = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) yyVals[0+yyTop], lt1.Location); + lbag.AddLocation (yyVal, savedLocation, GetLocation (yyVals[-1+yyTop])); + } + +void case_358() +#line 2972 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-2+yyTop]; + var lt2 = (LocatedToken) yyVals[-1+yyTop]; + var qam = new QualifiedAliasMember (lt1.Value, lt2.Value, (int) yyVals[0+yyTop], lt1.Location); + lbag.AddLocation (qam.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = qam; + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_360() +#line 2985 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_361() +#line 2991 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + var ma = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (int) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (ma.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = ma; + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_362() +#line 3002 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new SimpleName (lt.Value, (TypeArguments)yyVals[0+yyTop], lt.Location); + } + +void case_363() +#line 3007 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + var sn = new SimpleName (lt.Value, (int) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (sn.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = sn; + } + +void case_365() +#line 3021 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "generics"); + var list = locationListStack.Pop (); + list.Add (GetLocation (yyVals[-2+yyTop])); + list.Add (GetLocation (yyVals[-1+yyTop])); + lbag.AddLocation (yyVals[-1+yyTop], list); + + yyVal = yyVals[-1+yyTop];; + } + +void case_366() +#line 3032 "cs-parser.jay" +{ + Error_TypeExpected (lexer.Location); + yyVal = new TypeArguments (); + } + +void case_367() +#line 3040 "cs-parser.jay" +{ + TypeArguments type_args = new TypeArguments (); + type_args.Add ((FullNamedExpression) yyVals[0+yyTop]); + yyVal = type_args; + locationListStack.Push (new List ()); + } + +void case_368() +#line 3047 "cs-parser.jay" +{ + TypeArguments type_args = (TypeArguments) yyVals[-2+yyTop]; + type_args.Add ((FullNamedExpression) yyVals[0+yyTop]); + yyVal = type_args; + locationListStack.Peek ().Add (GetLocation (yyVals[-1+yyTop])); + } + +void case_370() +#line 3064 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new MemberName (lt.Value, (TypeParameters)yyVals[0+yyTop], lt.Location); + } + +void case_371() +#line 3073 "cs-parser.jay" +{ + MemberName mn = (MemberName)yyVals[0+yyTop]; + if (mn.TypeParameters != null) + syntax_error (mn.Location, string.Format ("Member `{0}' cannot declare type arguments", + mn.GetSignatureForError ())); + } + +void case_373() +#line 3084 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberName (lt.Value, (TypeParameters) yyVals[0+yyTop], (ATypeNameExpression) yyVals[-2+yyTop], lt.Location); + } + +void case_374() +#line 3093 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + yyVal = new MemberName (TypeDefinition.DefaultIndexerName, GetLocation (yyVals[0+yyTop])); + } + +void case_375() +#line 3098 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + yyVal = new MemberName (TypeDefinition.DefaultIndexerName, null, (ATypeNameExpression) yyVals[-1+yyTop], GetLocation (yyVals[0+yyTop])); + } + +void case_376() +#line 3106 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new SimpleName (lt.Value, (TypeArguments) yyVals[-1+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_377() +#line 3112 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-3+yyTop]; + var lt2 = (LocatedToken) yyVals[-2+yyTop]; + + yyVal = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) yyVals[-1+yyTop], lt1.Location); + lbag.AddLocation (yyVal, savedLocation, GetLocation (yyVals[0+yyTop])); + } + +void case_378() +#line 3120 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new MemberAccess ((ATypeNameExpression) yyVals[-3+yyTop], lt.Value, (TypeArguments) yyVals[-1+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_380() +#line 3130 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "generics"); + + yyVal = yyVals[-1+yyTop]; + var list = locationListStack.Pop (); + list.Add (GetLocation (yyVals[-2+yyTop])); + list.Add (GetLocation (yyVals[-1+yyTop])); + lbag.AddLocation (yyVals[-1+yyTop], list); + } + +void case_381() +#line 3144 "cs-parser.jay" +{ + var tparams = new TypeParameters (); + tparams.Add ((TypeParameter)yyVals[0+yyTop]); + yyVal = tparams; + locationListStack.Push (new List ()); + } + +void case_382() +#line 3151 "cs-parser.jay" +{ + var tparams = (TypeParameters) yyVals[-2+yyTop]; + tparams.Add ((TypeParameter)yyVals[0+yyTop]); + yyVal = tparams; + locationListStack.Peek ().Add (GetLocation (yyVals[-1+yyTop])); + } + +void case_383() +#line 3161 "cs-parser.jay" +{ + var lt = (LocatedToken)yyVals[0+yyTop]; + var variance = (VarianceDecl) yyVals[-1+yyTop]; + yyVal = new TypeParameter (new MemberName (lt.Value, lt.Location), (Attributes)yyVals[-2+yyTop], variance); + if (variance != null) + lbag.AddLocation (yyVal, savedLocation); + } + +void case_384() +#line 3169 "cs-parser.jay" +{ + if (GetTokenName (yyToken) == "type") + report.Error (81, GetLocation (yyVals[0+yyTop]), "Type parameter declaration must be an identifier not a type"); + else + Error_SyntaxError (yyToken); + + yyVal = new TypeParameter (MemberName.Null, null, null); + } + +void case_393() +#line 3213 "cs-parser.jay" +{ + report.Error (1536, GetLocation (yyVals[0+yyTop]), "Invalid parameter type `void'"); + yyVal = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[0+yyTop])); + } + +void case_396() +#line 3229 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + yyVal = new ComposedCast ((ATypeNameExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + var sn = yyVals[-1+yyTop] as SimpleName; + if (sn != null && sn.Name == "var") + yyVal = new VarExpr (sn.Location); + else + yyVal = yyVals[-1+yyTop]; + } + } + +void case_399() +#line 3249 "cs-parser.jay" +{ + Expression.Error_VoidInvalidInTheContext (GetLocation (yyVals[0+yyTop]), report); + yyVal = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[0+yyTop])); + } + +void case_400() +#line 3257 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + +void case_403() +#line 3273 "cs-parser.jay" +{ + var types = new List (2); + types.Add ((FullNamedExpression) yyVals[0+yyTop]); + yyVal = types; + } + +void case_404() +#line 3279 "cs-parser.jay" +{ + var types = (List) yyVals[-2+yyTop]; + types.Add ((FullNamedExpression) yyVals[0+yyTop]); + lbag.AddLocation (types, GetLocation (yyVals[-1+yyTop])); + yyVal = types; + } + +void case_405() +#line 3289 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is ComposedCast) { + report.Error (1521, GetLocation (yyVals[0+yyTop]), "Invalid base type `{0}'", ((ComposedCast)yyVals[0+yyTop]).GetSignatureForError ()); + } + yyVal = yyVals[0+yyTop]; + } + +void case_443() +#line 3353 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new CompletionSimpleName (MemberName.MakeName (lt.Value, null), lt.Location); + } + +void case_454() +#line 3394 "cs-parser.jay" +{ + yyVal = new ParenthesizedExpression ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_456() +#line 3406 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_457() +#line 3412 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + var ma = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (int) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (ma.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = ma; + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_458() +#line 3420 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "null propagating operator"); + + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new ConditionalMemberAccess ((Expression) yyVals[-4+yyTop], lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_459() +#line 3429 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_460() +#line 3435 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess (new BaseThis (GetLocation (yyVals[-3+yyTop])), lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_461() +#line 3441 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess (new SimpleName ("await", ((LocatedToken) yyVals[-3+yyTop]).Location), lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_462() +#line 3447 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-2+yyTop]; + var lt2 = (LocatedToken) yyVals[-1+yyTop]; + + yyVal = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) yyVals[0+yyTop], lt1.Location); + lbag.AddLocation (yyVal, savedLocation, GetLocation (yyVals[-1+yyTop])); + } + +void case_463() +#line 3455 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-2+yyTop]; + var lt2 = (LocatedToken) yyVals[-1+yyTop]; + var qam = new QualifiedAliasMember (lt1.Value, lt2.Value, (int) yyVals[0+yyTop], lt1.Location); + lbag.AddLocation (qam.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = qam; + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_465() +#line 3466 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new CompletionMemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, lt.Location); + } + +void case_467() +#line 3474 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new CompletionMemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, lt.Location); + } + +void case_468() +#line 3482 "cs-parser.jay" +{ + yyVal = new Invocation ((Expression) yyVals[-3+yyTop], (Arguments) yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_469() +#line 3487 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Invocation ((Expression) yyVals[-3+yyTop], (Arguments) yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_470() +#line 3494 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Invocation ((Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_473() +#line 3509 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] == null) { + yyVal = new CollectionOrObjectInitializers (GetLocation (yyVals[-2+yyTop])); + } else { + yyVal = new CollectionOrObjectInitializers ((List) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_474() +#line 3518 "cs-parser.jay" +{ + yyVal = new CollectionOrObjectInitializers ((List) yyVals[-2+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_477() +#line 3534 "cs-parser.jay" +{ + var a = new List (); + a.Add ((Expression) yyVals[0+yyTop]); + yyVal = a; + } + +void case_478() +#line 3540 "cs-parser.jay" +{ + var a = (List)yyVals[-2+yyTop]; + a.Add ((Expression) yyVals[0+yyTop]); + lbag.AddLocation (a, GetLocation (yyVals[-1+yyTop])); + yyVal = a; + } + +void case_479() +#line 3546 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = yyVals[-1+yyTop]; + } + +void case_480() +#line 3554 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new ElementInitializer (lt.Value, (Expression)yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_481() +#line 3560 "cs-parser.jay" +{ + var lt = (LocatedToken) Error_AwaitAsIdentifier (yyVals[-2+yyTop]); + yyVal = new ElementInitializer (lt.Value, (Expression)yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_483() +#line 3569 "cs-parser.jay" +{ + CompletionSimpleName csn = yyVals[-1+yyTop] as CompletionSimpleName; + if (csn == null) + yyVal = new CollectionElementInitializer ((Expression)yyVals[-1+yyTop]); + else + yyVal = new CompletionElementInitializer (csn.Prefix, csn.Location); + } + +void case_484() +#line 3577 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] == null) + yyVal = new CollectionElementInitializer (GetLocation (yyVals[-2+yyTop])); + else { + yyVal = new CollectionElementInitializer ((List)yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_485() +#line 3587 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation (yyVals[-4+yyTop]), "dictionary initializer"); + + yyVal = new DictionaryElementInitializer ((List)yyVals[-3+yyTop], (Expression) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_486() +#line 3595 "cs-parser.jay" +{ + report.Error (1920, GetLocation (yyVals[-1+yyTop]), "An element initializer cannot be empty"); + yyVal = new CollectionElementInitializer (GetLocation (yyVals[-1+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_491() +#line 3614 "cs-parser.jay" +{ + Arguments list = new Arguments (4); + list.Add ((Argument) yyVals[0+yyTop]); + yyVal = list; + } + +void case_492() +#line 3620 "cs-parser.jay" +{ + Arguments list = (Arguments) yyVals[-2+yyTop]; + if (list [list.Count - 1] is NamedArgument) + Error_NamedArgumentExpected ((NamedArgument) list [list.Count - 1]); + + list.Add ((Argument) yyVals[0+yyTop]); + lbag.AddLocation (list, GetLocation (yyVals[-1+yyTop])); + yyVal = list; + } + +void case_493() +#line 3630 "cs-parser.jay" +{ + Arguments list = (Arguments) yyVals[-2+yyTop]; + NamedArgument a = (NamedArgument) yyVals[0+yyTop]; + for (int i = 0; i < list.Count; ++i) { + NamedArgument na = list [i] as NamedArgument; + if (na != null && na.Name == a.Name) + report.Error (1740, na.Location, "Named argument `{0}' specified multiple times", + na.Name); + } + + list.Add (a); + lbag.AddLocation (list, GetLocation (yyVals[-1+yyTop])); + yyVal = list; + } + +void case_494() +#line 3645 "cs-parser.jay" +{ + if (lexer.putback_char == -1) + lexer.putback (')'); /* TODO: Wrong but what can I do*/ + Error_SyntaxError (yyToken); + yyVal = yyVals[-2+yyTop]; + } + +void case_495() +#line 3652 "cs-parser.jay" +{ + report.Error (839, GetLocation (yyVals[-1+yyTop]), "An argument is missing"); + yyVal = null; + } + +void case_500() +#line 3673 "cs-parser.jay" +{ + yyVal = new Argument ((Expression) yyVals[0+yyTop], Argument.AType.Ref); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_501() +#line 3678 "cs-parser.jay" +{ + yyVal = new Argument ((Expression) yyVals[0+yyTop], Argument.AType.Out); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_502() +#line 3683 "cs-parser.jay" +{ + yyVal = new Argument (new Arglist ((Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop]))); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_503() +#line 3688 "cs-parser.jay" +{ + yyVal = new Argument (new Arglist (GetLocation (yyVals[-2+yyTop]))); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_505() +#line 3700 "cs-parser.jay" +{ + yyVal = new ElementAccess ((Expression) yyVals[-3+yyTop], (Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_506() +#line 3705 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "null propagating operator"); + + yyVal = new ElementAccess ((Expression) yyVals[-4+yyTop], (Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])) { + ConditionalAccess = true + }; + + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_507() +#line 3716 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new ElementAccess ((Expression) yyVals[-3+yyTop], (Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_508() +#line 3721 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new ElementAccess ((Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-1+yyTop])); + } + +void case_509() +#line 3729 "cs-parser.jay" +{ + var list = new List (4); + list.Add ((Expression) yyVals[0+yyTop]); + yyVal = list; + } + +void case_510() +#line 3735 "cs-parser.jay" +{ + var list = (List) yyVals[-2+yyTop]; + list.Add ((Expression) yyVals[0+yyTop]); + lbag.AddLocation (list, GetLocation (yyVals[-1+yyTop])); + yyVal = list; + } + +void case_511() +#line 3745 "cs-parser.jay" +{ + Arguments args = new Arguments (4); + args.Add ((Argument) yyVals[0+yyTop]); + yyVal = args; + } + +void case_512() +#line 3751 "cs-parser.jay" +{ + Arguments args = (Arguments) yyVals[-2+yyTop]; + if (args [args.Count - 1] is NamedArgument && !(yyVals[0+yyTop] is NamedArgument)) + Error_NamedArgumentExpected ((NamedArgument) args [args.Count - 1]); + + args.Add ((Argument) yyVals[0+yyTop]); + lbag.AddLocation (args, GetLocation (yyVals[-1+yyTop])); + yyVal = args; + } + +void case_516() +#line 3779 "cs-parser.jay" +{ + yyVal = new ElementAccess (new BaseThis (GetLocation (yyVals[-3+yyTop])), (Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_517() +#line 3784 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new ElementAccess (null, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_520() +#line 3806 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-5+yyTop]), "object initializers"); + + yyVal = new NewInitialize ((FullNamedExpression) yyVals[-4+yyTop], (Arguments) yyVals[-2+yyTop], (CollectionOrObjectInitializers) yyVals[0+yyTop], GetLocation (yyVals[-5+yyTop])); + } else { + yyVal = new New ((FullNamedExpression) yyVals[-4+yyTop], (Arguments) yyVals[-2+yyTop], GetLocation (yyVals[-5+yyTop])); + } + + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_521() +#line 3819 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "collection initializers"); + + yyVal = new NewInitialize ((FullNamedExpression) yyVals[-1+yyTop], null, (CollectionOrObjectInitializers) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_522() +#line 3831 "cs-parser.jay" +{ + yyVal = new ArrayCreation ((FullNamedExpression) yyVals[-5+yyTop], (List) yyVals[-3+yyTop], + new ComposedTypeSpecifier (((List) yyVals[-3+yyTop]).Count, GetLocation (yyVals[-4+yyTop])) { + Next = (ComposedTypeSpecifier) yyVals[-1+yyTop] + }, (ArrayInitializer) yyVals[0+yyTop], GetLocation (yyVals[-6+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_523() +#line 3839 "cs-parser.jay" +{ + if (yyVals[0+yyTop] == null) + report.Error (1586, GetLocation (yyVals[-3+yyTop]), "Array creation must have array size or array initializer"); + + yyVal = new ArrayCreation ((FullNamedExpression) yyVals[-2+yyTop], (ComposedTypeSpecifier) yyVals[-1+yyTop], (ArrayInitializer) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + } + +void case_524() +#line 3846 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "implicitly typed arrays"); + + yyVal = new ImplicitlyTypedArrayCreation ((ComposedTypeSpecifier) yyVals[-1+yyTop], (ArrayInitializer) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_525() +#line 3853 "cs-parser.jay" +{ + report.Error (178, GetLocation (yyVals[-1+yyTop]), "Invalid rank specifier, expecting `,' or `]'"); + yyVal = new ArrayCreation ((FullNamedExpression) yyVals[-5+yyTop], null, GetLocation (yyVals[-6+yyTop])); + } + +void case_526() +#line 3858 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + /* It can be any of new expression, create the most common one*/ + yyVal = new New ((FullNamedExpression) yyVals[-1+yyTop], null, GetLocation (yyVals[-2+yyTop])); + } + +void case_528() +#line 3870 "cs-parser.jay" +{ + --lexer.parsing_type; + yyVal = yyVals[0+yyTop]; + } + +void case_529() +#line 3878 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "anonymous types"); + + yyVal = new NewAnonymousType ((List) yyVals[-1+yyTop], current_container, GetLocation (yyVals[-3+yyTop])); + + /* TODO: lbag comma location*/ + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_535() +#line 3905 "cs-parser.jay" +{ + var a = new List (4); + a.Add ((AnonymousTypeParameter) yyVals[0+yyTop]); + yyVal = a; + } + +void case_536() +#line 3911 "cs-parser.jay" +{ + var a = (List) yyVals[-2+yyTop]; + a.Add ((AnonymousTypeParameter) yyVals[0+yyTop]); + lbag.AddLocation (a, GetLocation (yyVals[-1+yyTop])); + + yyVal = a; + } + +void case_539() +#line 3930 "cs-parser.jay" +{ + var lt = (LocatedToken)yyVals[-2+yyTop]; + yyVal = new AnonymousTypeParameter ((Expression)yyVals[0+yyTop], lt.Value, lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_540() +#line 3936 "cs-parser.jay" +{ + var lt = (LocatedToken)yyVals[0+yyTop]; + yyVal = new AnonymousTypeParameter (new SimpleName (lt.Value, lt.Location), + lt.Value, lt.Location); + } + +void case_541() +#line 3942 "cs-parser.jay" +{ + MemberAccess ma = (MemberAccess) yyVals[0+yyTop]; + yyVal = new AnonymousTypeParameter (ma, ma.Name, ma.Location); + } + +void case_542() +#line 3947 "cs-parser.jay" +{ + report.Error (746, lexer.Location, + "Invalid anonymous type member declarator. Anonymous type members must be a member assignment, simple name or member access expression"); + yyVal = null; + } + +void case_546() +#line 3962 "cs-parser.jay" +{ + ((ComposedTypeSpecifier) yyVals[-1+yyTop]).Next = (ComposedTypeSpecifier) yyVals[0+yyTop]; + yyVal = yyVals[-1+yyTop]; + } + +void case_547() +#line 3970 "cs-parser.jay" +{ + yyVal = ComposedTypeSpecifier.CreateArrayDimension (1, GetLocation (yyVals[-1+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_548() +#line 3975 "cs-parser.jay" +{ + yyVal = ComposedTypeSpecifier.CreateArrayDimension ((int)yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_553() +#line 4005 "cs-parser.jay" +{ + var ai = new ArrayInitializer (0, GetLocation (yyVals[-1+yyTop])); + ai.VariableDeclaration = current_variable; + lbag.AddLocation (ai, GetLocation (yyVals[0+yyTop])); + yyVal = ai; + } + +void case_554() +#line 4012 "cs-parser.jay" +{ + var ai = new ArrayInitializer ((List) yyVals[-2+yyTop], GetLocation (yyVals[-3+yyTop])); + ai.VariableDeclaration = current_variable; + if (yyVals[-1+yyTop] != null) { + lbag.AddLocation (ai, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddLocation (ai, GetLocation (yyVals[0+yyTop])); + } + yyVal = ai; + } + +void case_555() +#line 4026 "cs-parser.jay" +{ + var list = new List (4); + list.Add ((Expression) yyVals[0+yyTop]); + yyVal = list; + } + +void case_556() +#line 4032 "cs-parser.jay" +{ + var list = (List) yyVals[-2+yyTop]; + list.Add ((Expression) yyVals[0+yyTop]); + lbag.AddLocation (list, GetLocation (yyVals[-1+yyTop])); + yyVal = list; + } + +void case_557() +#line 4042 "cs-parser.jay" +{ + yyVal = new TypeOf ((FullNamedExpression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_559() +#line 4051 "cs-parser.jay" +{ + Error_TypeExpected (lexer.Location); + yyVal = null; + } + +void case_560() +#line 4059 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "generics"); + + yyVal = yyVals[0+yyTop]; + } + +void case_561() +#line 4069 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (lt.Location, "namespace alias qualifier"); + savedLocation = GetLocation (yyVals[0+yyTop]); + yyVal = lt; + } + +void case_562() +#line 4080 "cs-parser.jay" +{ + yyVal = new SizeOf ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_563() +#line 4085 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new SizeOf ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_564() +#line 4095 "cs-parser.jay" +{ + yyVal = new CheckedExpr ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_565() +#line 4100 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new CheckedExpr (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_566() +#line 4109 "cs-parser.jay" +{ + yyVal = new UnCheckedExpr ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_567() +#line 4114 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new UnCheckedExpr (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_568() +#line 4123 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess (new Indirection ((Expression) yyVals[-3+yyTop], GetLocation (yyVals[-2+yyTop])), lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + } + +void case_570() +#line 4135 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + if ((ParametersCompiled) yyVals[-2+yyTop] != ParametersCompiled.Undefined) { + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop]), PopLocation (), PopLocation ()); + } else { + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop])); + } + } + +void case_572() +#line 4148 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + + if ((ParametersCompiled) yyVals[-2+yyTop] != ParametersCompiled.Undefined) { + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-3+yyTop]), PopLocation (), PopLocation ()); + } else { + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-3+yyTop])); + } + } + +void case_576() +#line 4173 "cs-parser.jay" +{ + valid_param_mod = 0; + yyVal = yyVals[-1+yyTop]; + PushLocation (GetLocation (yyVals[-1+yyTop])); + PushLocation (GetLocation (yyVals[-3+yyTop])); + } + +void case_577() +#line 4183 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "default value expression"); + + yyVal = new DefaultValueExpression ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_581() +#line 4203 "cs-parser.jay" +{ + yyVal = new Cast ((FullNamedExpression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_582() +#line 4208 "cs-parser.jay" +{ + if (!async_block) { + if (current_anonymous_method is LambdaExpression) { + report.Error (4034, GetLocation (yyVals[-1+yyTop]), + "The `await' operator can only be used when its containing lambda expression is marked with the `async' modifier"); + } else if (current_anonymous_method != null) { + report.Error (4035, GetLocation (yyVals[-1+yyTop]), + "The `await' operator can only be used when its containing anonymous method is marked with the `async' modifier"); + } else if (interactive_async != null) { + current_block.Explicit.RegisterAsyncAwait (); + interactive_async = true; + } else { + report.Error (4033, GetLocation (yyVals[-1+yyTop]), + "The `await' operator can only be used when its containing method is marked with the `async' modifier"); + } + } else { + current_block.Explicit.RegisterAsyncAwait (); + } + + yyVal = new Await ((Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + +void case_583() +#line 4230 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.LogicalNot, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_584() +#line 4236 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.OnesComplement, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_585() +#line 4242 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Cast ((FullNamedExpression) yyVals[-2+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_586() +#line 4249 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Await (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_594() +#line 4287 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.UnaryPlus, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_595() +#line 4293 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.UnaryNegation, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_596() +#line 4299 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new UnaryMutator (UnaryMutator.Mode.PreIncrement, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_597() +#line 4305 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new UnaryMutator (UnaryMutator.Mode.PreDecrement, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_598() +#line 4311 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Indirection (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_599() +#line 4317 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.AddressOf, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_601() +#line 4327 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Multiply, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_602() +#line 4332 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Division, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_603() +#line 4337 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Modulus, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_604() +#line 4342 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Multiply, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_605() +#line 4349 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Division, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_606() +#line 4356 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Modulus, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_608() +#line 4367 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Addition, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_609() +#line 4372 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Subtraction, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_610() +#line 4377 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Addition, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_611() +#line 4384 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Subtraction, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_613() +#line 4395 "cs-parser.jay" +{ + var is_expr = new Is ((Expression) yyVals[-3+yyTop], (Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + if (yyVals[0+yyTop] != null) { + if (lang_version != LanguageVersion.Experimental) + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "type pattern matching"); + + var lt = (LocatedToken) yyVals[0+yyTop]; + is_expr.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (is_expr.Variable); + } + + yyVal = is_expr; + } + +void case_614() +#line 4409 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new As ((Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-1+yyTop])); + } + +void case_615() +#line 4415 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Is ((Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-1+yyTop])); + } + +void case_616() +#line 4421 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new Is (new SimpleName (lt.Value, lt.Location), (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + +void case_617() +#line 4426 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new As (new SimpleName (lt.Value, lt.Location), (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + +void case_619() +#line 4435 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] is VarExpr) + yyVals[-1+yyTop] = new SimpleName ("var", ((VarExpr) yyVals[-1+yyTop]).Location); + + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + +void case_623() +#line 4454 "cs-parser.jay" +{ + Expression expr = (Expression) yyVals[-1+yyTop]; + if (yyVals[0+yyTop] == null) { + SimpleName sn = expr as SimpleName; + if (sn != null && sn.Name == "var") + yyVal = new VarExpr (sn.Location); + else + yyVal = yyVals[-1+yyTop]; + } else if (expr is ATypeNameExpression) { + yyVal = new ComposedCast ((ATypeNameExpression)expr, (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + Error_ExpectingTypeName (expr); + yyVal = null; + } + } + +void case_624() +#line 4470 "cs-parser.jay" +{ + ATypeNameExpression expr = yyVals[-1+yyTop] as ATypeNameExpression; + + if (expr != null) { + yyVal = new ComposedCast (expr, (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + Error_ExpectingTypeName ((Expression)yyVals[-1+yyTop]); + yyVal = expr; + } + } + +void case_628() +#line 4487 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LeftShift, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_629() +#line 4492 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.RightShift, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_630() +#line 4497 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LeftShift, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_631() +#line 4504 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.RightShift, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_633() +#line 4515 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LessThan, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_634() +#line 4520 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.GreaterThan, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_635() +#line 4525 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LessThanOrEqual, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_636() +#line 4530 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_637() +#line 4535 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LessThan, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_638() +#line 4542 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.GreaterThan, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_639() +#line 4549 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LessThanOrEqual, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_640() +#line 4556 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_642() +#line 4567 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Equality, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_643() +#line 4572 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Inequality, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_644() +#line 4577 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Equality, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_645() +#line 4584 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Inequality, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_647() +#line 4595 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.BitwiseAnd, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_648() +#line 4600 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.BitwiseAnd, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_650() +#line 4611 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.ExclusiveOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_651() +#line 4616 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.ExclusiveOr, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_653() +#line 4627 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.BitwiseOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_654() +#line 4632 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.BitwiseOr, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_656() +#line 4643 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LogicalAnd, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_657() +#line 4648 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LogicalAnd, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_659() +#line 4659 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LogicalOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_660() +#line 4664 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LogicalOr, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_662() +#line 4675 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-1+yyTop]), "null coalescing operator"); + + yyVal = new Nullable.NullCoalescingOperator ((Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_664() +#line 4687 "cs-parser.jay" +{ + yyVal = new Conditional (new BooleanExpression ((Expression) yyVals[-4+yyTop]), (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_665() +#line 4692 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Conditional (new BooleanExpression ((Expression) yyVals[-3+yyTop]), (Expression) yyVals[-1+yyTop], null, GetLocation (yyVals[-2+yyTop])); + } + +void case_666() +#line 4698 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Conditional (new BooleanExpression ((Expression) yyVals[-4+yyTop]), (Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_667() +#line 4705 "cs-parser.jay" +{ + Error_SyntaxError (Token.CLOSE_BRACE); + + yyVal = new Conditional (new BooleanExpression ((Expression) yyVals[-4+yyTop]), (Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + lexer.putback ('}'); + } + +void case_668() +#line 4716 "cs-parser.jay" +{ + yyVal = new SimpleAssign ((Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_669() +#line 4721 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Multiply, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_670() +#line 4726 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Division, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_671() +#line 4731 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Modulus, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_672() +#line 4736 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Addition, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_673() +#line 4741 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Subtraction, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_674() +#line 4746 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.LeftShift, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_675() +#line 4751 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.RightShift, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_676() +#line 4756 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.BitwiseAnd, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_677() +#line 4761 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.BitwiseOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_678() +#line 4766 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.ExclusiveOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_679() +#line 4774 "cs-parser.jay" +{ + var pars = new List (4); + pars.Add ((Parameter) yyVals[0+yyTop]); + parameterListCommas.Clear (); + yyVal = pars; + } + +void case_680() +#line 4781 "cs-parser.jay" +{ + var pars = (List) yyVals[-2+yyTop]; + Parameter p = (Parameter)yyVals[0+yyTop]; + if (pars[0].GetType () != p.GetType ()) { + report.Error (748, p.Location, "All lambda parameters must be typed either explicitly or implicitly"); + } + + pars.Add (p); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = pars; + } + +void case_681() +#line 4797 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + + yyVal = new Parameter ((FullNamedExpression) yyVals[-1+yyTop], lt.Value, (Parameter.Modifier) yyVals[-2+yyTop], null, lt.Location); + } + +void case_682() +#line 4803 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + + yyVal = new Parameter ((FullNamedExpression) yyVals[-1+yyTop], lt.Value, Parameter.Modifier.NONE, null, lt.Location); + } + +void case_683() +#line 4809 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new ImplicitLambdaParameter (lt.Value, lt.Location); + } + +void case_684() +#line 4814 "cs-parser.jay" +{ + var lt = (LocatedToken) Error_AwaitAsIdentifier (yyVals[0+yyTop]); + yyVal = new ImplicitLambdaParameter (lt.Value, lt.Location); + } + +void case_686() +#line 4822 "cs-parser.jay" +{ + var pars_list = (List) yyVals[0+yyTop]; + yyVal = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_688() +#line 4834 "cs-parser.jay" +{ + Block b = end_block (Location.Null); + b.IsCompilerGenerated = true; + b.AddStatement (new ContextualReturn ((Expression) yyVals[0+yyTop])); + yyVal = b; + } + +void case_690() +#line 4842 "cs-parser.jay" +{ + /* Handles only cases like foo = x.FirstOrDefault (l => );*/ + /* where we must restore current_variable*/ + Block b = end_block (Location.Null); + b.IsCompilerGenerated = true; + + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_692() +#line 4856 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_693() +#line 4864 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), false, lt.Location); + } + +void case_694() +#line 4870 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_695() +#line 4875 "cs-parser.jay" +{ + var lt = (LocatedToken) Error_AwaitAsIdentifier (yyVals[-1+yyTop]); + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), false, lt.Location); + } + +void case_696() +#line 4881 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_697() +#line 4886 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), true, lt.Location); + } + +void case_698() +#line 4892 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_700() +#line 4901 "cs-parser.jay" +{ + valid_param_mod = 0; + start_anonymous (true, (ParametersCompiled) yyVals[-2+yyTop], false, GetLocation (yyVals[-4+yyTop])); + } + +void case_701() +#line 4906 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_703() +#line 4915 "cs-parser.jay" +{ + valid_param_mod = 0; + start_anonymous (true, (ParametersCompiled) yyVals[-2+yyTop], true, GetLocation (yyVals[-5+yyTop])); + } + +void case_704() +#line 4920 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_711() +#line 4943 "cs-parser.jay" +{ + yyVal = new RefValueExpr ((Expression) yyVals[-3+yyTop], (FullNamedExpression) yyVals[-1+yyTop], GetLocation (yyVals[-5+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_712() +#line 4948 "cs-parser.jay" +{ + yyVal = new RefTypeExpr ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_713() +#line 4953 "cs-parser.jay" +{ + yyVal = new MakeRefExpr ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_718() +#line 4980 "cs-parser.jay" +{ + yyVal = yyVals[-1+yyTop]; + + /* Cannot use opt_formal_parameter_list because it can be shared instance for empty parameters*/ + lbag.AppendToMember (current_container, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "primary constructor"); + } + +void case_723() +#line 5009 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_type.PrimaryConstructorBaseArgumentsStart = GetLocation (yyVals[0+yyTop]); + } + +void case_724() +#line 5014 "cs-parser.jay" +{ + lbag.AppendToMember (current_container, GetLocation (yyVals[0+yyTop])); + current_type.PrimaryConstructorBaseArguments = (Arguments) yyVals[-1+yyTop]; + --lexer.parsing_block; + + yyVal = yyVals[-5+yyTop]; + } + +void case_726() +#line 5034 "cs-parser.jay" +{ + lexer.ConstraintsParsing = true; + + Class c = new Class (current_container, (MemberName) yyVals[0+yyTop], (Modifiers) yyVals[-4+yyTop], (Attributes) yyVals[-5+yyTop]); + if (((c.ModFlags & Modifiers.STATIC) != 0) && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (c.Location, "static classes"); + } + + push_current_container (c, yyVals[-3+yyTop]); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-2+yyTop])); + valid_param_mod = ParameterModifierType.PrimaryConstructor; + } + +void case_727() +#line 5048 "cs-parser.jay" +{ + valid_param_mod = 0; + lexer.ConstraintsParsing = false; + + if (yyVals[-1+yyTop] != null) + current_type.PrimaryConstructorParameters = (ParametersCompiled) yyVals[-1+yyTop]; + + if (yyVals[0+yyTop] != null) + current_container.SetConstraints ((List) yyVals[0+yyTop]); + + if (doc_support) { + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.parsing_modifiers = true; + } + +void case_728() +#line 5066 "cs-parser.jay" +{ + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_729() +#line 5072 "cs-parser.jay" +{ + if (yyVals[0+yyTop] == null) { + lbag.AppendToMember (current_container, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } else { + lbag.AppendToMember (current_container, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + yyVal = pop_current_class (); + } + +void case_732() +#line 5091 "cs-parser.jay" +{ + mod_locations = null; + yyVal = ModifierNone; + lexer.parsing_modifiers = false; + } + +void case_735() +#line 5105 "cs-parser.jay" +{ + var m1 = (Modifiers) yyVals[-1+yyTop]; + var m2 = (Modifiers) yyVals[0+yyTop]; + + if ((m1 & m2) != 0) { + report.Error (1004, lexer.Location - ModifiersExtensions.Name (m2).Length, + "Duplicate `{0}' modifier", ModifiersExtensions.Name (m2)); + } else if ((m2 & Modifiers.AccessibilityMask) != 0 && (m1 & Modifiers.AccessibilityMask) != 0 && + ((m2 | m1 & Modifiers.AccessibilityMask) != (Modifiers.PROTECTED | Modifiers.INTERNAL))) { + report.Error (107, lexer.Location - ModifiersExtensions.Name (m2).Length, + "More than one protection modifier specified"); + } + + yyVal = m1 | m2; + } + +void case_736() +#line 5124 "cs-parser.jay" +{ + yyVal = Modifiers.NEW; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + + if (current_container.Kind == MemberKind.Namespace) + report.Error (1530, GetLocation (yyVals[0+yyTop]), "Keyword `new' is not allowed on namespace elements"); + } + +void case_737() +#line 5132 "cs-parser.jay" +{ + yyVal = Modifiers.PUBLIC; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_738() +#line 5137 "cs-parser.jay" +{ + yyVal = Modifiers.PROTECTED; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_739() +#line 5142 "cs-parser.jay" +{ + yyVal = Modifiers.INTERNAL; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_740() +#line 5147 "cs-parser.jay" +{ + yyVal = Modifiers.PRIVATE; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_741() +#line 5152 "cs-parser.jay" +{ + yyVal = Modifiers.ABSTRACT; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_742() +#line 5157 "cs-parser.jay" +{ + yyVal = Modifiers.SEALED; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_743() +#line 5162 "cs-parser.jay" +{ + yyVal = Modifiers.STATIC; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_744() +#line 5167 "cs-parser.jay" +{ + yyVal = Modifiers.READONLY; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_745() +#line 5172 "cs-parser.jay" +{ + yyVal = Modifiers.VIRTUAL; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_746() +#line 5177 "cs-parser.jay" +{ + yyVal = Modifiers.OVERRIDE; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_747() +#line 5182 "cs-parser.jay" +{ + yyVal = Modifiers.EXTERN; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_748() +#line 5187 "cs-parser.jay" +{ + yyVal = Modifiers.VOLATILE; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_749() +#line 5192 "cs-parser.jay" +{ + yyVal = Modifiers.UNSAFE; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + if (!settings.Unsafe) + Error_UnsafeCodeNotAllowed (GetLocation (yyVals[0+yyTop])); + } + +void case_750() +#line 5199 "cs-parser.jay" +{ + yyVal = Modifiers.ASYNC; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_753() +#line 5212 "cs-parser.jay" +{ + current_type.SetBaseTypes ((List) yyVals[0+yyTop]); + lbag.AppendToMember (current_type, GetLocation (yyVals[-1+yyTop])); + } + +void case_754() +#line 5217 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + current_type.SetBaseTypes ((List) yyVals[-1+yyTop]); + } + +void case_757() +#line 5234 "cs-parser.jay" +{ + var constraints = new List (1); + constraints.Add ((Constraints) yyVals[0+yyTop]); + yyVal = constraints; + } + +void case_758() +#line 5240 "cs-parser.jay" +{ + var constraints = (List) yyVals[-1+yyTop]; + Constraints new_constraint = (Constraints)yyVals[0+yyTop]; + + foreach (Constraints c in constraints) { + if (new_constraint.TypeParameter.Value == c.TypeParameter.Value) { + report.Error (409, new_constraint.Location, + "A constraint clause has already been specified for type parameter `{0}'", + new_constraint.TypeParameter.Value); + } + } + + constraints.Add (new_constraint); + yyVal = constraints; + } + +void case_759() +#line 5259 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new Constraints (new SimpleMemberName (lt.Value, lt.Location), (List) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_760() +#line 5265 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new Constraints (new SimpleMemberName (lt.Value, lt.Location), null, GetLocation (yyVals[-2+yyTop])); + } + +void case_761() +#line 5275 "cs-parser.jay" +{ + var constraints = new List (1); + constraints.Add ((FullNamedExpression) yyVals[0+yyTop]); + yyVal = constraints; + } + +void case_762() +#line 5281 "cs-parser.jay" +{ + var constraints = (List) yyVals[-2+yyTop]; + var prev = constraints [constraints.Count - 1] as SpecialContraintExpr; + if (prev != null && (prev.Constraint & SpecialConstraint.Constructor) != 0) { + report.Error (401, GetLocation (yyVals[-1+yyTop]), "The `new()' constraint must be the last constraint specified"); + } + + prev = yyVals[0+yyTop] as SpecialContraintExpr; + if (prev != null) { + if ((prev.Constraint & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) { + report.Error (449, prev.Location, "The `class' or `struct' constraint must be the first constraint specified"); + } else { + prev = constraints [0] as SpecialContraintExpr; + if (prev != null && (prev.Constraint & SpecialConstraint.Struct) != 0) { + report.Error (451, GetLocation (yyVals[0+yyTop]), "The `new()' constraint cannot be used with the `struct' constraint"); + } + } + } + + constraints.Add ((FullNamedExpression) yyVals[0+yyTop]); + lbag.AddLocation (constraints, GetLocation (yyVals[-1+yyTop])); + yyVal = constraints; + } + +void case_763() +#line 5308 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is ComposedCast) + report.Error (706, GetLocation (yyVals[0+yyTop]), "Invalid constraint type `{0}'", ((ComposedCast)yyVals[0+yyTop]).GetSignatureForError ()); + + yyVal = yyVals[0+yyTop]; + } + +void case_764() +#line 5315 "cs-parser.jay" +{ + yyVal = new SpecialContraintExpr (SpecialConstraint.Constructor, GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_768() +#line 5335 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.V_3) + FeatureIsNotAvailable (lexer.Location, "generic type variance"); + + yyVal = yyVals[0+yyTop]; + } + +void case_769() +#line 5345 "cs-parser.jay" +{ + yyVal = new VarianceDecl (Variance.Covariant, GetLocation (yyVals[0+yyTop])); + savedLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_770() +#line 5350 "cs-parser.jay" +{ + yyVal = new VarianceDecl (Variance.Contravariant, GetLocation (yyVals[0+yyTop])); + savedLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_771() +#line 5371 "cs-parser.jay" +{ + ++lexer.parsing_block; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_773() +#line 5383 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_774() +#line 5388 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = end_block (lexer.Location); + } + +void case_775() +#line 5397 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_block.StartLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_776() +#line 5402 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_777() +#line 5406 "cs-parser.jay" +{ + report.Error (1525, GetLocation (yyVals[0+yyTop]), "Unexpected symbol '}', expected '{'"); + lexer.putback ('}'); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_778() +#line 5415 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_block.StartLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_779() +#line 5420 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_787() +#line 5448 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + var lt =(LocatedToken) yyVals[-1+yyTop]; + var sn = new SimpleName (lt.Value, lt.Location); + current_block.AddStatement(new StatementErrorExpression (sn)); + yyVal = null; + } + +void case_788() +#line 5457 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_821() +#line 5521 "cs-parser.jay" +{ + report.Error (1023, GetLocation (yyVals[0+yyTop]), "An embedded statement may not be a declaration or labeled statement"); + yyVal = null; + } + +void case_822() +#line 5526 "cs-parser.jay" +{ + report.Error (1023, GetLocation (yyVals[0+yyTop]), "An embedded statement may not be a declaration or labeled statement"); + yyVal = null; + } + +void case_823() +#line 5531 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new EmptyStatement (GetLocation (yyVals[0+yyTop])); + } + +void case_824() +#line 5539 "cs-parser.jay" +{ + /* Uses lexer.Location because semicolon location is not kept in quick mode*/ + yyVal = new EmptyStatement (lexer.Location); + } + +void case_825() +#line 5547 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + LabeledStatement labeled = new LabeledStatement (lt.Value, current_block, lt.Location); + lbag.AddLocation (labeled, GetLocation (yyVals[0+yyTop])); + current_block.AddLabel (labeled); + current_block.AddStatement (labeled); + } + +void case_828() +#line 5560 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] is VarExpr) + yyVals[-1+yyTop] = new SimpleName ("var", ((VarExpr) yyVals[-1+yyTop]).Location); + + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + +void case_829() +#line 5576 "cs-parser.jay" +{ + /* Ok, the above "primary_expression" is there to get rid of*/ + /* both reduce/reduce and shift/reduces in the grammar, it should*/ + /* really just be "type_name". If you use type_name, a reduce/reduce*/ + /* creeps up. If you use namespace_or_type_name (which is all we need*/ + /* really) two shift/reduces appear.*/ + /* */ + + /* So the super-trick is that primary_expression*/ + /* can only be either a SimpleName or a MemberAccess. */ + /* The MemberAccess case arises when you have a fully qualified type-name like :*/ + /* Foo.Bar.Blah i;*/ + /* SimpleName is when you have*/ + /* Blah i;*/ + + Expression expr = (Expression) yyVals[-1+yyTop]; + if (yyVals[0+yyTop] == null) { + SimpleName sn = expr as SimpleName; + if (sn != null && sn.Name == "var") + yyVal = new VarExpr (sn.Location); + else + yyVal = yyVals[-1+yyTop]; + } else if (expr is ATypeNameExpression) { + yyVal = new ComposedCast ((ATypeNameExpression)expr, (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + Error_ExpectingTypeName (expr); + yyVal = null; + } + } + +void case_830() +#line 5606 "cs-parser.jay" +{ + ATypeNameExpression expr = yyVals[-1+yyTop] as ATypeNameExpression; + + if (expr != null) { + yyVal = new ComposedCast (expr, (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + Error_ExpectingTypeName ((Expression)yyVals[-1+yyTop]); + yyVal = expr; + } + } + +void case_834() +#line 5623 "cs-parser.jay" +{ + ((ComposedTypeSpecifier) yyVals[-1+yyTop]).Next = (ComposedTypeSpecifier) yyVals[0+yyTop]; + yyVal = yyVals[-1+yyTop]; + } + +void case_838() +#line 5646 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_839() +#line 5653 "cs-parser.jay" +{ + yyVal = current_variable; + current_variable = null; + if (yyVals[-2+yyTop] != null) + lbag.AddLocation (yyVal, PopLocation (), GetLocation (yyVals[0+yyTop])); + else + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_840() +#line 5662 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.Constant, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockConstant ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_841() +#line 5669 "cs-parser.jay" +{ + if (current_variable.Initializer != null) { + lbag.AddLocation (current_variable, GetLocation (yyVals[-6+yyTop]), savedLocation, GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddLocation (current_variable, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[0+yyTop])); + } + yyVal = current_variable;; + current_variable = null; + } + +void case_843() +#line 5682 "cs-parser.jay" +{ + /* Redundant, but wont regress*/ + report.Error (1525, lexer.Location, "Unexpected symbol }"); + lexer.putback ('}'); + yyVal = yyVals[0+yyTop]; + } + +void case_845() +#line 5693 "cs-parser.jay" +{ + current_variable.Initializer = (Expression) yyVals[0+yyTop]; + PushLocation (GetLocation (yyVals[-1+yyTop])); + yyVal = current_variable; + } + +void case_846() +#line 5699 "cs-parser.jay" +{ + if (yyToken == Token.OPEN_BRACKET_EXPR) { + report.Error (650, lexer.Location, + "Syntax error, bad array declarator. To declare a managed array the rank specifier precedes the variable's identifier. To declare a fixed size buffer field, use the fixed keyword before the field type"); + } else { + Error_SyntaxError (yyToken); + } + } + +void case_850() +#line 5717 "cs-parser.jay" +{ + foreach (var d in current_variable.Declarators) { + if (d.Initializer == null) + Error_MissingInitializer (d.Variable.Location); + } + } + +void case_853() +#line 5732 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_variable.Variable, lt.Value, lt.Location); + var d = new BlockVariableDeclarator (li, null); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation (yyVals[-1+yyTop])); + } + +void case_854() +#line 5741 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + var li = new LocalVariable (current_variable.Variable, lt.Value, lt.Location); + var d = new BlockVariableDeclarator (li, (Expression) yyVals[0+yyTop]); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_856() +#line 5757 "cs-parser.jay" +{ + savedLocation = GetLocation (yyVals[-1+yyTop]); + current_variable.Initializer = (Expression) yyVals[0+yyTop]; + } + +void case_861() +#line 5775 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.Constant, lt.Location); + var d = new BlockVariableDeclarator (li, (Expression) yyVals[0+yyTop]); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_863() +#line 5788 "cs-parser.jay" +{ + yyVal = new StackAlloc ((Expression) yyVals[-3+yyTop], (Expression) yyVals[-1+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_864() +#line 5793 "cs-parser.jay" +{ + report.Error (1575, GetLocation (yyVals[-1+yyTop]), "A stackalloc expression requires [] after type"); + yyVal = new StackAlloc ((Expression) yyVals[0+yyTop], null, GetLocation (yyVals[-1+yyTop])); + } + +void case_865() +#line 5801 "cs-parser.jay" +{ + yyVal = yyVals[-1+yyTop]; + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_867() +#line 5807 "cs-parser.jay" +{ + yyVal = yyVals[-1+yyTop]; + report.Error (1002, GetLocation (yyVals[0+yyTop]), "; expected"); + lexer.putback ('}'); + } + +void case_870() +#line 5825 "cs-parser.jay" +{ + ExpressionStatement s = yyVals[0+yyTop] as ExpressionStatement; + if (s == null) { + var expr = yyVals[0+yyTop] as Expression; + yyVal = new StatementErrorExpression (expr); + } else { + yyVal = new StatementExpression (s); + } + } + +void case_871() +#line 5838 "cs-parser.jay" +{ + Expression expr = (Expression) yyVals[0+yyTop]; + yyVal = new StatementExpression (new OptionalAssign (expr, lexer.Location)); + } + +void case_872() +#line 5843 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new EmptyStatement (GetLocation (yyVals[0+yyTop])); + } + +void case_875() +#line 5857 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + yyVal = new If ((BooleanExpression) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_876() +#line 5866 "cs-parser.jay" +{ + yyVal = new If ((BooleanExpression) yyVals[-4+yyTop], (Statement) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-6+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + + if (yyVals[-2+yyTop] is EmptyStatement) + Warning_EmptyStatement (GetLocation (yyVals[-2+yyTop])); + if (yyVals[0+yyTop] is EmptyStatement) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + } + +void case_877() +#line 5876 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new If ((BooleanExpression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_879() +#line 5890 "cs-parser.jay" +{ + yyVal = new Switch ((Expression) yyVals[-5+yyTop], (ExplicitBlock) current_block.Explicit, GetLocation (yyVals[-7+yyTop])); + end_block (GetLocation (yyVals[0+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_880() +#line 5896 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Switch ((Expression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_887() +#line 5927 "cs-parser.jay" +{ + var label = (SwitchLabel) yyVals[0+yyTop]; + label.SectionStart = true; + current_block.AddStatement (label); + } + +void case_889() +#line 5940 "cs-parser.jay" +{ + yyVal = new SwitchLabel ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_890() +#line 5945 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new SwitchLabel ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_896() +#line 5964 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + yyVal = new While ((BooleanExpression) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_897() +#line 5972 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new While ((BooleanExpression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_898() +#line 5982 "cs-parser.jay" +{ + yyVal = new Do ((Statement) yyVals[-5+yyTop], (BooleanExpression) yyVals[-2+yyTop], GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_899() +#line 5987 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Do ((Statement) yyVals[-1+yyTop], null, GetLocation (yyVals[-2+yyTop]), Location.Null); + } + +void case_900() +#line 5992 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Do ((Statement) yyVals[-4+yyTop], (BooleanExpression) yyVals[-1+yyTop], GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_901() +#line 6002 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[0+yyTop])); + current_block.IsCompilerGenerated = true; + For f = new For (GetLocation (yyVals[-1+yyTop])); + current_block.AddStatement (f); + lbag.AddStatement (f, current_block.StartLocation); + yyVal = f; + } + +void case_903() +#line 6019 "cs-parser.jay" +{ + For f = (For) yyVals[-2+yyTop]; + f.Initializer = (Statement) yyVals[-1+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[0+yyTop])); + yyVal = f; + } + +void case_905() +#line 6029 "cs-parser.jay" +{ + report.Error (1525, GetLocation (yyVals[0+yyTop]), "Unexpected symbol ')', expected ';'"); + For f = (For) yyVals[-2+yyTop]; + f.Initializer = (Statement) yyVals[-1+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[0+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_906() +#line 6040 "cs-parser.jay" +{ + For f = (For) yyVals[-2+yyTop]; + f.Condition = (BooleanExpression) yyVals[-1+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[0+yyTop])); + yyVal = f; + } + +void case_908() +#line 6051 "cs-parser.jay" +{ + report.Error (1525, GetLocation (yyVals[0+yyTop]), "Unexpected symbol ')', expected ';'"); + For f = (For) yyVals[-2+yyTop]; + f.Condition = (BooleanExpression) yyVals[-1+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[0+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_909() +#line 6063 "cs-parser.jay" +{ + For f = (For) yyVals[-3+yyTop]; + f.Iterator = (Statement) yyVals[-2+yyTop]; + + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + f.Statement = (Statement) yyVals[0+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[-1+yyTop])); + + yyVal = end_block (GetLocation (yyVals[-1+yyTop])); + } + +void case_910() +#line 6076 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = end_block (current_block.StartLocation); + } + +void case_913() +#line 6089 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_914() +#line 6096 "cs-parser.jay" +{ + yyVal = current_variable; + if (yyVals[-1+yyTop] != null) + lbag.AddLocation (current_variable, PopLocation ()); + + current_variable = null; + } + +void case_922() +#line 6123 "cs-parser.jay" +{ + var sl = yyVals[-2+yyTop] as StatementList; + if (sl == null) { + sl = new StatementList ((Statement) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop]); + lbag.AddStatement (sl, GetLocation (yyVals[-1+yyTop])); + } else { + sl.Add ((Statement) yyVals[0+yyTop]); + lbag.AddLocation (sl, GetLocation (yyVals[-1+yyTop])); + + } + + yyVal = sl; + } + +void case_923() +#line 6140 "cs-parser.jay" +{ + report.Error (230, GetLocation (yyVals[-3+yyTop]), "Type and identifier are both required in a foreach statement"); + + start_block (GetLocation (yyVals[-2+yyTop])); + current_block.IsCompilerGenerated = true; + + Foreach f = new Foreach ((Expression) yyVals[-1+yyTop], null, null, null, null, GetLocation (yyVals[-3+yyTop])); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation (yyVals[-2+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_924() +#line 6153 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + start_block (GetLocation (yyVals[-3+yyTop])); + current_block.IsCompilerGenerated = true; + + var lt = (LocatedToken) yyVals[-1+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + + Foreach f = new Foreach ((Expression) yyVals[-2+yyTop], li, null, null, null, GetLocation (yyVals[-4+yyTop])); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation (yyVals[-3+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_925() +#line 6170 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-5+yyTop])); + current_block.IsCompilerGenerated = true; + + var lt = (LocatedToken) yyVals[-3+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + yyVal = li; + } + +void case_926() +#line 6180 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + Foreach f = new Foreach ((Expression) yyVals[-6+yyTop], (LocalVariable) yyVals[-1+yyTop], (Expression) yyVals[-3+yyTop], (Statement) yyVals[0+yyTop], current_block, GetLocation (yyVals[-8+yyTop])); + lbag.AddStatement (f, GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + end_block (GetLocation (yyVals[-2+yyTop])); + + yyVal = f; + } + +void case_927() +#line 6191 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-3+yyTop])); + current_block.IsCompilerGenerated = true; + var lt = yyVals[-1+yyTop] as LocatedToken; + var li = lt != null ? new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location) : null; + + Foreach f = new Foreach ((Expression) yyVals[-2+yyTop], li, null, null, null, GetLocation (yyVals[-4+yyTop])); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation (yyVals[-3+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_928() +#line 6204 "cs-parser.jay" +{ + Foreach f = new Foreach ((Expression) yyVals[-1+yyTop], null, null, null, null, GetLocation (yyVals[-3+yyTop])); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation (yyVals[-2+yyTop])); + yyVal = f; + } + +void case_935() +#line 6224 "cs-parser.jay" +{ + yyVal = new Break (GetLocation (yyVals[-1+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_936() +#line 6232 "cs-parser.jay" +{ + yyVal = new Continue (GetLocation (yyVals[-1+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_937() +#line 6237 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Continue (GetLocation (yyVals[-1+yyTop])); + } + +void case_938() +#line 6245 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new Goto (lt.Value, GetLocation (yyVals[-2+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_939() +#line 6251 "cs-parser.jay" +{ + yyVal = new GotoCase ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_940() +#line 6256 "cs-parser.jay" +{ + yyVal = new GotoDefault (GetLocation (yyVals[-2+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_941() +#line 6264 "cs-parser.jay" +{ + yyVal = new Return ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_942() +#line 6269 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Return ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_943() +#line 6274 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Return (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_944() +#line 6282 "cs-parser.jay" +{ + yyVal = new Throw ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_945() +#line 6287 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Throw ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_946() +#line 6292 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Throw (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_947() +#line 6300 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if (yyVals[-1+yyTop] == null) { + report.Error (1627, GetLocation (yyVals[0+yyTop]), "Expression expected after yield return"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.Explicit.RegisterIteratorYield (); + yyVal = new Yield ((Expression) yyVals[-1+yyTop], lt.Location); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_948() +#line 6316 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) yyVals[-3+yyTop]; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if (yyVals[-1+yyTop] == null) { + report.Error (1627, GetLocation (yyVals[0+yyTop]), "Expression expected after yield return"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.Explicit.RegisterIteratorYield (); + yyVal = new Yield ((Expression) yyVals[-1+yyTop], lt.Location); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_949() +#line 6334 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.ParametersBlock.TopBlock.IsIterator = true; + yyVal = new YieldBreak (lt.Location); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_953() +#line 6360 "cs-parser.jay" +{ + yyVal = new TryFinally ((Statement) yyVals[-2+yyTop], (ExplicitBlock) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_954() +#line 6365 "cs-parser.jay" +{ + yyVal = new TryFinally (new TryCatch ((Block) yyVals[-3+yyTop], (List) yyVals[-2+yyTop], GetLocation (yyVals[-4+yyTop]), true), (ExplicitBlock) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_955() +#line 6370 "cs-parser.jay" +{ + Error_SyntaxError (1524, yyToken); + yyVal = new TryCatch ((Block) yyVals[-1+yyTop], null, GetLocation (yyVals[-2+yyTop]), false); + } + +void case_956() +#line 6378 "cs-parser.jay" +{ + var l = new List (2); + + l.Add ((Catch) yyVals[0+yyTop]); + yyVal = l; + } + +void case_957() +#line 6385 "cs-parser.jay" +{ + var l = (List) yyVals[-1+yyTop]; + + Catch c = (Catch) yyVals[0+yyTop]; + var prev_catch = l [l.Count - 1]; + if (prev_catch.IsGeneral && prev_catch.Filter == null) { + report.Error (1017, c.loc, "Try statement already has an empty catch block"); + } + + l.Add (c); + yyVal = l; + } + +void case_960() +#line 6406 "cs-parser.jay" +{ + var c = new Catch ((ExplicitBlock) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + c.Filter = (CatchFilterExpression) yyVals[-1+yyTop]; + yyVal = c; + } + +void case_961() +#line 6412 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-3+yyTop])); + var c = new Catch ((ExplicitBlock) current_block, GetLocation (yyVals[-4+yyTop])); + c.TypeExpression = (FullNamedExpression) yyVals[-2+yyTop]; + + if (yyVals[-1+yyTop] != null) { + var lt = (LocatedToken) yyVals[-1+yyTop]; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (c.Variable); + } + + lbag.AddLocation (c, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + yyVal = c; + } + +void case_962() +#line 6427 "cs-parser.jay" +{ + ((Catch) yyVals[-2+yyTop]).Filter = (CatchFilterExpression) yyVals[-1+yyTop]; + yyVal = yyVals[-2+yyTop]; + } + +void case_963() +#line 6432 "cs-parser.jay" +{ + if (yyToken == Token.CLOSE_PARENS) { + report.Error (1015, lexer.Location, + "A type that derives from `System.Exception', `object', or `string' expected"); + } else { + Error_SyntaxError (yyToken); + } + + yyVal = new Catch (null, GetLocation (yyVals[-2+yyTop])); + } + +void case_964() +#line 6443 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + /* Required otherwise missing block could not be detected because*/ + /* start_block is run early*/ + var c = new Catch (null, GetLocation (yyVals[-5+yyTop])); + c.TypeExpression = (FullNamedExpression) yyVals[-3+yyTop]; + + if (yyVals[-2+yyTop] != null) { + var lt = (LocatedToken) yyVals[-2+yyTop]; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + } + + if (yyVals[-2+yyTop] != null) { + var lt = (LocatedToken) yyVals[-2+yyTop]; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + } + + lbag.AddLocation (c, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-1+yyTop])); + + yyVal = c; + } + +void case_966() +#line 6470 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.V_5) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "exception filter"); + + yyVal = new CatchFilterExpression ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_969() +#line 6495 "cs-parser.jay" +{ + if (!settings.Unsafe) + Error_UnsafeCodeNotAllowed (GetLocation (yyVals[0+yyTop])); + } + +void case_971() +#line 6505 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + yyVal = new Lock ((Expression) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_972() +#line 6513 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Lock ((Expression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_973() +#line 6523 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-2+yyTop])); + + current_block.IsCompilerGenerated = true; + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.FixedVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + current_variable = new Fixed.VariableDeclaration ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_974() +#line 6533 "cs-parser.jay" +{ + yyVal = current_variable; + current_variable = null; + } + +void case_975() +#line 6538 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + Fixed f = new Fixed ((Fixed.VariableDeclaration) yyVals[-1+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-9+yyTop])); + current_block.AddStatement (f); + lbag.AddStatement (f, GetLocation (yyVals[-8+yyTop]), GetLocation (yyVals[-2+yyTop])); + yyVal = end_block (GetLocation (yyVals[-2+yyTop])); + } + +void case_976() +#line 6551 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-2+yyTop])); + + current_block.IsCompilerGenerated = true; + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.UsingVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + current_variable = new Using.VariableDeclaration ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_977() +#line 6561 "cs-parser.jay" +{ + yyVal = current_variable; + current_variable = null; + } + +void case_978() +#line 6566 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + Using u = new Using ((Using.VariableDeclaration) yyVals[-1+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-8+yyTop])); + lbag.AddStatement (u, GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-2+yyTop])); + current_block.AddStatement (u); + yyVal = end_block (GetLocation (yyVals[-2+yyTop])); + } + +void case_979() +#line 6576 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + yyVal = new Using ((Expression) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_980() +#line 6584 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Using ((Expression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_982() +#line 6595 "cs-parser.jay" +{ + /* It has to be here for the parent to safely restore artificial block*/ + Error_SyntaxError (yyToken); + } + +void case_984() +#line 6607 "cs-parser.jay" +{ + current_variable.Initializer = (Expression) yyVals[0+yyTop]; + lbag.AddLocation (current_variable, GetLocation (yyVals[-1+yyTop])); + yyVal = current_variable; + } + +void case_985() +#line 6619 "cs-parser.jay" +{ + lexer.query_parsing = false; + + Linq.AQueryClause from = yyVals[-1+yyTop] as Linq.AQueryClause; + + from.Tail.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = from; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_986() +#line 6631 "cs-parser.jay" +{ + Linq.AQueryClause from = yyVals[-1+yyTop] as Linq.AQueryClause; + + from.Tail.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = from; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_987() +#line 6642 "cs-parser.jay" +{ + lexer.query_parsing = false; + yyVal = yyVals[-1+yyTop]; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_988() +#line 6649 "cs-parser.jay" +{ + yyVal = yyVals[-1+yyTop]; + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_989() +#line 6658 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], rv, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (clause, GetLocation (yyVals[-1+yyTop])); + yyVal = new Linq.QueryExpression (clause); + } + +void case_990() +#line 6668 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], rv, GetLocation (yyVals[-4+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-3+yyTop] + }; + lbag.AddLocation (clause, GetLocation (yyVals[-1+yyTop])); + yyVal = new Linq.QueryExpression (clause); + } + +void case_991() +#line 6683 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], rv, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (clause, GetLocation (yyVals[-1+yyTop])); + yyVal = new Linq.QueryExpression (clause); + } + +void case_992() +#line 6693 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], rv, GetLocation (yyVals[-4+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-3+yyTop] + }; + lbag.AddLocation (clause, GetLocation (yyVals[-1+yyTop])); + yyVal = new Linq.QueryExpression (clause); + } + +void case_994() +#line 6712 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + yyVal = new Linq.SelectMany ((Linq.QueryBlock)current_block, sn, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_996() +#line 6727 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + + yyVal = new Linq.SelectMany ((Linq.QueryBlock)current_block, sn, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-5+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-4+yyTop] + }; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_997() +#line 6746 "cs-parser.jay" +{ + Linq.AQueryClause head = (Linq.AQueryClause)yyVals[-1+yyTop]; + + if (yyVals[0+yyTop] != null) + head.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + + if (yyVals[-2+yyTop] != null) { + Linq.AQueryClause clause = (Linq.AQueryClause)yyVals[-2+yyTop]; + clause.Tail.Next = head; + head = clause; + } + + yyVal = head; + } + +void case_998() +#line 6761 "cs-parser.jay" +{ + Linq.AQueryClause head = (Linq.AQueryClause)yyVals[0+yyTop]; + + if (yyVals[-1+yyTop] != null) { + Linq.AQueryClause clause = (Linq.AQueryClause)yyVals[-1+yyTop]; + clause.Tail.Next = head; + head = clause; + } + + yyVal = head; + } + +void case_1000() +#line 6774 "cs-parser.jay" +{ + report.Error (742, GetLocation (yyVals[0+yyTop]), "Unexpected symbol `{0}'. A query body must end with select or group clause", GetSymbolName (yyToken)); + yyVal = yyVals[-1+yyTop]; + } + +void case_1001() +#line 6779 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_1003() +#line 6791 "cs-parser.jay" +{ + yyVal = new Linq.Select ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_1004() +#line 6798 "cs-parser.jay" +{ + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock)current_block); + } + +void case_1005() +#line 6806 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + +void case_1006() +#line 6813 "cs-parser.jay" +{ + var obj = (object[]) yyVals[0+yyTop]; + + yyVal = new Linq.GroupBy ((Linq.QueryBlock)current_block, (Expression)yyVals[-2+yyTop], linq_clause_blocks.Pop (), (Expression)obj[0], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, (Location) obj[1]); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_1008() +#line 6830 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new object[2] { null, Location.Null }; + } + +void case_1010() +#line 6839 "cs-parser.jay" +{ + ((Linq.AQueryClause)yyVals[-1+yyTop]).Tail.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = yyVals[-1+yyTop]; + } + +void case_1017() +#line 6859 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + yyVal = new Linq.Let ((Linq.QueryBlock) current_block, sn, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + } + +void case_1019() +#line 6878 "cs-parser.jay" +{ + yyVal = new Linq.Where ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_1020() +#line 6888 "cs-parser.jay" +{ + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1021() +#line 6896 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1022() +#line 6904 "cs-parser.jay" +{ + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + +void case_1023() +#line 6912 "cs-parser.jay" +{ + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + current_block.SetEndLocation (lexer.Location); + + var outer_selector = linq_clause_blocks.Pop (); + var block = linq_clause_blocks.Pop (); + + var lt = (LocatedToken) yyVals[-10+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + Linq.RangeVariable into; + + if (yyVals[0+yyTop] == null) { + into = sn; + yyVal = new Linq.Join (block, sn, (Expression)yyVals[-7+yyTop], outer_selector, (Linq.QueryBlock) current_block, GetLocation (yyVals[-11+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-9+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-3+yyTop])); + } else { + /**/ + /* Set equals right side parent to beginning of linq query, it is not accessible therefore cannot cause name collisions*/ + /**/ + var parent = block.Parent; + while (parent is Linq.QueryBlock) { + parent = parent.Parent; + } + current_block.Parent = parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lt = (LocatedToken) yyVals[0+yyTop]; + into = new Linq.RangeVariable (lt.Value, lt.Location); + + yyVal = new Linq.GroupJoin (block, sn, (Expression)yyVals[-7+yyTop], outer_selector, (Linq.QueryBlock) current_block, into, GetLocation (yyVals[-11+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-9+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-3+yyTop]), opt_intoStack.Pop ()); + } + + current_block = block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (into); + } + +void case_1024() +#line 6950 "cs-parser.jay" +{ + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1025() +#line 6958 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1026() +#line 6966 "cs-parser.jay" +{ + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + +void case_1027() +#line 6974 "cs-parser.jay" +{ + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + current_block.SetEndLocation (lexer.Location); + + var outer_selector = linq_clause_blocks.Pop (); + var block = linq_clause_blocks.Pop (); + + var lt = (LocatedToken) yyVals[-10+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + Linq.RangeVariable into; + + if (yyVals[0+yyTop] == null) { + into = sn; + yyVal = new Linq.Join (block, sn, (Expression)yyVals[-7+yyTop], outer_selector, (Linq.QueryBlock) current_block, GetLocation (yyVals[-12+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-11+yyTop] + }; + lbag.AddLocation (yyVal, GetLocation (yyVals[-10+yyTop]), GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-4+yyTop])); + } else { + /**/ + /* Set equals right side parent to beginning of linq query, it is not accessible therefore cannot cause name collisions*/ + /**/ + var parent = block.Parent; + while (parent is Linq.QueryBlock) { + parent = parent.Parent; + } + current_block.Parent = parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lt = (LocatedToken) yyVals[0+yyTop]; + into = new Linq.RangeVariable (lt.Value, lt.Location); /* TODO:*/ + + yyVal = new Linq.GroupJoin (block, sn, (Expression)yyVals[-7+yyTop], outer_selector, (Linq.QueryBlock) current_block, into, GetLocation (yyVals[-12+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-11+yyTop] + }; + lbag.AddLocation (yyVal, GetLocation (yyVals[-10+yyTop]), GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-4+yyTop]), opt_intoStack.Pop ()); + } + + current_block = block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (into); + } + +void case_1029() +#line 7020 "cs-parser.jay" +{ + opt_intoStack.Push (GetLocation (yyVals[-1+yyTop])); + yyVal = yyVals[0+yyTop]; + } + +void case_1030() +#line 7028 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + lbag.AddLocation (current_block, GetLocation (yyVals[0+yyTop])); + } + +void case_1031() +#line 7033 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + yyVal = yyVals[0+yyTop]; + } + +void case_1033() +#line 7044 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + +void case_1034() +#line 7051 "cs-parser.jay" +{ + ((Linq.AQueryClause)yyVals[-3+yyTop]).Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = yyVals[-3+yyTop]; + } + +void case_1036() +#line 7060 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock ((Linq.QueryBlock) current_block, lexer.Location); + } + +void case_1037() +#line 7067 "cs-parser.jay" +{ + ((Linq.AQueryClause)yyVals[-3+yyTop]).Tail.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = yyVals[-3+yyTop]; + } + +void case_1039() +#line 7079 "cs-parser.jay" +{ + yyVal = new Linq.OrderByAscending ((Linq.QueryBlock) current_block, (Expression)yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_1040() +#line 7084 "cs-parser.jay" +{ + yyVal = new Linq.OrderByDescending ((Linq.QueryBlock) current_block, (Expression)yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_1042() +#line 7096 "cs-parser.jay" +{ + yyVal = new Linq.ThenByAscending ((Linq.QueryBlock) current_block, (Expression)yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_1043() +#line 7101 "cs-parser.jay" +{ + yyVal = new Linq.ThenByDescending ((Linq.QueryBlock) current_block, (Expression)yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_1045() +#line 7111 "cs-parser.jay" +{ + /* query continuation block is not linked with query block but with block*/ + /* before. This means each query can use same range variable names for*/ + /* different identifiers.*/ + + current_block.SetEndLocation (GetLocation (yyVals[-1+yyTop])); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1046() +#line 7127 "cs-parser.jay" +{ + var current_block = linq_clause_blocks.Pop (); + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + yyVal = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, null, rv, GetLocation (yyVals[-3+yyTop])) { + next = (Linq.AQueryClause)yyVals[0+yyTop] + }; + } + +void case_1049() +#line 7154 "cs-parser.jay" +{ + current_container = current_type = new Class (current_container, new MemberName (""), Modifiers.PUBLIC, null); + + /* (ref object retval)*/ + Parameter [] mpar = new Parameter [1]; + mpar [0] = new Parameter (new TypeExpression (compiler.BuiltinTypes.Object, Location.Null), "$retval", Parameter.Modifier.REF, null, Location.Null); + + ParametersCompiled pars = new ParametersCompiled (mpar); + var mods = Modifiers.PUBLIC | Modifiers.STATIC; + if (settings.Unsafe) + mods |= Modifiers.UNSAFE; + + current_local_parameters = pars; + var method = new InteractiveMethod ( + current_type, + new TypeExpression (compiler.BuiltinTypes.Void, Location.Null), + mods, + pars); + + current_type.AddMember (method); + oob_stack.Push (method); + + interactive_async = false; + + ++lexer.parsing_block; + start_block (lexer.Location); + } + +void case_1050() +#line 7182 "cs-parser.jay" +{ + --lexer.parsing_block; + var method = (InteractiveMethod) oob_stack.Pop (); + method.Block = (ToplevelBlock) end_block(lexer.Location); + + if (interactive_async == true) { + method.ChangeToAsync (); + } + + InteractiveResult = (Class) pop_current_class (); + current_local_parameters = null; + } + +void case_1060() +#line 7228 "cs-parser.jay" +{ + module.DocumentationBuilder.ParsedBuiltinType = (TypeExpression)yyVals[-1+yyTop]; + module.DocumentationBuilder.ParsedParameters = (List)yyVals[0+yyTop]; + yyVal = null; + } + +void case_1061() +#line 7234 "cs-parser.jay" +{ + module.DocumentationBuilder.ParsedBuiltinType = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[-1+yyTop])); + module.DocumentationBuilder.ParsedParameters = (List)yyVals[0+yyTop]; + yyVal = null; + } + +void case_1062() +#line 7240 "cs-parser.jay" +{ + module.DocumentationBuilder.ParsedBuiltinType = (TypeExpression)yyVals[-3+yyTop]; + module.DocumentationBuilder.ParsedParameters = (List)yyVals[0+yyTop]; + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberName (lt.Value); + } + +void case_1065() +#line 7255 "cs-parser.jay" +{ + module.DocumentationBuilder.ParsedParameters = (List)yyVals[-1+yyTop]; + yyVal = new MemberName ((MemberName) yyVals[-6+yyTop], MemberCache.IndexerNameAlias, Location.Null); + } + +void case_1066() +#line 7260 "cs-parser.jay" +{ + var p = (List)yyVals[0+yyTop] ?? new List (1); + p.Add (new DocumentationParameter ((FullNamedExpression) yyVals[-1+yyTop])); + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = Operator.OpType.Explicit; + yyVal = null; + } + +void case_1067() +#line 7268 "cs-parser.jay" +{ + var p = (List)yyVals[0+yyTop] ?? new List (1); + p.Add (new DocumentationParameter ((FullNamedExpression) yyVals[-1+yyTop])); + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = Operator.OpType.Implicit; + yyVal = null; + } + +void case_1068() +#line 7276 "cs-parser.jay" +{ + var p = (List)yyVals[0+yyTop]; + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = (Operator.OpType) yyVals[-1+yyTop]; + yyVal = null; + } + +void case_1076() +#line 7314 "cs-parser.jay" +{ + var parameters = new List (); + parameters.Add ((DocumentationParameter) yyVals[0+yyTop]); + yyVal = parameters; + } + +void case_1077() +#line 7320 "cs-parser.jay" +{ + var parameters = yyVals[-2+yyTop] as List; + parameters.Add ((DocumentationParameter) yyVals[0+yyTop]); + yyVal = parameters; + } + +void case_1078() +#line 7329 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != null) + yyVal = new DocumentationParameter ((Parameter.Modifier) yyVals[-1+yyTop], (FullNamedExpression) yyVals[0+yyTop]); + else + yyVal = new DocumentationParameter ((FullNamedExpression) yyVals[0+yyTop]); + } + +#line default + static readonly short [] yyLhs = { -1, + 0, 4, 0, 0, 1, 1, 1, 1, 2, 2, + 11, 11, 12, 12, 13, 13, 14, 15, 15, 15, + 19, 20, 17, 17, 22, 22, 22, 18, 18, 18, + 23, 23, 24, 24, 7, 7, 6, 6, 21, 21, + 8, 8, 25, 25, 25, 26, 26, 26, 26, 26, + 9, 9, 10, 10, 34, 32, 37, 33, 33, 33, + 33, 35, 35, 35, 36, 36, 41, 38, 39, 40, + 40, 42, 42, 42, 42, 42, 43, 43, 43, 47, + 44, 46, 49, 49, 49, 51, 51, 52, 52, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 67, 62, 72, 74, 77, 78, 79, + 28, 28, 82, 54, 54, 83, 83, 84, 84, 85, + 87, 81, 81, 86, 86, 92, 55, 96, 55, 55, + 91, 99, 91, 93, 93, 100, 100, 101, 102, 101, + 97, 97, 103, 103, 104, 105, 95, 95, 98, 98, + 98, 108, 56, 111, 112, 106, 113, 114, 115, 106, + 106, 106, 107, 107, 117, 117, 120, 118, 110, 110, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 122, 122, 125, 125, 125, 125, 128, 125, 126, 126, + 129, 129, 130, 130, 130, 123, 123, 123, 131, 131, + 131, 124, 133, 135, 136, 138, 57, 139, 57, 137, + 141, 137, 140, 140, 143, 145, 59, 144, 144, 134, + 134, 134, 134, 134, 149, 146, 150, 147, 148, 148, + 148, 151, 152, 153, 155, 29, 29, 154, 154, 156, + 156, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 159, 60, 160, 160, 163, 158, 158, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, 162, 165, + 164, 166, 164, 164, 164, 61, 169, 171, 167, 168, + 168, 170, 170, 175, 173, 176, 173, 173, 173, 177, + 63, 179, 58, 182, 183, 58, 58, 178, 185, 178, + 180, 180, 186, 186, 187, 188, 187, 189, 184, 181, + 181, 181, 181, 181, 193, 190, 194, 191, 192, 192, + 64, 65, 196, 198, 199, 30, 195, 195, 195, 197, + 197, 197, 200, 200, 201, 202, 201, 201, 201, 203, + 204, 205, 31, 206, 206, 16, 16, 16, 207, 207, + 207, 211, 211, 209, 209, 209, 212, 212, 214, 71, + 132, 109, 109, 142, 142, 215, 215, 215, 213, 213, + 216, 216, 217, 217, 219, 219, 90, 80, 80, 94, + 94, 127, 127, 161, 161, 221, 221, 221, 220, 224, + 224, 224, 226, 226, 227, 225, 225, 225, 225, 225, + 225, 225, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 230, 230, 230, 231, 231, 231, 251, 251, 252, + 252, 253, 253, 233, 233, 250, 250, 250, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 235, 235, 235, + 255, 255, 256, 256, 257, 257, 259, 259, 259, 260, + 260, 260, 260, 260, 260, 260, 261, 261, 174, 174, + 254, 254, 254, 254, 254, 266, 266, 265, 265, 267, + 267, 267, 267, 268, 236, 236, 236, 236, 264, 264, + 269, 269, 270, 270, 237, 238, 238, 239, 240, 241, + 241, 232, 232, 232, 232, 232, 275, 271, 242, 242, + 276, 276, 277, 277, 278, 278, 278, 278, 279, 279, + 279, 279, 272, 272, 222, 222, 274, 274, 280, 280, + 273, 273, 89, 89, 281, 281, 243, 282, 282, 210, + 208, 244, 244, 245, 245, 246, 246, 247, 284, 248, + 285, 248, 283, 283, 287, 286, 234, 288, 288, 288, + 288, 288, 288, 288, 288, 288, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 290, + 290, 290, 290, 290, 290, 290, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 292, 292, 292, + 292, 292, 294, 294, 294, 294, 295, 295, 295, 295, + 295, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 297, 297, 297, 297, 297, 298, 298, 298, 299, 299, + 299, 300, 300, 300, 301, 301, 301, 302, 302, 302, + 303, 303, 304, 304, 304, 304, 304, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 306, 306, + 307, 307, 307, 307, 308, 308, 310, 309, 309, 309, + 50, 50, 312, 311, 313, 311, 314, 311, 315, 316, + 311, 317, 318, 311, 45, 45, 262, 262, 262, 262, + 249, 249, 249, 88, 320, 73, 73, 321, 322, 322, + 322, 322, 324, 322, 325, 326, 327, 328, 27, 70, + 70, 69, 69, 116, 116, 329, 329, 329, 329, 329, + 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, + 75, 75, 323, 323, 76, 76, 330, 330, 331, 331, + 332, 332, 333, 333, 333, 333, 218, 218, 334, 334, + 335, 119, 68, 68, 336, 172, 172, 338, 337, 66, + 66, 339, 339, 340, 340, 340, 340, 340, 344, 344, + 345, 345, 345, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 346, 346, 346, 346, + 346, 346, 346, 346, 346, 346, 346, 346, 346, 360, + 360, 360, 360, 347, 361, 343, 362, 362, 363, 363, + 363, 363, 223, 223, 364, 48, 48, 366, 341, 370, + 341, 368, 368, 365, 365, 365, 367, 367, 374, 374, + 373, 373, 375, 375, 369, 369, 371, 371, 376, 376, + 377, 372, 372, 372, 348, 348, 348, 359, 359, 378, + 379, 379, 349, 349, 380, 380, 380, 383, 381, 381, + 382, 382, 384, 384, 384, 385, 386, 386, 387, 387, + 387, 350, 350, 350, 350, 388, 388, 389, 389, 389, + 393, 390, 396, 392, 392, 399, 395, 395, 398, 398, + 394, 394, 402, 401, 401, 397, 397, 400, 400, 404, + 403, 403, 391, 391, 405, 391, 391, 391, 351, 351, + 351, 351, 351, 351, 406, 407, 407, 408, 408, 408, + 409, 409, 409, 410, 410, 410, 411, 411, 411, 412, + 412, 352, 352, 352, 352, 413, 413, 293, 293, 414, + 416, 414, 414, 414, 415, 415, 353, 354, 417, 357, + 355, 355, 419, 420, 358, 422, 423, 356, 356, 356, + 421, 421, 418, 418, 319, 319, 319, 319, 424, 424, + 426, 426, 428, 427, 429, 427, 425, 425, 425, 425, + 425, 433, 431, 434, 436, 431, 435, 435, 430, 430, + 437, 437, 437, 437, 437, 442, 438, 443, 439, 444, + 445, 446, 440, 448, 449, 450, 440, 447, 447, 452, + 441, 451, 455, 451, 454, 457, 454, 453, 453, 453, + 456, 456, 456, 432, 458, 432, 3, 3, 459, 3, + 3, 460, 460, 263, 263, 258, 258, 5, 461, 461, + 461, 461, 461, 465, 461, 461, 461, 461, 462, 462, + 463, 466, 463, 464, 464, 467, 467, 468, + }; + static readonly short [] yyLen = { 2, + 2, 0, 3, 1, 2, 4, 3, 1, 0, 1, + 1, 2, 4, 2, 1, 2, 1, 3, 5, 2, + 0, 0, 11, 3, 0, 1, 1, 1, 3, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, + 0, 1, 1, 2, 0, 3, 0, 6, 3, 2, + 1, 1, 1, 1, 1, 3, 0, 3, 1, 0, + 3, 0, 1, 1, 3, 3, 1, 1, 1, 0, + 4, 4, 0, 1, 1, 0, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 4, 0, 0, 0, 0, 0, + 17, 5, 0, 9, 5, 0, 1, 1, 2, 3, + 0, 3, 1, 1, 1, 0, 8, 0, 9, 6, + 0, 0, 3, 0, 1, 1, 2, 2, 0, 5, + 0, 1, 1, 2, 3, 0, 4, 2, 1, 1, + 1, 0, 3, 0, 0, 10, 0, 0, 0, 12, + 8, 5, 1, 1, 1, 1, 0, 4, 0, 1, + 1, 3, 3, 3, 5, 3, 5, 1, 1, 1, + 1, 3, 4, 6, 2, 4, 0, 7, 0, 1, + 1, 2, 1, 1, 1, 4, 6, 4, 1, 2, + 2, 1, 0, 0, 0, 0, 12, 0, 6, 0, + 0, 4, 1, 1, 0, 0, 10, 3, 1, 1, + 2, 1, 2, 1, 0, 5, 0, 5, 1, 1, + 1, 0, 0, 0, 0, 15, 5, 0, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 5, 1, 1, 0, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 7, 0, 7, 2, 2, 2, 0, 0, 9, 1, + 1, 0, 1, 0, 6, 0, 6, 2, 1, 0, + 8, 0, 9, 0, 0, 10, 5, 0, 0, 3, + 0, 1, 1, 2, 2, 0, 5, 0, 2, 2, + 2, 1, 1, 1, 0, 5, 0, 5, 1, 1, + 2, 4, 0, 0, 0, 12, 0, 2, 2, 0, + 1, 2, 1, 3, 2, 0, 5, 3, 1, 0, + 0, 0, 13, 0, 1, 1, 3, 3, 1, 4, + 4, 2, 2, 0, 3, 2, 1, 3, 0, 3, + 1, 1, 3, 1, 2, 3, 4, 4, 0, 3, + 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 1, 1, 2, + 2, 2, 1, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 3, 4, 4, 5, 4, 4, + 4, 3, 3, 3, 4, 3, 4, 4, 4, 3, + 0, 1, 3, 4, 0, 1, 1, 3, 2, 3, + 3, 1, 2, 3, 5, 2, 1, 1, 0, 1, + 1, 3, 3, 3, 2, 1, 1, 1, 1, 2, + 2, 4, 3, 1, 4, 5, 4, 3, 1, 3, + 1, 3, 1, 1, 1, 4, 3, 2, 2, 6, + 3, 7, 4, 3, 7, 3, 0, 2, 4, 3, + 1, 2, 0, 1, 1, 3, 1, 2, 3, 1, + 1, 1, 0, 1, 1, 2, 2, 3, 1, 2, + 0, 1, 2, 4, 1, 3, 4, 1, 1, 1, + 2, 4, 4, 4, 2, 4, 2, 4, 0, 4, + 0, 5, 0, 1, 0, 4, 4, 1, 2, 2, + 4, 2, 2, 2, 4, 2, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, + 3, 3, 4, 3, 3, 3, 3, 1, 2, 1, + 2, 2, 2, 2, 1, 1, 1, 3, 3, 3, + 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 3, 3, 3, 3, 1, 3, 3, 1, 3, + 3, 1, 3, 3, 1, 3, 3, 1, 3, 3, + 1, 3, 1, 5, 4, 5, 5, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, + 3, 2, 1, 1, 0, 1, 0, 2, 1, 1, + 1, 1, 0, 4, 0, 4, 0, 5, 0, 0, + 7, 0, 0, 8, 1, 1, 1, 1, 1, 1, + 6, 4, 4, 1, 1, 0, 1, 3, 0, 1, + 1, 2, 0, 6, 0, 0, 0, 0, 15, 0, + 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 3, 0, 1, 1, 2, 4, 3, + 1, 3, 1, 3, 1, 1, 0, 1, 1, 1, + 0, 4, 1, 1, 0, 4, 1, 0, 4, 0, + 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 4, 1, 2, 2, 2, + 1, 1, 1, 2, 1, 1, 1, 0, 6, 0, + 7, 1, 1, 0, 2, 1, 0, 1, 0, 1, + 1, 2, 2, 4, 0, 2, 0, 1, 1, 2, + 4, 1, 5, 2, 2, 2, 2, 2, 2, 1, + 1, 1, 1, 1, 5, 7, 4, 0, 8, 4, + 0, 1, 1, 2, 1, 2, 1, 2, 3, 3, + 1, 1, 1, 1, 1, 5, 4, 7, 3, 6, + 0, 4, 0, 4, 2, 0, 4, 2, 3, 1, + 0, 1, 0, 5, 1, 0, 1, 0, 1, 1, + 1, 3, 4, 5, 0, 9, 5, 4, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 4, 3, + 3, 3, 2, 3, 3, 2, 4, 4, 3, 0, + 1, 3, 4, 5, 3, 1, 2, 0, 1, 3, + 0, 8, 3, 6, 0, 4, 2, 2, 0, 3, + 5, 4, 0, 0, 10, 0, 0, 9, 5, 4, + 2, 1, 0, 2, 2, 2, 2, 2, 4, 5, + 4, 5, 0, 5, 0, 6, 3, 2, 2, 2, + 1, 0, 3, 0, 0, 5, 2, 1, 1, 2, + 1, 1, 1, 1, 1, 0, 5, 0, 3, 0, + 0, 0, 12, 0, 0, 0, 13, 0, 2, 0, + 3, 1, 0, 4, 1, 0, 4, 1, 2, 2, + 1, 2, 2, 0, 0, 4, 2, 3, 0, 4, + 2, 2, 3, 0, 1, 1, 1, 2, 2, 2, + 2, 4, 3, 0, 7, 4, 4, 3, 1, 3, + 0, 0, 4, 0, 1, 1, 3, 2, + }; + static readonly short [] yyDefRed = { 0, + 8, 0, 0, 0, 0, 0, 0, 0, 2, 4, + 0, 0, 11, 14, 0, 1047, 0, 0, 1051, 0, + 0, 15, 17, 408, 414, 421, 409, 411, 0, 410, + 0, 417, 419, 406, 0, 413, 415, 407, 418, 420, + 416, 0, 369, 1069, 0, 412, 1058, 0, 10, 1, + 0, 0, 0, 12, 0, 872, 0, 0, 0, 0, + 0, 0, 0, 0, 449, 0, 0, 0, 0, 0, + 0, 0, 447, 0, 0, 0, 515, 0, 448, 0, + 0, 0, 969, 0, 0, 0, 710, 0, 0, 0, + 0, 0, 0, 0, 771, 0, 824, 0, 0, 0, + 0, 0, 0, 0, 0, 446, 0, 699, 0, 871, + 0, 807, 0, 442, 832, 831, 0, 0, 0, 423, + 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, + 434, 435, 436, 437, 438, 439, 440, 441, 444, 445, + 706, 587, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 707, 705, 708, 709, 791, 793, + 0, 789, 792, 808, 810, 811, 812, 813, 814, 815, + 816, 817, 818, 819, 809, 0, 0, 0, 873, 874, + 892, 893, 894, 895, 929, 930, 931, 932, 933, 934, + 0, 0, 0, 20, 0, 0, 356, 0, 359, 1055, + 16, 1048, 0, 0, 263, 262, 259, 264, 265, 258, + 277, 276, 269, 270, 266, 268, 267, 271, 260, 261, + 272, 273, 279, 278, 274, 275, 0, 1072, 1061, 0, + 0, 1060, 0, 1059, 3, 55, 0, 0, 0, 44, + 41, 43, 46, 47, 48, 49, 50, 53, 13, 0, + 0, 0, 935, 565, 450, 451, 967, 0, 0, 0, + 0, 0, 0, 0, 0, 937, 936, 0, 575, 569, + 574, 823, 870, 794, 821, 820, 822, 795, 796, 797, + 798, 799, 800, 801, 802, 803, 804, 805, 806, 0, + 0, 0, 901, 0, 0, 0, 837, 836, 0, 0, + 0, 0, 0, 0, 0, 0, 943, 0, 0, 0, + 0, 422, 0, 0, 0, 946, 0, 0, 0, 0, + 567, 968, 0, 0, 0, 835, 402, 0, 0, 0, + 0, 0, 0, 388, 389, 0, 398, 0, 0, 0, + 0, 0, 0, 0, 702, 0, 586, 0, 0, 695, + 0, 0, 582, 0, 0, 584, 580, 594, 588, 595, + 589, 583, 579, 599, 593, 598, 592, 596, 590, 597, + 591, 693, 561, 0, 560, 443, 362, 363, 0, 0, + 0, 0, 0, 825, 0, 355, 0, 400, 401, 0, + 0, 518, 519, 0, 0, 0, 829, 830, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1050, 790, 838, 828, 0, 868, 869, 1001, 1018, + 0, 0, 1002, 1004, 0, 1030, 987, 985, 1011, 0, + 0, 1009, 1012, 1013, 1014, 1015, 988, 986, 0, 0, + 0, 0, 18, 0, 0, 0, 1068, 0, 0, 370, + 0, 0, 1070, 0, 0, 42, 741, 747, 739, 0, + 736, 746, 740, 738, 737, 744, 742, 743, 749, 745, + 748, 750, 0, 0, 734, 45, 54, 517, 0, 513, + 514, 0, 0, 511, 0, 840, 0, 0, 0, 899, + 0, 867, 865, 866, 0, 0, 0, 714, 0, 940, + 938, 715, 0, 0, 542, 0, 0, 530, 537, 0, + 0, 0, 531, 0, 0, 547, 549, 0, 526, 0, + 0, 0, 0, 0, 521, 0, 524, 528, 391, 390, + 942, 941, 0, 0, 945, 944, 955, 0, 0, 0, + 956, 559, 0, 385, 558, 0, 0, 970, 0, 0, + 834, 0, 396, 397, 0, 0, 395, 0, 0, 0, + 600, 0, 0, 571, 0, 697, 617, 616, 0, 0, + 788, 0, 0, 0, 782, 784, 785, 786, 454, 455, + 0, 366, 367, 0, 194, 193, 195, 0, 684, 0, + 0, 0, 392, 0, 679, 0, 0, 949, 0, 0, + 0, 462, 463, 0, 466, 0, 0, 0, 0, 464, + 0, 0, 508, 0, 470, 0, 0, 0, 0, 496, + 499, 0, 0, 491, 498, 497, 668, 669, 670, 671, + 672, 673, 674, 675, 676, 678, 677, 604, 601, 606, + 603, 605, 602, 614, 612, 615, 0, 0, 626, 625, + 0, 0, 0, 0, 610, 0, 611, 0, 630, 0, + 0, 631, 0, 637, 0, 638, 0, 639, 0, 640, + 0, 644, 0, 645, 0, 648, 0, 651, 0, 654, + 0, 657, 0, 660, 0, 662, 0, 0, 546, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1000, 999, + 0, 1010, 0, 998, 0, 0, 357, 358, 1066, 1067, + 0, 0, 191, 0, 0, 1076, 384, 0, 0, 0, + 381, 1062, 1064, 61, 63, 64, 0, 0, 56, 0, + 0, 65, 67, 30, 28, 0, 0, 0, 731, 0, + 735, 460, 0, 516, 0, 564, 0, 577, 180, 202, + 0, 0, 0, 170, 0, 0, 0, 181, 570, 0, + 973, 0, 921, 902, 0, 912, 0, 923, 0, 939, + 877, 0, 972, 0, 0, 529, 0, 538, 548, 550, + 0, 0, 0, 0, 482, 0, 0, 477, 0, 0, + 692, 691, 509, 0, 552, 523, 0, 0, 151, 553, + 149, 150, 555, 0, 563, 562, 880, 0, 0, 0, + 0, 953, 0, 957, 557, 566, 980, 0, 976, 897, + 0, 991, 0, 989, 0, 0, 712, 713, 0, 0, + 0, 690, 689, 696, 0, 461, 787, 773, 774, 772, + 783, 694, 0, 365, 682, 0, 0, 0, 585, 581, + 948, 947, 826, 467, 459, 0, 0, 465, 456, 457, + 568, 507, 505, 504, 501, 500, 0, 495, 452, 453, + 468, 469, 0, 621, 622, 623, 624, 959, 613, 619, + 665, 0, 846, 0, 0, 1019, 993, 0, 1020, 0, + 1003, 1005, 1016, 0, 1031, 0, 997, 1045, 19, 360, + 361, 1078, 192, 1073, 0, 770, 769, 0, 768, 0, + 380, 0, 60, 57, 0, 0, 0, 0, 0, 0, + 387, 0, 725, 0, 0, 85, 84, 0, 512, 0, + 0, 0, 0, 0, 185, 576, 0, 0, 0, 0, + 0, 913, 905, 903, 0, 924, 0, 0, 971, 539, + 536, 0, 486, 0, 0, 0, 1056, 1057, 473, 479, + 0, 483, 0, 0, 0, 0, 0, 0, 878, 0, + 963, 0, 960, 954, 979, 0, 896, 992, 990, 0, + 572, 0, 698, 688, 368, 681, 680, 700, 458, 506, + 503, 0, 494, 493, 492, 666, 667, 664, 0, 862, + 845, 0, 0, 0, 851, 0, 995, 0, 1024, 0, + 0, 1039, 1040, 1033, 0, 1077, 383, 382, 0, 0, + 66, 59, 0, 68, 29, 22, 0, 0, 333, 0, + 237, 0, 112, 0, 82, 856, 124, 125, 0, 0, + 0, 859, 200, 201, 0, 0, 0, 0, 173, 182, + 174, 176, 900, 0, 0, 0, 0, 0, 922, 0, + 0, 487, 488, 481, 484, 480, 0, 474, 478, 0, + 544, 0, 510, 520, 472, 556, 554, 0, 0, 0, + 982, 0, 0, 711, 703, 0, 502, 0, 0, 843, + 842, 839, 852, 994, 0, 0, 0, 1008, 0, 1006, + 1017, 0, 1046, 1065, 0, 79, 0, 0, 73, 74, + 77, 78, 0, 350, 339, 338, 0, 726, 233, 107, + 0, 841, 860, 186, 0, 198, 0, 0, 0, 898, + 984, 0, 0, 0, 0, 904, 0, 925, 876, 0, + 525, 522, 885, 0, 891, 0, 0, 883, 0, 887, + 966, 0, 981, 977, 0, 701, 0, 0, 996, 1021, + 0, 1007, 0, 0, 1035, 0, 80, 71, 0, 0, + 0, 334, 0, 0, 0, 0, 0, 187, 0, 177, + 175, 974, 914, 908, 906, 0, 485, 0, 879, 884, + 0, 888, 964, 0, 0, 704, 0, 854, 0, 1025, + 1042, 1043, 1036, 58, 0, 75, 76, 0, 0, 0, + 0, 0, 0, 0, 720, 0, 752, 0, 717, 861, + 184, 0, 197, 0, 0, 926, 890, 889, 0, 978, + 863, 0, 0, 0, 81, 0, 0, 351, 0, 0, + 349, 335, 0, 343, 0, 405, 0, 403, 0, 0, + 727, 0, 757, 234, 0, 188, 975, 910, 907, 0, + 0, 919, 775, 777, 962, 1022, 0, 1037, 0, 0, + 0, 331, 0, 0, 718, 754, 0, 723, 0, 0, + 758, 0, 108, 0, 0, 0, 1026, 27, 26, 23, + 352, 348, 0, 0, 344, 404, 0, 760, 0, 0, + 0, 0, 909, 0, 0, 0, 0, 0, 32, 336, + 0, 765, 0, 766, 763, 0, 761, 103, 104, 0, + 100, 0, 0, 88, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 101, 102, 152, 0, 0, 250, + 242, 243, 244, 245, 246, 247, 248, 249, 0, 0, + 240, 109, 776, 0, 1023, 0, 353, 347, 724, 0, + 0, 0, 0, 728, 89, 0, 291, 286, 290, 0, + 235, 241, 0, 1029, 1027, 764, 762, 0, 0, 0, + 0, 0, 0, 0, 0, 300, 0, 0, 251, 0, + 0, 257, 0, 167, 166, 153, 163, 164, 165, 0, + 0, 0, 105, 0, 0, 285, 0, 0, 284, 0, + 157, 0, 0, 374, 332, 0, 372, 0, 0, 0, + 0, 0, 0, 0, 0, 729, 0, 236, 110, 115, + 113, 307, 0, 371, 0, 0, 0, 0, 128, 0, + 0, 0, 0, 0, 0, 162, 154, 0, 0, 0, + 215, 0, 375, 0, 252, 0, 0, 0, 0, 304, + 0, 282, 130, 0, 280, 0, 0, 0, 132, 0, + 376, 0, 0, 204, 209, 0, 0, 0, 373, 255, + 168, 111, 123, 121, 0, 0, 309, 0, 0, 0, + 0, 0, 158, 0, 288, 0, 0, 0, 0, 136, + 0, 0, 0, 0, 377, 378, 0, 0, 0, 0, + 0, 118, 324, 0, 305, 0, 0, 318, 0, 0, + 0, 313, 0, 148, 0, 0, 0, 0, 143, 0, + 0, 301, 0, 133, 0, 127, 137, 155, 161, 224, + 0, 205, 0, 0, 216, 0, 122, 0, 114, 119, + 0, 0, 0, 320, 0, 321, 310, 0, 0, 303, + 314, 283, 0, 0, 129, 144, 281, 0, 299, 0, + 289, 293, 139, 0, 0, 0, 221, 223, 0, 256, + 120, 325, 327, 306, 0, 0, 319, 316, 147, 145, + 159, 298, 0, 0, 0, 156, 225, 227, 206, 0, + 219, 217, 0, 0, 318, 0, 294, 296, 140, 0, + 0, 0, 0, 329, 330, 326, 328, 317, 160, 0, + 0, 231, 230, 229, 226, 228, 211, 207, 218, 0, + 0, 0, 295, 297, 213, 214, 0, 212, + }; + protected static readonly short [] yyDgoto = { 7, + 8, 50, 9, 51, 10, 11, 52, 237, 771, 772, + 12, 13, 53, 22, 23, 331, 240, 756, 939, 1133, + 1257, 1310, 1634, 936, 241, 242, 243, 244, 245, 246, + 247, 248, 749, 474, 750, 751, 1040, 752, 753, 1044, + 937, 1128, 1129, 1130, 273, 641, 1225, 111, 948, 813, + 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, + 1352, 1353, 1354, 1355, 1356, 593, 1382, 860, 493, 760, + 1437, 1054, 1238, 1195, 1236, 1271, 1322, 1393, 1478, 1266, + 1505, 1479, 1530, 1531, 1532, 1056, 1528, 1057, 822, 940, + 1490, 1464, 1518, 548, 1511, 1484, 1547, 1020, 1516, 1519, + 1520, 1615, 1548, 1549, 1545, 1357, 1416, 1386, 1438, 773, + 1492, 1594, 1461, 1551, 1626, 494, 1417, 1418, 274, 1447, + 774, 775, 776, 777, 778, 731, 611, 1242, 732, 733, + 954, 1440, 1469, 1562, 1523, 1596, 1648, 1632, 1470, 1657, + 1652, 1441, 1496, 1622, 1599, 1563, 1564, 1645, 1630, 1631, + 1052, 1194, 1302, 1369, 1421, 1370, 1371, 1409, 1444, 1410, + 334, 227, 1527, 1412, 1512, 1509, 1358, 1388, 1433, 1591, + 1553, 1285, 1592, 642, 1640, 1641, 1432, 1508, 1481, 1540, + 1535, 1506, 1572, 1577, 1538, 1541, 1542, 1625, 1578, 1536, + 1537, 1636, 1623, 1624, 1049, 1137, 1262, 1230, 1293, 1263, + 1264, 1313, 1191, 1290, 1327, 388, 197, 113, 377, 378, + 114, 604, 470, 230, 1456, 740, 741, 928, 941, 335, + 336, 435, 327, 337, 311, 1267, 1268, 46, 118, 312, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 258, 891, 818, 1094, 1083, 806, 979, 807, 808, + 1084, 141, 202, 814, 644, 645, 646, 885, 503, 504, + 304, 1092, 816, 436, 306, 532, 533, 534, 535, 538, + 824, 566, 270, 509, 849, 271, 508, 142, 143, 144, + 145, 673, 899, 674, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 614, 615, 616, 854, 855, + 157, 601, 589, 851, 379, 1106, 585, 1175, 158, 523, + 1233, 1234, 1237, 1317, 1050, 1193, 1300, 1413, 495, 1272, + 1273, 1336, 1337, 929, 354, 1305, 0, 0, 594, 595, + 275, 276, 277, 161, 162, 163, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 175, 290, + 621, 176, 177, 328, 905, 708, 1023, 1112, 951, 767, + 1060, 1021, 1024, 1153, 1025, 1061, 1062, 291, 178, 179, + 180, 1166, 1098, 1167, 1168, 1169, 1170, 181, 182, 183, + 184, 784, 516, 785, 1156, 1078, 1157, 1279, 1245, 1280, + 786, 1077, 787, 1282, 1206, 185, 186, 187, 188, 189, + 190, 313, 560, 561, 831, 1214, 324, 1076, 961, 1244, + 1103, 996, 1215, 191, 448, 192, 449, 1026, 1115, 450, + 451, 724, 715, 716, 1120, 1030, 452, 453, 454, 455, + 456, 1031, 710, 1028, 1219, 1306, 1375, 1117, 1253, 1326, + 915, 718, 916, 1184, 1122, 1185, 1254, 1035, 17, 19, + 47, 48, 229, 734, 932, 468, 735, 736, + }; + protected static readonly short [] yySindex = { -68, + 0, -187, 189, 161, 258,18441, 0, 251, 0, 0, + 258, 161, 0, 0, 303, 0, 8261, 258, 0, -151, + -258, 0, 0, 0, 0, 0, 0, 0, 249, 0, + 448, 0, 0, 0, 5471, 0, 0, 0, 0, 0, + 0, 297, 0, 0, 426, 0, 0, 720, 0, 0, + 251, 495, 258, 0, 534, 0, 322, 590, -183,17987, + -27, 138, 512, 8419, 0, 138, 138, 138, -90, 138, + 138, 610, 0,10410, 138, 138, 0,10568, 0, 642, + 138, -181, 0, 138, 645, 138, 0,18473,18473, 666, + 138, 138, 97,10728, 0,17096, 0,11916,12048,12180, +12312,12444,12576,12708,12840, 0, 232, 0, 9503, 0, + 184, 0, 50, 0, 0, 0, 579, 500, -234, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 853, 917, 209, 755, 636, 338, 671, 697, + 686, 694, 533, 713, 0, 0, 0, 0, 0, 0, + 3940, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 50, 745, 197, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 377, 418, 495, 0, 517, -240, 0, 704, 0, 0, + 0, 0, 9503, 9503, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 297, 0, 0, 712, + 751, 0, -200, 0, 0, 0, 495,19024, 841, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 877, + 50,17233, 0, 0, 0, 0, 0,17096, -205, -141, + 901, 811, 488, 500, 50, 0, 0, 9503, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, + 275,17987, 0, 9503,17096, 816, 0, 0, 826,17096, +17096, 6467, 537, -93, 874, 9503, 0,10728, 232, 1010, + 897, 0, 913, 9503,17096, 0, 1022, 918, 690, 951, + 0, 0,17096, 642,16685, 0, 0, 645,17096, 577, + 634, 989, 50, 0, 0, 745, 0, -234, 1013, 50, +17096,17096,17096, 512, 0, 980, 0, 9503, 9503, 0, +11784, 50, 0, 8577, 250, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1069, 0, 0, 0, 0,18291, 577, + 959, 958,17096, 0, 562, 0, 126, 0, 0, 316, + 259, 0, 0, 923,10860, 8893, 0, 0,17096,17096, +17096,17096,17096,17096,17096,17096,17096,17096,17096,12972, +13104,13236, 2103,15577,13368,13500,13632,13764,13896,14028, +14160,14292,14424,14556,14688,14820,14952,15084,15216,17644, +17096, 0, 0, 0, 0, 745, 0, 0, 0, 0, +18473,18473, 0, 0, 50, 0, 0, 0, 0, 552, + 981, 0, 0, 0, 0, 0, 0, 0, 495, 841, + 927, 943, 0, 562, 297, 297, 0, 486, 107, 0, + 297, 998, 0, -193,19024, 0, 0, 0, 0, -145, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255,19054, 0, 0, 0, 0, 962, 0, + 0, 1003, 788, 0, 1001, 0, 1011, 164, 642, 0, + 138, 0, 0, 0, 50,16685, -115, 0, 1021, 0, + 0, 0, 112, 168, 0, 811, 488, 0, 0, 1008, + 0, 1041, 0, 1037, 983, 0, 0, 805, 0, 9167, + 806,11018, 874,16548, 0, 9328, 0, 0, 0, 0, + 0, 0, 174, 192, 0, 0, 0, -224, 642, -147, + 0, 0, 645, 0, 0, 1042, 1044, 0, 195, 50, + 0, 205, 0, 0,17096, 1126, 0,17096, 1129, 1050, + 0, 1053, 1055, 0,18291, 0, 0, 0, 188, 962, + 0, -92, -272, 8577, 0, 0, 0, 0, 0, 0, + 188, 0, 0, -235, 0, 0, 0, 645, 0, 577, + 50, 9661, 0, 1056, 0, 1060,15348, 0, 1181, 1061, + 8577, 0, 0, 1014, 0, 962, 50,17233, 1015, 0, + 562, 962, 0, 116, 0,17096,17096, 1067, 1186, 0, + 0, 270, 146, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,17769,17769, 0, 0, + -234, 0, 50, 745, 0, 917, 0, 917, 0,11652, + 209, 0, 209, 0, 755, 0, 755, 0, 755, 0, + 755, 0, 636, 0, 636, 0, 338, 0, 671, 0, + 697, 0, 686, 0, 694, 0, -57, -180, 0,11018, + 1151, 50, 1152, 50,11018,11018, 1066,17096, 0, 0, + 981, 0, 50, 0, 244, 562, 0, 0, 0, 0, + 9661, 486, 0, 1076, 1075, 0, 0, 707, 495, -8, + 0, 0, 0, 0, 0, 0, -185, 1077, 0, 1079, + 1081, 0, 0, 0, 0, 1083,10269, 1039, 0, 435, + 0, 0, 687, 0,17233, 0, 1082, 0, 0, 0, + 729, 141, 1086, 0, 1088, 1091, 1092, 0, 0,17096, + 0, 50, 0, 0, 818, 0, 1094, 0, 256, 0, + 0, 8419, 0, 8419, 9802, 0,15688, 0, 0, 0, + 9962,10094, 422,11018, 0, 7, 149, 0, 1031, 1046, + 0, 0, 0, 834, 0, 0, 1099, 1098, 0, 0, + 0, 0, 0, 1100, 0, 0, 0, 1106, 138, 4352, + 642, 0, 642, 0, 0, 0, 0, 8419, 0, 0, + 8419, 0,17096, 0,17096, 9503, 0, 0, 642, 1103, + 188, 0, 0, 0,17096, 0, 0, 0, 0, 0, + 0, 0, 9503, 0, 0, 50,18291, 1135, 0, 0, + 0, 0, 0, 0, 0, 962, 842, 0, 0, 0, + 0, 0, 0, 0, 0, 0,16411, 0, 0, 0, + 0, 0, 9486, 0, 0, 0, 0, 0, 0, 0, + 0,10252, 0, 9644, 1108, 0, 0, 1190, 0, 1191, + 0, 0, 0, 932, 0, 1111, 0, 0, 0, 0, + 0, 0, 0, 0, 486, 0, 0, 1068, 0, 107, + 0, 486, 0, 0, 927, 1119, 1122, 1072, 1130, 1039, + 0, 1121, 0, 1245, 1246, 0, 0,11018, 0,16822, + 1128, 729, 9661, 9503, 0, 0, 371, 1249, 1250, 219, + 1127, 0, 0, 0,17096, 0,17096, 1230, 0, 0, + 0,16959, 0, 342,16959, 848, 0, 0, 0, 0, + 9030, 0, 1256, 745,11018, 1145, 9802, 1146, 0,17096, + 0, 50, 0, 0, 0, -87, 0, 0, 0, 1141, + 0, 1174, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 706, 0, 0, 0, 0, 0, 0, 9503, 0, + 0, 50, -242, 1108, 0,11018, 0,11018, 0, 191, +11018, 0, 0, 0, 503, 0, 0, 0, 1150, 927, + 0, 0,11176, 0, 0, 0, 1155, 4512, 0, 1039, + 0, 1039, 0, 1039, 0, 0, 0, 0, 50, 1156, + 1128, 0, 0, 0, -109, -125, 1154, 1157, 0, 0, + 0, 0, 0, 1159, 9802, 1108, -180,17096, 0, 1166, + 8419, 0, 0, 0, 0, 0, 1158, 0, 0, 1170, + 0, 874, 0, 0, 0, 0, 0, -201, 1169, 1171, + 0, 1108, 1175, 0, 0, 188, 0, 1123, 1167, 0, + 0, 0, 0, 0,11018, 1204,11018, 0,11018, 0, + 0,17096, 0, 0, 1081, 0, 515, 739, 0, 0, + 0, 0, 161, 0, 0, 0, 1185, 0, 0, 0, + 1172, 0, 0, 0, 682, 0, 1176, 1300, 1303, 0, + 0, 1108, 1193, 1108, 1195, 0, 1187, 0, 0,16959, + 0, 0, 0,17096, 0, 1194, -209, 0, 8101, 0, + 0, 1313, 0, 0, 188, 0,17096, 9644, 0, 0, + 1223, 0, 964, 1197, 0, 1202, 0, 0,11176, 258, + 164, 0, 852, 1199, 1205,16822, 1207, 0,17096, 0, + 0, 0, 0, 0, 0, 8419, 0, 77, 0, 0, + 8577, 0, 0, 1290, 8419, 0, 1213, 0,11018, 0, + 0, 0, 0, 0,17096, 0, 0, 495, 1214, 495, + 164, 9503, 1199, 1253, 0, 1253, 0, 1199, 0, 0, + 0,17096, 0, 8419,11334, 0, 0, 0, 906, 0, + 0, 1240,11018,17096, 0, 495, 1220, 0, 1180, 991, + 0, 0, 1225, 0, 1222, 0, 165, 0, 1229, 1183, + 0, 1253, 0, 0, 1253, 0, 0, 0, 0, 1231, + 1094, 0, 0, 0, 0, 0, 1255, 0, 68, 1253, + 1349, 0, 1238, 495, 0, 0, 9503, 0, 106, 1241, + 0, 1242, 0, 8419, 8577,11018, 0, 0, 0, 0, + 0, 0, 1226, 1235, 0, 0,16548, 0, 2289, 66, + 495, 1247, 0, 1248, 1260,11018, 1251,17096, 0, 0, + 1254, 0, 1257, 0, 0, 1243, 0, 0, 0,19054, + 0, 1261, 66, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 714,19054, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1262, 495, + 0, 0, 0, 50, 0, 1260, 0, 0, 0, 1259, + 2289, 8577,18159, 0, 0, 595, 0, 0, 0, 5312, + 0, 0, 66, 0, 0, 0, 0, -272, 9503, 9503, + 176, 9503, 477, 645, 1282, 0, 577, 1663, 0, 1317, + 0, 0, 1235, 0, 0, 0, 0, 0, 0, 4029, + 1235, 1264, 0, -123, -120, 0, 9503, -118, 0, 9503, + 0, 1215, 1265, 0, 0, 12, 0, 143, 4675, 0, + 1266, 1219, 153, 595, 5471, 0,17096, 0, 0, 0, + 0, 0, 12, 0, 1271, 1221, 1269, 1267, 0, 1270, + 1224, 1275, 164, 1272, 1274, 0, 0, 1280, 1281, 1310, + 0, 962, 0, 965, 0, 1296, 1292, 1235, -62, 0, + 1288, 0, 0, 1301, 0, 1304, 1299, 1302, 0, 1306, + 0, 164, 164, 0, 0, 164, 1305, 1309, 0, 0, + 0, 0, 0, 0, 1314, 150, 0, 1316, 164, 1421, + 1318, 164, 0, 501, 0, 9802, 1273, 1308, 1306, 0, + 1321, 1322, 152, 1325, 0, 0, 164,16822, 1278, 1323, + 1314, 0, 0,19054, 0, 495, 495, 0, 1279, 1324, + 1316, 0, 1326, 0,17096, 1284, 1327, 1318, 0, 1332, + 164, 0, 114, 0, 1328, 0, 0, 0, 0, 0, +19054, 0, 152, 152, 0, 1333, 0, -62, 0, 0, + 409, 1338,19054, 0,19054, 0, 0, 9802, 1329, 0, + 0, 0, 1337, 1301, 0, 0, 0, 1336, 0, 208, + 0, 0, 0, 1253, 1017, 1343, 0, 0, 538, 0, + 0, 0, 0, 0, 1398, 1451, 0, 0, 0, 0, + 0, 0, 1344, 1345, 9802, 0, 0, 0, 0, 152, + 0, 0, 580, 580, 0, 1253, 0, 0, 0, -143, + -143, 1340, 1354, 0, 0, 0, 0, 0, 0,16548, +16548, 0, 0, 0, 0, 0, 0, 0, 0, 1360, + 1361,16822, 0, 0, 0, 0, 1358, 0, + }; + protected static readonly short [] yyRindex = { 1936, + 0, 0, 8735, 1936, 0, 0, 0, 1736, 0, 0, + 3522, 1489, 0, 0, 0, 0, 0, 3522, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1737, 0, 0, 1737, 0, 0, 1737, 0, 0, + 1736, 3602, 2027, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1371, 0, 0, 0, 0, 0, 0, 0, + 0,10427, 0, 1363, 0, 0, 0, 1363, 0, 0, + 0, 0, 0, 0, 3192, 0, 0, 0, 0, 0, + 0, 0, 0, 190, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5375, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5788, 5311, 5699, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5893, 6070, 4119, 6623, 7048, 7255, 7393, 7531, + 7669, 7807, 1311, 5470, 0, 0, 0, 0, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3649, 0, 688, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1737, 0, 0, 307, + 0, 0, 0, 0, 0, 0, 3712, 432, 3755, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4351, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1373, 0, 0, 0, 0, 0, 0, 4351, 1366, + 0, 0, 0, 0, 0, 0, 1366, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2148, 0, 403, + 2851, 0, 0, 0, 0, 2982, 0, 2851, 0, 0, + 0, 0, 0, 1371, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1372, 3087, + 0, 0, 1363, 0, 4351, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 278, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2384, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1852, 0, 0, 0, 0, 0, 0, 0, 3798, 3861, + 0, 0, 0, 1531, 1737, 1737, 0, 8910, -206, 0, + 1737, 1744, 0, 0, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 479,17919, 0, 0, 0, 0, 4351, 0, + 0, 0, 0, 0, 0, 0, 0,18191, 0, 0, + 0, 0, 0, 0, 0, 868, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 233, 1047, 0, 0, 230, + 1928, 0, 0, 1378, 513, 0, 0, 0, 0, 215, + 0, 0, 4831, 1376, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1384, 0, 2553, + 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1372, 0, 0, 0,17370, 4351, + 0, 7940, 0, 225, 0, 0, 0, 0, 0, 0, +17370, 0, 0, 0, 0, 0, 0, 60, 0, 703, + 0, 0, 0, 1381, 0, 0, 0, 0, 1366, 0, + 0, 0, 0, 4191, 0, 4351, 0, 0, 4030, 0, + 4351, 4991, 0, 0, 0, 0, 0, -202, 0, 0, + 0, 0, 313, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5535, 5640, 6142, 5965, 0, 6247, 0, 6319, 0, 0, + 6469, 0, 6554, 0, 6692, 0, 6761, 0, 6830, 0, + 6899, 0, 7117, 0, 7186, 0, 7324, 0, 7462, 0, + 7600, 0, 7738, 0, 7876, 0, 0, 749, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1852, 0, 0, 0, 0, 1531, 0, 0, 0, 0, + 0,11035, 0, 0, 817, 0, 0, 1339,15851, 0, + 0, 0, 0, 0, 0, 0, 781, 774, 0, 0, + 1385, 0, 0, 0, 0, 1820, 0, 0, 0, 0, + 0, 0,11492, 0, 0, 0, 845, 0, 0, 0, +11193,18364, 0, 0, 881, 920, 937, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 870, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1386, 0, 0, 0, + 0, 0, 6961, 0, 0, 0, 261, 0, 117, 4511, + 0, 0, 0, 0, 0, 0, 0, 1387, 0, 0, + 0, 0, 0, 1388, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +17370, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4351, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 674, 0, 0, 0, 0, 0, + 0, 0, 0, -169, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0,11193, 0, 0, 0, 0, -206, + 0, 9345, 0, 0, 1391, 0, 873, 0, 0, 0, + 0, 1395, 0, 1346, 1348, 0, 0, 0, 0, 0, + 1382,11351, 0, 0, 0, 0,18396, 0, 0, 0, + 939, 0, 0, 0, 0, 0, 0, 2721, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4671, 0, 5151, 1399, 0, 0, 0, + 0, 1396, 0, 0, 0, 939, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 771, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 940, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1394, 0, 0, 0, 0, 0, 957, 960, 0, 0, + 0, 0, 0, 0, 0, 1401, 761, 1400, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4831, 0, 0, 0, 0, 0, 1403, 0, 0, + 0, 1401, 0, 0, 0,17370, 0, 752, 762, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1385, 0,15689, 0, 0, 0, + 0, 0,18521, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 793, 0, 798, 0, 0, 0, + 0, 1402, 0, 895, 1404, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1407, 0, 0, 0, + 0, 202, 0, 0,17370, 0, 0, 0, 0, 0, + 0, 0, 113, 678, 0, 0, 0, 0, 0,18592, +18191, 0, 386, 511, 449, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -208, 0, 0, 1020, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,18748, 0, -253, +18191, 0, 522, 1410, 0, 1410, 0, 511, 0, 0, + 0, 0, 0, 0, 1406, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,18791, 0, 0, 0,16125, + 0, 0, 1411, 0, 0, 0, 364, 0, 532, 0, + 0, 606, 0, 0, 1410, 0, 0, 0, 0, 0, + 1409, 0, 0, 0, 0, 0, 0, 0, 3479, 1412, + 493, 0, 0, 333, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1414, 0, 0, 0, 0, 0, + 0, 0, 0, 3363, 0, 0, 1376, 0, 0,15957, +16209, 0, 0, 0, 767, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 617, 0, 0, 0,18091, + 0, 0,16041, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5152, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,16293, + 0, 0, 0, 0, 0, 767, 0, 0, 0, 0, + 0, 198, 479, 0, 0, 0, 0, 0, 0, 479, + 0, 0,15957, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4830, 507, 0,16335, 0, 0, 0, + 4990, 0, 3363, 0, 0, 0, 0, 0, 0, 0, + 3363, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 257, 0, 584, 0, 647, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 824, 0, 692, 0, 0, 0, 0, 0, + 0, 0,18191, 947, 0, 0, 0, 0, 0, 0, + 0, 1390, 0, 438, 0, 0, 0, 3363, 0, 0, + 963, 0, 0, 0, 0, 0, 0, 0, 0, 1417, + 0,18191,18191, 0, 0,18259, 0, 0, 0, 0, + 0, 0, 0, 0, 1418,15719, 0, 1420,18191,17507, + 1425,18191, 0, 0, 0, 0, 0, 0, 1426, 0, + 0, 0,18984, 0, 0, 0,18191, 0, 0, 0, + 1427, 0, 0, 424, 0,18914,18954, 0, 0, 0, + 1428, 0, 0, 0, 0, 0, 0, 1430, 0, 0, +18191, 0, 716, 0, 968, 0, 0, 0, 0, 0, + 1029, 0,18834,18874, 0, 0, 0, 0, 0, 0, + 0, 0, 1469, 0, 1525, 0, 0, 0, 978, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,18984, + 0, 0,18634,18672, 0, 611, 0, 0, 0, 0, + 0,15498, 0, 0, 0, 0, 0, 0, 0, 1376, + 1376, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + protected static readonly short [] yyGindex = { 0, + 0, 1757, 0, 0, 0, -1, -16, -178, -44, -41, + 0, 1797, 1806, 103, 0, 6, 0, 0, 0, 0, + 0, 0,-1157, -774, -216, -708, 0, 0, 0, 0, + 0, -221, 0, 0, 0, 776, 0, 883, 0, 0, + 0, 0, 630, 632, -17, -227, 0, -47, 0, -112, + 430, 0, 487, -587, -577, -576, -541, -537, -525, -516, + -503, 0, 0,-1152, 0,-1187, 0, 434,-1258, 0, + 3, 0, 0, 0, 596,-1183, 0, 0, 0, -3, + 267, 0, 0, 0, 306,-1151, 0, -281, -301, -354, + 0, 0, 0, -985, 262, 0, 0, -529, 0, 0, + 320, 0, 0, 299, 0, 0, 401, 0, -630, -856, + 0, 0, 0, 0, 0, -404, 335,-1396, -10, 0, + 0, 0, 893, 898, 900, 1089, -572, 0, 0, -336, + 902, 436, 0, -905, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 234, 0, 0, + 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, + -315, 423, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 505, 0, -531, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 246, 0, 0, 331, 0, 0, 336, + 340, 266, 0, 0, 0, 0, 0, 0, 0, 0, + 598, 0, 0, 0, 0, -86, 0, 135, -373, -304, + 1277, 0, 421, 0, -349, 0, 966, 0, 1577, 148, + -305, -274, -81, 203, 1232, 0, 601, 0, -21, 509, + 1495, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -267, + 0, 28, 0, -352, 0, -280, 0, 0, 0, 931, + -927, -313, -132, 554, 0, 1026, 0, 1276, -343, 1160, + 0, 0, 828, 1851, 0, 0, 0, 0, 1137, 0, + 0, 0, 1593, 0, 0, 0, 0, 0, 1627, 986, + 987, 0, 946, 0, 812, 984, 1514, 1515, 1513, 1516, + 1517, 0, 1512, 0, 0, 0, 1084, 1364, -569, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -306, + 748, 0, -444, 0, 0, 0, 0, 0, -475, 0, + 681, 0, 573, 0, 0, 0, 0, 0, 787, -563, + -11, -334, -7, 0, 1796, 0, 48, 0, 85, 91, + 111, 128, 157, 173, 175, 181, 193, 194, 0, -662, + 0, -23, 0, 0, 882, 0, 804, 0, 0, 0, + 0, 784, -959, 862, -931, 0, 905, -489, 0, 0, + 0, 0, 0, 0, 800, 0, 802, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 724, 0, 0, 0, 0, 0, 0, 0, + 0, -31, 0, 1416, 763, 0, 0, 985, 0, 0, + 0, 0, 0, 0, -176, 0, 0, 0, 0, 0, + 1529, 1283, 0, 0, 0, 0, 1532, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 607, 0, 0, 0, + 0, 0, 0, 0, 0, 731, 0, 0, 0, 0, + 0, 0, 35, 1054, 0, 0, 0, 1062, + }; + protected static readonly short [] yyTable = { 110, + 550, 193, 18, 547, 564, 159, 112, 238, 44, 160, + 239, 622, 817, 519, 459, 458, 823, 497, 761, 597, + 476, 299, 572, 545, 501, 196, 783, 580, 432, 543, + 861, 862, 397, 1108, 531, 389, 265, 398, 264, 866, + 332, 339, 612, 643, 1240, 346, 318, 1086, 257, 988, + 254, 634, 1274, 710, 1163, 1054, 310, 873, 1164, 886, + 317, 577, 744, 613, 164, 385, 1164, 829, 14, 319, + 933, 322, 254, 1495, 321, 903, 20, 1261, 355, 232, + 623, 1383, 234, 6, 333, 340, 1038, 51, 745, 268, + 727, 1303, 1113, 292, 293, 294, 858, 300, 301, 51, + 1390, 165, 314, 315, 194, 381, 1311, 166, 320, 323, + 754, 325, 1642, 329, 321, 340, 1152, 1324, 342, 343, + 746, 558, 386, 201, 472, 762, 1110, 167, 434, 968, + 1146, 969, 1450, 462, 1111, 1452, 463, 1458, 833, 863, + 788, 1261, 1152, 110, 168, 396, 1144, 255, 238, 159, + 112, 460, 326, 160, 198, 201, 1330, 859, 922, 728, + 886, 709, 539, 857, 115, 51, 255, 501, 1101, 1038, + 710, 200, 710, 169, 1038, 995, 1038, 295, 997, 1038, + 1038, 864, 1038, 1038, 95, 296, 95, 1, 255, 170, + 255, 171, 475, 1503, 1398, 460, 256, 172, 901, 465, + 466, 904, 1621, 499, 502, 1038, 373, 115, 164, 173, + 174, 115, 51, 1165, 886, 256, 856, 506, 43, 116, + 1113, 1165, 198, 198, 95, 747, 809, 710, 266, 374, + 255, 15, 1207, 1643, 500, 473, 375, 256, 497, 256, + 505, 815, 476, 198, 573, 165, 571, 297, 612, 574, + 372, 166, 875, 297, 530, 1446, 389, 879, 881, 597, + 1038, 467, 116, 1448, 507, 970, 116, 195, 515, 613, + 264, 167, 297, 755, 540, 1571, 541, 518, 255, 256, + 264, 1003, 522, 524, 877, 576, 597, 323, 168, 2, + 517, 396, 579, 1147, 1075, 1451, 613, 554, 1453, 373, + 1459, 570, 1595, 298, 590, 567, 379, 569, 115, 298, + 553, 522, 1054, 568, 1605, 393, 1606, 169, 902, 1504, + 1502, 1338, 374, 1308, 582, 583, 880, 256, 298, 375, + 542, 544, 1247, 170, 1229, 171, 376, 198, 198, 626, + 6, 172, 596, 631, 587, 588, 598, 502, 502, 267, + 1186, 620, 920, 173, 174, 732, 3, 4, 5, 6, + 732, 1318, 737, 116, 732, 619, 930, 791, 1041, 1589, + 603, 882, 1054, 344, 1265, 977, 1567, 500, 640, 732, + 1065, 647, 648, 649, 650, 651, 652, 653, 654, 655, + 656, 657, 264, 711, 713, 923, 955, 717, 1466, 900, + 501, 892, 198, 373, 980, 1533, 732, 1560, 931, 665, + 1616, 510, 297, 707, 238, 613, 1428, 460, 1159, 769, + 1296, 921, 393, 793, 738, 732, 374, 739, 198, 825, + 393, 1426, 393, 1339, 393, 236, 978, 712, 714, 115, + 198, 564, 1639, 852, 1309, 16, 1118, 827, 198, 382, + 837, 1041, 1248, 549, 198, 837, 1041, 1096, 1041, 297, + 840, 1041, 1041, 1612, 1041, 1041, 725, 781, 298, 789, + 413, 1613, 115, 960, 1073, 1079, 236, 1473, 393, 748, + 1427, 1319, 198, 198, 792, 1054, 883, 1041, 297, 1590, + 765, 1054, 782, 961, 116, 511, 502, 383, 779, 729, + 730, 115, 1009, 837, 770, 742, 414, 49, 198, 255, + 236, 966, 369, 198, 1467, 298, 345, 497, 2, 236, + 893, 236, 839, 981, 812, 119, 640, 116, 821, 531, + 612, 757, 1614, 236, 1012, 758, 1176, 501, 780, 1297, + 794, 1119, 1041, 1246, 624, 1151, 826, 198, 832, 967, + 497, 613, 1250, 203, 625, 95, 116, 842, 256, 384, + 844, 669, 379, 865, 828, 837, 780, 838, 119, 961, + 961, 1474, 119, 437, 372, 198, 198, 841, 853, 876, + 502, 1277, 596, 475, 896, 830, 598, 415, 416, 897, + 853, 1074, 20, 781, 386, 198, 759, 906, 540, 369, + 827, 837, 911, 912, 540, 1216, 1488, 837, 198, 596, + 500, 1341, 1360, 598, 837, 1063, 670, 462, 884, 884, + 919, 297, 599, 373, 369, 898, 438, 780, 369, 476, + 364, 126, 439, 126, 1341, 1521, 1522, 613, 126, 1524, + 386, 1323, 889, 512, 475, 379, 374, 861, 1058, 379, + 489, 513, 1543, 375, 781, 1550, 827, 1597, 1598, 540, + 376, 1360, 837, 115, 908, 1014, 910, 809, 228, 119, + 1566, 1602, 369, 439, 379, 918, 982, 629, 379, 600, + 379, 379, 379, 379, 1341, 490, 732, 630, 379, 627, + 476, 250, 812, 379, 1588, 251, 836, 812, 812, 890, + 914, 342, 753, 732, 514, 1095, 943, 489, 732, 1091, + 1085, 1067, 732, 550, 1633, 440, 985, 502, 116, 198, + 441, 55, 442, 1603, 719, 443, 444, 732, 445, 446, + 944, 753, 1429, 1361, 962, 753, 423, 424, 732, 628, + 236, 115, 490, 1362, 1363, 252, 198, 500, 1235, 530, + 730, 51, 204, 719, 732, 783, 440, 945, 439, 364, + 942, 441, 522, 442, 372, 364, 443, 444, 115, 445, + 446, 1155, 364, 732, 730, 264, 364, 821, 731, 1364, + 379, 1430, 1361, 1365, 812, 1331, 812, 716, 1269, 364, + 815, 605, 1362, 1363, 1454, 1366, 116, 228, 606, 231, + 119, 730, 731, 975, 1367, 379, 447, 719, 1468, 379, + 607, 364, 379, 373, 379, 1132, 716, 1368, 1006, 379, + 993, 364, 994, 116, 716, 998, 992, 999, 1364, 731, + 1486, 1032, 1365, 119, 597, 1055, 374, 1004, 1001, 502, + 853, 440, 1000, 375, 1366, 502, 441, 457, 442, 751, + 376, 443, 444, 1367, 445, 446, 990, 372, 1123, 1005, + 721, 345, 119, 390, 236, 198, 1368, 345, 95, 640, + 722, 255, 1093, 391, 346, 640, 597, 1415, 751, 373, + 1414, 535, 1208, 269, 1018, 738, 821, 535, 739, 721, + 440, 198, 392, 393, 1058, 441, 1187, 442, 461, 722, + 443, 444, 374, 445, 446, 1620, 373, 536, 373, 375, + 249, 537, 394, 1114, 1032, 1116, 376, 1243, 1121, 1032, + 256, 1032, 671, 395, 1032, 1032, 371, 1032, 1032, 374, + 812, 374, 518, 1034, 429, 386, 375, 1414, 375, 115, + 748, 115, 1047, 376, 898, 557, 430, 95, 756, 1080, + 1066, 371, 387, 755, 1082, 759, 1329, 1082, 558, 759, + 1276, 1132, 95, 761, 198, 326, 253, 812, 373, 821, + 597, 1415, 1099, 756, 1109, 559, 374, 302, 755, 303, + 198, 720, 756, 375, 759, 115, 1554, 755, 115, 208, + 386, 374, 946, 759, 116, 502, 116, 198, 375, 947, + 926, 198, 1179, 1439, 1181, 1032, 1182, 462, 812, 95, + 812, 1141, 927, 812, 203, 1439, 1034, 1145, 419, 420, + 326, 1034, 1028, 1034, 119, 1131, 1034, 1034, 1408, 1034, + 1034, 326, 421, 422, 605, 1420, 952, 341, 497, 476, + 116, 606, 847, 116, 1136, 748, 1378, 597, 1607, 1256, + 847, 1197, 1138, 607, 1139, 425, 1140, 821, 1443, 364, + 522, 364, 1465, 1198, 364, 364, 302, 1411, 302, 198, + 1443, 427, 364, 302, 1411, 683, 364, 683, 1107, 1465, + 893, 1283, 1284, 292, 292, 1629, 426, 198, 198, 364, + 1387, 228, 292, 233, 428, 853, 550, 812, 1497, 812, + 1498, 812, 119, 431, 1183, 1028, 1252, 1034, 1650, 1651, + 1028, 1188, 1028, 1189, 303, 1028, 1028, 844, 1028, 1028, + 864, 364, 464, 844, 864, 844, 864, 469, 864, 119, + 853, 1190, 498, 844, 853, 844, 853, 844, 853, 848, + 1287, 502, 1082, 848, 69, 69, 518, 848, 69, 417, + 418, 364, 364, 198, 364, 364, 62, 596, 764, 1217, + 821, 598, 765, 183, 853, 183, 549, 183, 196, 471, + 196, 1131, 196, 1228, 198, 799, 810, 344, 518, 800, + 537, 518, 198, 238, 352, 1259, 460, 1075, 1260, 1075, + 963, 369, 520, 1325, 964, 369, 1028, 364, 369, 596, + 369, 812, 521, 598, 984, 369, 562, 1255, 985, 496, + 236, 238, 1010, 1376, 460, 24, 765, 25, 1087, 855, + 26, 855, 985, 1231, 518, 27, 1058, 1232, 115, 28, + 685, 687, 689, 691, 399, 812, 1183, 45, 30, 369, + 911, 546, 915, 70, 911, 32, 915, 70, 117, 1259, + 33, 171, 1260, 171, 34, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 551, 36, 847, 37, 502, + 387, 847, 38, 1283, 1284, 1340, 1359, 555, 1260, 739, + 39, 40, 575, 116, 41, 1032, 1033, 563, 812, 552, + 178, 117, 178, 596, 556, 117, 199, 598, 1340, 640, + 119, 1260, 119, 410, 411, 412, 578, 179, 812, 179, + 518, 983, 72, 983, 72, 1335, 115, 1221, 1222, 338, + 338, 131, 586, 131, 602, 1359, 1394, 202, 739, 202, + 172, 617, 172, 24, 618, 25, 723, 308, 26, 308, + 338, 632, 138, 27, 138, 380, 119, 28, 1340, 119, + 1656, 1260, 315, 115, 315, 974, 30, 976, 115, 1292, + 236, 726, 115, 32, 199, 199, 198, 743, 33, 380, + 596, 116, 34, 766, 598, 1419, 374, 1335, 763, 374, + 469, 1617, 1618, 768, 36, 199, 37, 965, 965, 795, + 38, 115, 117, 732, 732, 1424, 1425, 790, 39, 40, + 676, 678, 41, 681, 683, 85, 693, 695, 116, 796, + 364, 797, 798, 116, 835, 836, 836, 116, 364, 843, + 364, 836, 845, 1457, 846, 847, 1460, 848, 836, 1477, + 867, 198, 868, 1419, 338, 338, 871, 872, 887, 364, + 364, 888, 874, 878, 907, 909, 116, 913, 924, 925, + 462, 115, 115, 198, 934, 935, 938, 43, 956, 364, + 200, 1534, 957, 950, 739, 958, 959, 364, 965, 983, + 364, 986, 893, 989, 987, 1002, 836, 1008, 1561, 199, + 199, 739, 1022, 1027, 1029, 1034, 1037, 380, 38, 1042, + 1045, 1573, 1575, 1043, 739, 739, 1048, 1046, 821, 338, + 1051, 1053, 1059, 1419, 1071, 1072, 116, 116, 1075, 1081, + 518, 1090, 540, 1104, 1097, 198, 1105, 198, 1561, 1561, + 1124, 739, 739, 117, 198, 338, 1134, 1583, 1148, 115, + 364, 1149, 1142, 198, 198, 1150, 198, 338, 1158, 1160, + 1161, 1171, 1442, 1172, 199, 338, 1177, 1174, 1178, 549, + 1180, 338, 1192, 1196, 1442, 1200, 117, 1199, 1201, 1442, + 821, 198, 1209, 1205, 198, 1202, 661, 1204, 1213, 1220, + 199, 1223, 1224, 1442, 1232, 1561, 1231, 1241, 739, 338, + 338, 829, 199, 1251, 116, 117, 1258, 1286, 1289, 119, + 199, 1270, 502, 502, 1295, 1442, 199, 821, 1291, 1294, + 1298, 1299, 1307, 1304, 1312, 338, 1314, 1328, 1320, 1321, + 338, 1329, 1635, 1635, 1372, 1374, 1373, 1381, 1431, 1644, + 1644, 1445, 640, 640, 199, 199, 1379, 1377, 1380, 1384, + 1391, 1396, 1449, 1462, 1655, 1471, 1463, 1472, 1480, 1474, + 1482, 1485, 1453, 1483, 338, 117, 1487, 1491, 1494, 661, + 199, 1493, 1414, 1489, 661, 199, 661, 661, 661, 661, + 661, 661, 661, 661, 661, 661, 661, 1500, 1501, 1507, + 1510, 1514, 338, 338, 1515, 1513, 1544, 119, 1525, 661, + 1517, 661, 1526, 661, 1556, 661, 661, 661, 1529, 199, + 1539, 1555, 1546, 1558, 1559, 1565, 1568, 1579, 1582, 1569, + 1580, 661, 1584, 1585, 1587, 1600, 1604, 1609, 1611, 1593, + 1608, 1619, 1603, 1602, 119, 1627, 1628, 199, 199, 119, + 353, 1647, 1649, 119, 357, 359, 361, 363, 365, 367, + 369, 371, 1653, 1654, 1658, 9, 1071, 199, 573, 950, + 661, 533, 951, 1063, 685, 38, 534, 117, 489, 38, + 199, 965, 119, 686, 532, 33, 33, 767, 857, 490, + 38, 34, 337, 364, 232, 38, 106, 34, 958, 38, + 858, 881, 38, 849, 850, 882, 916, 755, 918, 341, + 917, 920, 780, 732, 38, 38, 364, 732, 755, 38, + 38, 364, 364, 134, 116, 38, 311, 38, 38, 38, + 38, 141, 135, 117, 312, 38, 142, 235, 54, 38, + 21, 38, 119, 119, 364, 1125, 338, 1041, 1226, 24, + 1227, 38, 1422, 38, 38, 117, 38, 364, 364, 1385, + 38, 1423, 364, 1275, 1601, 364, 1570, 364, 1557, 364, + 364, 364, 364, 338, 1475, 1610, 1586, 364, 1552, 1068, + 38, 364, 117, 1064, 1069, 364, 1070, 38, 38, 953, + 1455, 199, 1389, 364, 1646, 1392, 364, 1476, 364, 364, + 1638, 1581, 1576, 364, 364, 1574, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 199, 1637, + 119, 1315, 364, 364, 1499, 1038, 565, 1316, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 672, 364, + 364, 1089, 886, 364, 364, 364, 364, 364, 1015, 1162, + 364, 364, 305, 477, 949, 364, 364, 364, 364, 364, + 364, 364, 364, 971, 353, 37, 584, 1100, 697, 701, + 699, 706, 1239, 703, 364, 705, 478, 364, 850, 364, + 1007, 364, 1301, 1397, 364, 1211, 433, 1203, 1154, 479, + 364, 1218, 338, 1173, 481, 1143, 1210, 581, 1281, 482, + 1212, 483, 484, 485, 486, 834, 1249, 353, 721, 487, + 1102, 722, 1395, 488, 1288, 1039, 1036, 1434, 338, 0, + 0, 0, 0, 0, 0, 489, 0, 0, 490, 0, + 491, 0, 0, 917, 0, 0, 0, 199, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 117, 492, 117, 36, 0, 0, 0, + 0, 1435, 0, 199, 0, 0, 659, 661, 663, 0, + 0, 581, 581, 581, 581, 581, 581, 581, 581, 581, + 581, 581, 581, 581, 581, 581, 581, 0, 0, 0, + 0, 338, 0, 0, 0, 0, 0, 0, 0, 117, + 0, 0, 117, 0, 0, 0, 24, 338, 0, 0, + 24, 1436, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 24, 0, 0, 338, 0, 24, 0, 338, 0, + 24, 0, 0, 24, 0, 0, 199, 1044, 0, 0, + 0, 0, 0, 0, 0, 24, 24, 0, 0, 0, + 24, 24, 199, 0, 0, 0, 24, 0, 24, 24, + 24, 24, 0, 0, 0, 0, 24, 0, 0, 199, + 24, 0, 24, 199, 0, 0, 0, 833, 0, 0, + 0, 0, 24, 0, 0, 24, 0, 24, 0, 0, + 0, 24, 0, 0, 0, 0, 581, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 24, 0, 0, 338, 338, 0, 21, 24, 24, + 1044, 0, 37, 0, 0, 1044, 37, 1044, 1044, 1044, + 1044, 1044, 1044, 1044, 1044, 1044, 1044, 37, 0, 0, + 0, 199, 37, 0, 0, 0, 37, 0, 0, 37, + 1044, 0, 1044, 0, 1044, 0, 1044, 1044, 1044, 199, + 199, 37, 37, 0, 0, 0, 37, 37, 0, 0, + 0, 0, 37, 870, 37, 37, 37, 37, 0, 0, + 338, 0, 37, 0, 0, 0, 37, 0, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, + 37, 37, 0, 37, 0, 0, 0, 37, 0, 338, + 0, 1044, 0, 36, 0, 0, 0, 36, 0, 0, + 0, 444, 0, 894, 895, 199, 541, 37, 36, 444, + 0, 444, 541, 36, 0, 37, 353, 36, 0, 0, + 36, 0, 117, 0, 0, 0, 199, 0, 0, 0, + 444, 444, 36, 36, 199, 0, 0, 36, 36, 0, + 0, 0, 0, 36, 0, 36, 36, 36, 36, 0, + 444, 0, 0, 36, 0, 0, 0, 36, 444, 36, + 0, 444, 0, 0, 0, 0, 0, 541, 664, 36, + 0, 0, 36, 0, 36, 0, 0, 24, 36, 25, + 0, 0, 26, 0, 0, 0, 0, 27, 0, 0, + 0, 28, 0, 545, 0, 0, 0, 0, 36, 0, + 30, 0, 0, 0, 0, 36, 36, 32, 0, 0, + 117, 0, 33, 833, 0, 0, 34, 0, 833, 833, + 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, + 37, 0, 0, 0, 38, 0, 0, 353, 0, 0, + 0, 833, 39, 40, 0, 0, 41, 117, 0, 85, + 0, 0, 117, 0, 833, 833, 117, 0, 0, 833, + 0, 0, 833, 0, 833, 0, 833, 833, 833, 833, + 0, 0, 0, 338, 833, 0, 0, 0, 833, 0, + 0, 0, 833, 0, 0, 117, 0, 0, 0, 0, + 833, 0, 0, 833, 0, 833, 833, 0, 0, 0, + 833, 833, 0, 833, 833, 833, 833, 833, 833, 833, + 833, 833, 833, 833, 0, 0, 0, 0, 199, 833, + 833, 0, 0, 0, 0, 833, 833, 833, 833, 833, + 833, 380, 833, 833, 833, 0, 833, 833, 338, 0, + 833, 833, 833, 833, 0, 117, 117, 833, 833, 0, + 0, 0, 833, 833, 833, 833, 833, 833, 833, 833, + 338, 0, 952, 24, 0, 25, 0, 0, 26, 0, + 1332, 833, 0, 27, 833, 0, 833, 28, 833, 0, + 0, 833, 0, 199, 0, 0, 30, 833, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 0, 33, 0, + 1333, 0, 34, 0, 0, 199, 0, 0, 0, 0, + 0, 0, 0, 0, 36, 0, 37, 581, 0, 0, + 38, 1334, 338, 117, 338, 0, 0, 0, 39, 40, + 0, 338, 41, 0, 0, 85, 0, 0, 0, 0, + 338, 338, 0, 338, 0, 0, 0, 0, 0, 545, + 0, 0, 0, 0, 545, 545, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 199, 338, 199, + 0, 338, 0, 0, 0, 0, 199, 545, 0, 0, + 0, 0, 0, 0, 0, 199, 199, 0, 199, 0, + 545, 545, 0, 0, 0, 545, 0, 0, 545, 0, + 545, 0, 545, 545, 545, 545, 0, 0, 0, 0, + 545, 0, 0, 199, 545, 0, 199, 380, 545, 0, + 0, 0, 0, 0, 0, 0, 545, 0, 0, 545, + 875, 545, 545, 0, 0, 0, 545, 545, 0, 545, + 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, + 0, 0, 0, 0, 0, 545, 545, 545, 0, 0, + 0, 545, 545, 0, 545, 545, 545, 545, 545, 545, + 545, 0, 545, 545, 0, 545, 545, 545, 545, 545, + 545, 545, 545, 545, 545, 0, 545, 545, 545, 545, + 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, + 545, 545, 545, 545, 545, 545, 545, 545, 0, 0, + 545, 0, 545, 0, 545, 0, 0, 545, 952, 952, + 0, 0, 0, 545, 0, 0, 952, 952, 952, 952, + 952, 0, 952, 952, 0, 952, 952, 952, 952, 952, + 952, 952, 952, 0, 0, 0, 0, 952, 0, 952, + 952, 952, 952, 952, 952, 0, 0, 952, 0, 0, + 354, 952, 952, 0, 952, 952, 952, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 952, 0, 952, 0, + 952, 952, 0, 0, 952, 0, 952, 952, 952, 952, + 952, 952, 952, 952, 952, 952, 952, 952, 0, 952, + 0, 0, 952, 952, 0, 0, 952, 952, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 952, 952, 952, 952, 952, 0, 0, 0, 0, + 952, 952, 0, 0, 952, 0, 0, 0, 0, 952, + 952, 952, 952, 952, 0, 0, 0, 952, 0, 952, + 0, 0, 0, 0, 0, 952, 952, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 952, 952, 952, 952, 0, 952, 875, 875, 0, 0, + 0, 394, 952, 0, 875, 875, 875, 875, 875, 0, + 875, 875, 0, 875, 875, 875, 875, 875, 875, 875, + 0, 0, 0, 0, 0, 875, 0, 875, 875, 875, + 875, 875, 875, 0, 0, 875, 0, 0, 0, 875, + 875, 0, 875, 875, 875, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 875, 0, 875, 0, 875, 875, + 0, 0, 875, 0, 875, 875, 875, 875, 875, 875, + 875, 875, 875, 875, 875, 875, 0, 875, 0, 0, + 875, 875, 0, 0, 875, 875, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 875, + 875, 875, 875, 875, 0, 0, 364, 0, 875, 875, + 0, 0, 875, 0, 0, 0, 0, 875, 875, 875, + 875, 875, 0, 0, 0, 875, 354, 875, 0, 0, + 0, 354, 354, 875, 875, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 354, 0, 0, 0, 875, 875, + 875, 875, 0, 875, 0, 0, 0, 354, 354, 0, + 875, 0, 354, 0, 0, 354, 0, 354, 0, 354, + 354, 354, 354, 0, 0, 0, 0, 354, 0, 0, + 0, 354, 0, 0, 0, 354, 0, 0, 0, 0, + 0, 0, 0, 354, 0, 0, 354, 0, 354, 354, + 0, 399, 0, 354, 354, 0, 354, 354, 354, 354, + 354, 354, 354, 354, 354, 354, 354, 0, 0, 0, + 0, 0, 354, 354, 0, 0, 0, 0, 354, 354, + 354, 354, 354, 354, 0, 354, 354, 354, 0, 354, + 354, 0, 0, 354, 354, 354, 354, 394, 0, 0, + 354, 354, 394, 394, 0, 354, 354, 354, 354, 354, + 354, 354, 354, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 354, 394, 0, 354, 0, 354, + 0, 354, 0, 0, 354, 0, 0, 0, 394, 394, + 354, 0, 0, 394, 0, 0, 394, 0, 394, 0, + 394, 394, 394, 394, 0, 0, 0, 0, 394, 0, + 0, 0, 394, 0, 0, 0, 394, 0, 0, 0, + 0, 0, 0, 0, 394, 0, 0, 394, 0, 394, + 394, 0, 0, 0, 394, 394, 0, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 0, 0, + 0, 0, 364, 394, 394, 0, 0, 0, 364, 394, + 394, 0, 394, 394, 394, 0, 394, 394, 394, 0, + 394, 394, 31, 0, 394, 394, 394, 394, 0, 0, + 0, 394, 394, 0, 0, 0, 394, 394, 394, 394, + 394, 394, 394, 394, 364, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 394, 0, 0, 394, 0, + 394, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 394, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 364, 0, 0, 0, 364, + 364, 0, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 0, 0, 0, 399, 0, 364, + 0, 0, 0, 399, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 0, 364, 364, 0, 0, 364, + 364, 364, 364, 364, 0, 0, 364, 364, 25, 0, + 0, 364, 364, 364, 364, 364, 364, 364, 364, 399, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 364, 0, 0, 364, 0, 364, 0, 364, 0, 0, + 364, 0, 0, 0, 0, 0, 364, 0, 0, 0, + 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, + 399, 0, 0, 0, 399, 399, 0, 399, 399, 399, + 399, 399, 399, 399, 399, 399, 399, 399, 0, 0, + 0, 0, 0, 0, 399, 0, 0, 0, 0, 399, + 399, 399, 399, 399, 399, 0, 399, 399, 399, 0, + 399, 399, 0, 0, 399, 399, 399, 399, 0, 0, + 0, 399, 399, 0, 0, 0, 399, 399, 399, 399, + 399, 399, 399, 399, 0, 0, 0, 0, 0, 0, + 0, 5, 0, 0, 0, 399, 0, 0, 399, 0, + 399, 0, 399, 0, 0, 399, 0, 0, 31, 31, + 0, 399, 0, 31, 0, 0, 0, 31, 0, 31, + 0, 0, 31, 0, 31, 31, 0, 31, 0, 31, + 0, 31, 0, 31, 31, 31, 31, 0, 1052, 31, + 31, 0, 0, 0, 0, 31, 0, 31, 31, 31, + 0, 0, 31, 31, 31, 0, 31, 0, 0, 31, + 0, 31, 31, 31, 31, 0, 0, 0, 31, 31, + 31, 0, 0, 31, 31, 31, 0, 0, 0, 0, + 0, 0, 31, 31, 0, 31, 31, 0, 31, 31, + 31, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 31, 31, 31, 0, 0, 25, 0, 0, 0, 25, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 0, 0, 0, 7, 25, 0, 0, 0, 25, + 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 25, 25, 0, 0, 35, 25, + 25, 31, 35, 0, 0, 25, 0, 25, 25, 25, + 25, 0, 0, 35, 0, 25, 0, 1053, 35, 25, + 0, 25, 35, 0, 0, 35, 0, 0, 0, 0, + 0, 25, 0, 0, 25, 0, 25, 35, 35, 0, + 25, 0, 35, 35, 0, 0, 0, 0, 35, 0, + 35, 35, 35, 35, 0, 0, 0, 0, 35, 0, + 25, 0, 35, 0, 35, 0, 0, 25, 25, 0, + 0, 0, 0, 0, 35, 0, 0, 35, 5, 35, + 52, 0, 51, 35, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 0, 0, 0, 0, 51, 0, + 0, 0, 51, 35, 0, 51, 0, 0, 0, 0, + 0, 35, 0, 0, 0, 0, 0, 51, 51, 0, + 0, 0, 51, 51, 0, 1052, 0, 0, 51, 51, + 51, 51, 51, 51, 0, 0, 0, 0, 51, 0, + 51, 0, 51, 0, 51, 51, 0, 0, 0, 51, + 0, 0, 51, 0, 51, 0, 0, 51, 0, 51, + 0, 0, 0, 51, 51, 51, 0, 0, 0, 51, + 51, 0, 0, 0, 0, 51, 0, 51, 51, 51, + 51, 0, 0, 51, 0, 51, 0, 0, 51, 51, + 0, 51, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 51, 0, 51, 51, 0, 51, 0, 51, 0, + 51, 0, 51, 0, 0, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 51, 0, + 51, 7, 51, 51, 0, 52, 0, 0, 51, 0, + 51, 51, 51, 51, 0, 0, 52, 0, 51, 0, + 0, 52, 51, 0, 51, 52, 0, 0, 52, 0, + 0, 0, 0, 0, 51, 0, 0, 51, 0, 51, + 52, 52, 0, 51, 1053, 52, 52, 0, 51, 0, + 0, 52, 0, 52, 52, 52, 52, 0, 0, 51, + 0, 52, 0, 51, 51, 52, 0, 52, 51, 0, + 0, 51, 0, 0, 0, 0, 0, 52, 0, 0, + 52, 0, 52, 51, 51, 0, 52, 0, 51, 51, + 0, 0, 0, 0, 51, 0, 51, 51, 51, 51, + 0, 0, 0, 0, 51, 0, 52, 52, 51, 0, + 51, 52, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 0, 52, 51, 0, 51, 0, 52, 0, 51, + 0, 52, 0, 0, 52, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 52, 52, 0, 51, + 0, 52, 52, 0, 0, 0, 0, 52, 0, 52, + 52, 52, 52, 0, 0, 0, 0, 52, 0, 0, + 0, 52, 0, 52, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 52, 0, 56, 52, 0, 52, 0, + 0, 0, 52, 57, 24, 58, 25, 0, 0, 26, + 59, 0, 60, 61, 27, 62, 63, 64, 28, 0, + 0, 0, 52, 0, 65, 0, 66, 30, 67, 68, + 69, 70, 0, 0, 32, 0, 0, 0, 71, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 74, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 76, 77, 78, 79, 80, 81, 39, + 40, 82, 83, 41, 84, 0, 85, 0, 0, 86, + 87, 0, 0, 88, 89, 836, 0, 0, 0, 477, + 0, 836, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 93, 94, 0, 0, 0, 0, 95, 0, 0, + 0, 96, 478, 0, 0, 0, 97, 98, 99, 100, + 101, 0, 0, 0, 102, 479, 103, 836, 0, 0, + 481, 0, 104, 105, 0, 482, 0, 483, 484, 485, + 486, 0, 0, 0, 0, 487, 0, 0, 0, 488, + 0, 0, 0, 1434, 0, 0, 0, 106, 107, 108, + 109, 489, 0, 0, 490, 0, 491, 0, 836, 200, + 0, 0, 0, 836, 627, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 0, 0, 0, + 492, 0, 836, 836, 0, 0, 0, 0, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 0, 836, 836, + 0, 836, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 0, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 0, 836, 1436, 836, 0, + 836, 836, 836, 836, 0, 0, 0, 627, 0, 836, + 0, 0, 627, 0, 627, 627, 627, 627, 627, 627, + 627, 627, 627, 627, 627, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 627, 836, 627, + 0, 627, 0, 627, 627, 627, 0, 0, 0, 0, + 0, 627, 627, 627, 627, 0, 0, 0, 627, 627, + 0, 0, 0, 627, 627, 627, 627, 627, 627, 627, + 627, 0, 0, 0, 0, 0, 0, 0, 0, 836, + 0, 0, 627, 0, 836, 0, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 836, 627, 0, + 0, 0, 0, 836, 836, 0, 0, 0, 0, 836, + 836, 836, 836, 836, 836, 836, 836, 836, 0, 836, + 836, 0, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 0, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 364, 991, 0, 836, + 0, 836, 364, 0, 836, 0, 24, 0, 25, 0, + 836, 26, 0, 0, 0, 0, 27, 0, 0, 0, + 28, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 364, 0, + 0, 33, 0, 0, 0, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, + 0, 39, 40, 0, 0, 41, 0, 0, 85, 364, + 0, 0, 0, 0, 364, 0, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 0, 0, + 0, 0, 0, 364, 364, 0, 0, 0, 0, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 0, 364, + 364, 0, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 0, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 0, 547, 1135, 0, 364, + 380, 364, 547, 0, 364, 0, 24, 0, 25, 0, + 364, 26, 0, 0, 0, 0, 27, 0, 0, 0, + 28, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 547, 0, + 0, 33, 0, 0, 0, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, + 0, 39, 40, 0, 0, 41, 0, 0, 85, 547, + 0, 0, 0, 0, 547, 0, 547, 547, 547, 547, + 547, 547, 547, 547, 547, 547, 547, 0, 0, 0, + 0, 0, 0, 547, 547, 0, 0, 0, 547, 547, + 547, 547, 547, 547, 547, 547, 547, 547, 0, 547, + 547, 0, 547, 547, 547, 547, 547, 547, 547, 547, + 547, 547, 0, 547, 547, 547, 547, 547, 547, 547, + 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, + 547, 547, 547, 547, 547, 0, 543, 0, 0, 547, + 380, 547, 543, 0, 0, 477, 0, 0, 0, 0, + 547, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 478, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 543, 0, + 0, 479, 0, 0, 0, 0, 481, 0, 0, 0, + 0, 482, 0, 483, 484, 485, 486, 0, 0, 0, + 0, 487, 0, 0, 0, 488, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 489, 0, 543, + 490, 0, 491, 0, 543, 0, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 0, 0, 0, + 0, 0, 0, 543, 543, 0, 492, 0, 543, 543, + 0, 543, 543, 543, 543, 543, 543, 543, 0, 543, + 543, 0, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 0, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 0, 551, 0, 0, 543, + 386, 543, 551, 1453, 543, 0, 0, 0, 0, 0, + 543, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 386, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 551, 0, + 0, 386, 0, 0, 254, 0, 386, 0, 386, 386, + 386, 386, 0, 0, 0, 0, 386, 0, 0, 0, + 386, 0, 0, 0, 386, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 386, 0, 386, 0, 551, + 0, 0, 0, 0, 551, 0, 551, 551, 551, 551, + 551, 551, 551, 551, 551, 551, 551, 0, 0, 0, + 0, 386, 0, 551, 551, 0, 0, 0, 386, 551, + 0, 551, 551, 551, 551, 551, 551, 551, 0, 551, + 551, 0, 551, 551, 551, 551, 551, 551, 551, 551, + 551, 551, 0, 551, 551, 551, 551, 551, 551, 551, + 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, + 551, 551, 551, 551, 551, 0, 364, 0, 386, 551, + 385, 551, 364, 0, 551, 0, 0, 0, 0, 0, + 551, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 385, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 385, 0, 364, 0, + 0, 385, 0, 0, 253, 0, 385, 0, 385, 385, + 385, 385, 0, 0, 0, 0, 385, 0, 0, 0, + 385, 0, 0, 0, 385, 0, 0, 0, 0, 0, + 0, 0, 385, 0, 0, 385, 0, 385, 0, 364, + 0, 0, 0, 0, 364, 0, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 0, 0, 0, + 0, 385, 0, 364, 364, 0, 0, 0, 385, 364, + 0, 364, 364, 364, 364, 364, 364, 364, 0, 364, + 364, 0, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 0, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 0, 471, 0, 385, 364, + 0, 364, 471, 0, 364, 0, 732, 0, 732, 0, + 364, 732, 0, 732, 732, 0, 732, 0, 732, 0, + 732, 0, 732, 732, 732, 0, 0, 0, 732, 732, + 0, 0, 0, 0, 732, 0, 732, 732, 471, 0, + 0, 732, 0, 0, 0, 732, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 732, 0, 732, + 0, 0, 0, 732, 732, 0, 0, 0, 0, 0, + 0, 732, 732, 0, 0, 732, 0, 0, 732, 471, + 0, 0, 0, 732, 471, 0, 471, 471, 471, 471, + 471, 471, 471, 471, 471, 471, 471, 0, 0, 0, + 0, 0, 0, 471, 471, 0, 0, 0, 0, 471, + 0, 471, 471, 471, 471, 471, 471, 471, 0, 471, + 471, 0, 471, 471, 471, 471, 471, 471, 471, 471, + 471, 471, 0, 471, 471, 471, 471, 471, 471, 471, + 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, + 471, 471, 471, 471, 471, 0, 578, 0, 0, 471, + 732, 471, 578, 0, 471, 0, 24, 0, 25, 0, + 471, 26, 0, 0, 1399, 0, 27, 0, 757, 0, + 28, 0, 758, 1400, 1401, 0, 0, 0, 1402, 30, + 0, 0, 0, 0, 1403, 0, 32, 0, 578, 0, + 0, 33, 0, 0, 0, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 364, 0, 0, 38, 0, 0, 364, 0, 0, 0, + 836, 39, 40, 0, 0, 41, 0, 0, 1404, 578, + 0, 0, 0, 1405, 578, 0, 578, 578, 578, 578, + 578, 578, 578, 578, 578, 578, 578, 0, 0, 0, + 0, 0, 364, 578, 0, 0, 0, 0, 0, 578, + 0, 578, 0, 578, 0, 578, 578, 578, 836, 578, + 578, 0, 578, 578, 578, 578, 578, 578, 578, 578, + 578, 578, 0, 0, 0, 578, 578, 578, 578, 578, + 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, + 578, 578, 578, 0, 578, 663, 0, 0, 0, 578, + 1407, 364, 0, 0, 0, 0, 0, 364, 364, 0, + 578, 0, 0, 364, 364, 364, 364, 364, 364, 364, + 836, 364, 0, 364, 364, 205, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 0, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 0, + 354, 0, 0, 364, 0, 364, 354, 206, 364, 0, + 0, 0, 0, 0, 364, 0, 0, 0, 663, 0, + 0, 0, 0, 663, 0, 663, 663, 663, 663, 663, + 663, 663, 663, 663, 663, 663, 0, 0, 0, 0, + 0, 0, 354, 0, 0, 0, 0, 0, 663, 0, + 663, 0, 663, 0, 663, 663, 663, 0, 207, 208, + 209, 210, 0, 211, 212, 213, 214, 215, 216, 217, + 218, 0, 0, 219, 220, 221, 222, 223, 224, 225, + 226, 0, 0, 354, 0, 0, 0, 0, 354, 0, + 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, + 354, 0, 0, 0, 0, 620, 0, 354, 422, 663, + 0, 620, 0, 354, 354, 354, 422, 354, 422, 354, + 354, 354, 0, 354, 354, 0, 0, 354, 354, 354, + 354, 0, 0, 0, 354, 354, 0, 422, 422, 354, + 354, 354, 354, 354, 354, 354, 354, 620, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 422, 354, 0, + 0, 0, 0, 354, 422, 422, 0, 0, 422, 0, + 422, 0, 0, 0, 354, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 620, 0, + 0, 0, 0, 620, 0, 620, 620, 620, 620, 620, + 620, 620, 620, 620, 620, 620, 422, 0, 0, 0, + 0, 0, 620, 423, 0, 0, 0, 0, 620, 0, + 620, 423, 620, 423, 620, 620, 620, 0, 620, 620, + 0, 0, 620, 620, 620, 620, 0, 0, 0, 620, + 620, 0, 423, 423, 620, 620, 620, 620, 620, 620, + 620, 620, 0, 354, 0, 0, 0, 0, 0, 354, + 0, 0, 423, 620, 0, 0, 0, 0, 620, 0, + 423, 354, 422, 423, 0, 0, 0, 422, 354, 620, + 422, 422, 422, 422, 0, 422, 0, 422, 422, 0, + 422, 422, 422, 422, 422, 354, 422, 422, 422, 422, + 0, 422, 422, 422, 422, 422, 422, 422, 422, 422, + 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, + 422, 422, 422, 0, 0, 0, 0, 354, 0, 422, + 0, 0, 422, 0, 0, 0, 354, 0, 422, 0, + 0, 354, 0, 354, 354, 354, 354, 354, 354, 354, + 354, 354, 354, 354, 0, 0, 0, 0, 600, 0, + 354, 0, 0, 0, 600, 0, 354, 354, 354, 0, + 354, 0, 354, 354, 354, 0, 354, 354, 0, 0, + 354, 354, 354, 354, 0, 0, 0, 354, 354, 0, + 0, 0, 354, 354, 354, 354, 354, 354, 354, 354, + 600, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 354, 0, 0, 0, 0, 354, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 354, 0, 0, + 618, 0, 0, 0, 0, 0, 618, 0, 0, 0, + 0, 600, 0, 0, 0, 0, 600, 0, 600, 600, + 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 600, 618, 600, 0, 600, 0, 600, 600, 600, + 0, 600, 600, 0, 0, 600, 600, 600, 600, 600, + 600, 600, 600, 600, 0, 0, 0, 600, 600, 600, + 600, 600, 600, 600, 600, 0, 0, 0, 0, 0, + 0, 0, 0, 618, 0, 0, 600, 0, 618, 0, + 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, + 618, 0, 600, 0, 0, 607, 0, 618, 0, 0, + 0, 607, 0, 618, 0, 618, 0, 618, 0, 618, + 618, 618, 0, 618, 618, 0, 0, 618, 618, 618, + 618, 0, 0, 0, 618, 618, 0, 0, 0, 618, + 618, 618, 618, 618, 618, 618, 618, 607, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 618, 0, + 0, 0, 0, 618, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 618, 0, 0, 958, 0, 0, + 0, 0, 0, 958, 0, 0, 0, 0, 607, 0, + 0, 0, 0, 607, 0, 607, 607, 607, 607, 607, + 607, 607, 607, 607, 607, 607, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 607, 958, + 607, 0, 607, 0, 607, 607, 607, 0, 607, 607, + 0, 0, 607, 607, 607, 607, 0, 0, 0, 607, + 607, 0, 0, 0, 607, 607, 607, 607, 607, 607, + 607, 607, 0, 0, 0, 0, 0, 0, 0, 0, + 958, 0, 0, 607, 0, 958, 0, 958, 958, 958, + 958, 958, 958, 958, 958, 958, 958, 958, 0, 607, + 0, 0, 608, 0, 0, 0, 0, 0, 608, 0, + 958, 0, 958, 0, 958, 0, 958, 958, 958, 0, + 958, 958, 0, 0, 958, 958, 958, 958, 0, 0, + 0, 958, 958, 0, 0, 0, 958, 958, 958, 958, + 958, 958, 958, 958, 608, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 958, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 958, 0, 0, 609, 0, 0, 0, 0, 0, + 609, 0, 0, 0, 0, 608, 0, 0, 0, 0, + 608, 0, 608, 608, 608, 608, 608, 608, 608, 608, + 608, 608, 608, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 608, 609, 608, 0, 608, + 0, 608, 608, 608, 0, 608, 608, 0, 0, 608, + 608, 608, 608, 0, 0, 0, 608, 608, 0, 0, + 0, 608, 608, 608, 608, 608, 608, 608, 608, 0, + 0, 0, 0, 0, 0, 0, 0, 609, 0, 0, + 608, 0, 609, 0, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 0, 608, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 609, 0, 609, + 0, 609, 0, 609, 609, 609, 0, 609, 609, 0, + 0, 609, 609, 609, 609, 0, 0, 0, 609, 609, + 0, 0, 0, 609, 609, 609, 609, 609, 609, 609, + 609, 0, 525, 0, 628, 0, 0, 0, 0, 0, + 57, 24, 609, 25, 0, 0, 26, 259, 0, 0, + 0, 27, 62, 63, 0, 28, 0, 0, 609, 0, + 0, 65, 0, 0, 30, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 0, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 0, 77, 0, 79, 0, 81, 39, 40, 260, 0, + 41, 0, 0, 0, 0, 0, 0, 628, 0, 629, + 0, 0, 628, 0, 628, 628, 628, 628, 628, 628, + 628, 628, 628, 628, 628, 90, 91, 92, 261, 526, + 0, 0, 0, 0, 0, 0, 0, 628, 96, 628, + 0, 628, 0, 628, 628, 628, 0, 0, 0, 0, + 0, 628, 628, 628, 628, 0, 0, 0, 628, 628, + 0, 0, 0, 628, 628, 628, 628, 628, 628, 628, + 628, 0, 0, 0, 0, 0, 0, 0, 632, 0, + 0, 0, 628, 0, 106, 527, 0, 0, 0, 0, + 0, 0, 629, 0, 0, 528, 529, 629, 628, 629, + 629, 629, 629, 629, 629, 629, 629, 629, 629, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 629, 0, 629, 0, 629, 0, 629, 629, + 629, 0, 0, 0, 0, 0, 629, 629, 629, 629, + 0, 0, 0, 629, 629, 0, 0, 633, 629, 629, + 629, 629, 629, 629, 629, 629, 0, 0, 0, 0, + 0, 632, 0, 0, 0, 0, 632, 629, 632, 632, + 632, 632, 632, 632, 632, 632, 632, 632, 632, 0, + 0, 0, 0, 629, 0, 0, 0, 0, 0, 0, + 0, 632, 0, 632, 0, 632, 0, 632, 632, 632, + 0, 0, 0, 0, 0, 632, 632, 632, 632, 0, + 0, 0, 632, 632, 0, 0, 634, 0, 0, 632, + 632, 632, 632, 632, 632, 0, 0, 0, 0, 0, + 633, 0, 0, 0, 0, 633, 632, 633, 633, 633, + 633, 633, 633, 633, 633, 633, 633, 633, 0, 0, + 0, 0, 632, 0, 0, 0, 0, 0, 0, 0, + 633, 0, 633, 0, 633, 0, 633, 633, 633, 0, + 0, 0, 0, 0, 633, 633, 633, 633, 0, 0, + 0, 633, 633, 0, 0, 635, 0, 0, 633, 633, + 633, 633, 633, 633, 0, 0, 0, 0, 0, 634, + 0, 0, 0, 0, 634, 633, 634, 634, 634, 634, + 634, 634, 634, 634, 634, 634, 634, 0, 0, 0, + 0, 633, 0, 0, 0, 0, 0, 0, 0, 634, + 0, 634, 0, 634, 0, 634, 634, 634, 0, 0, + 0, 0, 0, 634, 634, 634, 634, 0, 0, 0, + 634, 634, 0, 0, 636, 0, 0, 634, 634, 634, + 634, 634, 634, 0, 0, 0, 0, 0, 635, 0, + 0, 0, 0, 635, 634, 635, 635, 635, 635, 635, + 635, 635, 635, 635, 635, 635, 0, 0, 0, 0, + 634, 0, 0, 0, 0, 0, 0, 0, 635, 0, + 635, 0, 635, 0, 635, 635, 635, 0, 0, 0, + 0, 0, 635, 635, 635, 635, 364, 0, 0, 635, + 635, 0, 364, 0, 0, 0, 635, 635, 635, 635, + 635, 635, 0, 0, 0, 0, 0, 636, 0, 0, + 0, 0, 636, 635, 636, 636, 636, 636, 636, 636, + 636, 636, 636, 636, 636, 0, 0, 0, 364, 635, + 0, 0, 0, 0, 0, 0, 0, 636, 0, 636, + 0, 636, 0, 636, 636, 636, 0, 0, 0, 0, + 0, 636, 636, 636, 636, 0, 0, 0, 636, 636, + 0, 0, 0, 0, 0, 636, 636, 636, 636, 636, + 636, 0, 0, 641, 0, 0, 0, 0, 0, 0, + 0, 0, 636, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 364, 0, 0, 0, 636, 364, + 0, 0, 364, 0, 364, 364, 0, 0, 0, 364, + 364, 0, 0, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 0, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 642, 364, 364, 0, 0, 0, 0, 0, + 0, 364, 0, 0, 364, 0, 641, 0, 0, 0, + 364, 641, 0, 641, 641, 641, 641, 641, 641, 641, + 641, 641, 641, 641, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 641, 0, 641, 0, + 641, 0, 641, 641, 641, 0, 0, 0, 0, 0, + 0, 0, 641, 641, 0, 0, 0, 641, 641, 0, + 0, 643, 0, 0, 0, 0, 641, 641, 641, 641, + 0, 0, 0, 0, 0, 642, 0, 0, 0, 0, + 642, 641, 642, 642, 642, 642, 642, 642, 642, 642, + 642, 642, 642, 0, 0, 0, 0, 641, 0, 0, + 0, 0, 0, 0, 0, 642, 0, 642, 0, 642, + 0, 642, 642, 642, 0, 0, 0, 0, 0, 0, + 0, 642, 642, 0, 0, 0, 642, 642, 0, 0, + 646, 0, 0, 0, 0, 642, 642, 642, 642, 0, + 0, 0, 0, 0, 643, 0, 0, 0, 0, 643, + 642, 643, 643, 643, 643, 643, 643, 643, 643, 643, + 643, 643, 0, 0, 0, 0, 642, 0, 0, 0, + 0, 0, 0, 0, 643, 0, 643, 0, 643, 0, + 643, 643, 643, 0, 0, 0, 0, 0, 0, 0, + 643, 643, 0, 0, 0, 643, 643, 0, 0, 647, + 0, 0, 0, 0, 643, 643, 643, 643, 0, 0, + 0, 0, 0, 646, 0, 0, 0, 0, 646, 643, + 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, + 646, 0, 0, 0, 0, 643, 0, 0, 0, 0, + 0, 0, 0, 646, 0, 646, 0, 646, 0, 646, + 646, 646, 0, 0, 0, 0, 0, 0, 0, 646, + 646, 0, 0, 0, 646, 646, 0, 0, 649, 0, + 0, 0, 0, 0, 0, 646, 646, 0, 0, 0, + 0, 0, 647, 0, 0, 0, 0, 647, 646, 647, + 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, + 0, 0, 0, 0, 646, 0, 0, 0, 0, 0, + 0, 0, 647, 0, 647, 0, 647, 0, 647, 647, + 647, 0, 0, 0, 0, 0, 0, 0, 647, 647, + 0, 0, 0, 647, 647, 0, 0, 650, 0, 0, + 0, 0, 0, 0, 647, 647, 0, 0, 0, 0, + 0, 649, 0, 0, 0, 0, 649, 647, 649, 649, + 649, 649, 649, 649, 649, 649, 649, 649, 649, 0, + 0, 0, 0, 647, 0, 0, 0, 0, 0, 0, + 0, 649, 0, 649, 0, 649, 0, 649, 649, 649, + 0, 0, 0, 0, 0, 0, 0, 0, 649, 0, + 0, 0, 649, 649, 0, 0, 652, 0, 0, 0, + 0, 0, 0, 649, 649, 0, 0, 0, 0, 0, + 650, 0, 0, 0, 0, 650, 649, 650, 650, 650, + 650, 650, 650, 650, 650, 650, 650, 650, 0, 0, + 0, 0, 649, 0, 0, 0, 0, 0, 0, 0, + 650, 0, 650, 0, 650, 0, 650, 650, 650, 0, + 0, 0, 0, 0, 0, 0, 0, 650, 0, 0, + 0, 650, 650, 0, 0, 653, 0, 0, 0, 0, + 0, 0, 650, 650, 0, 0, 0, 0, 0, 652, + 0, 0, 0, 0, 652, 650, 652, 652, 652, 652, + 652, 652, 652, 652, 652, 652, 652, 0, 0, 0, + 0, 650, 0, 0, 0, 0, 0, 0, 0, 652, + 0, 652, 0, 652, 0, 652, 652, 652, 0, 0, + 0, 0, 0, 0, 0, 0, 652, 0, 0, 0, + 0, 652, 0, 0, 655, 0, 0, 0, 0, 0, + 0, 652, 652, 0, 0, 0, 0, 0, 653, 0, + 0, 0, 0, 653, 652, 653, 653, 653, 653, 653, + 653, 653, 653, 653, 653, 653, 0, 0, 0, 0, + 652, 0, 0, 0, 0, 0, 0, 0, 653, 0, + 653, 0, 653, 0, 653, 653, 653, 0, 0, 0, + 0, 0, 0, 0, 0, 653, 0, 0, 0, 0, + 653, 0, 0, 656, 0, 0, 0, 0, 0, 0, + 653, 653, 0, 0, 0, 0, 0, 655, 0, 0, + 0, 0, 655, 653, 655, 655, 655, 655, 655, 655, + 655, 655, 655, 655, 655, 0, 0, 0, 0, 653, + 0, 0, 0, 0, 0, 0, 0, 655, 0, 655, + 0, 655, 0, 655, 655, 655, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, + 0, 0, 658, 0, 0, 0, 0, 0, 0, 655, + 655, 0, 0, 0, 0, 0, 656, 0, 0, 0, + 0, 656, 655, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 0, 0, 0, 0, 655, 0, + 0, 0, 0, 0, 0, 0, 656, 0, 656, 0, + 656, 0, 656, 656, 656, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 656, 0, + 0, 659, 0, 0, 0, 0, 0, 0, 656, 656, + 0, 0, 0, 0, 0, 658, 0, 0, 0, 0, + 658, 656, 658, 658, 658, 658, 658, 658, 658, 658, + 658, 658, 658, 0, 0, 0, 0, 656, 0, 0, + 0, 0, 0, 0, 0, 658, 0, 658, 0, 658, + 0, 658, 658, 658, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 658, 0, 0, + 0, 364, 0, 0, 0, 836, 0, 0, 658, 0, + 0, 0, 0, 0, 659, 0, 0, 0, 0, 659, + 658, 659, 659, 659, 659, 659, 659, 659, 659, 659, + 659, 659, 0, 0, 0, 0, 658, 364, 0, 0, + 0, 0, 0, 0, 659, 0, 659, 0, 659, 0, + 659, 659, 659, 836, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 659, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 659, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 659, + 0, 0, 0, 0, 0, 0, 364, 0, 0, 0, + 0, 0, 364, 364, 0, 659, 0, 0, 364, 364, + 0, 364, 0, 364, 0, 836, 364, 0, 364, 364, + 0, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 0, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 0, 0, 591, 0, 364, 0, + 364, 0, 0, 364, 57, 24, 58, 25, 1164, 364, + 26, 59, 0, 60, 61, 27, 62, 63, 64, 28, + 0, 0, 0, 0, 0, 65, 0, 66, 30, 67, + 68, 69, 70, 0, 0, 32, 0, 0, 0, 71, + 33, 0, 72, 73, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 74, 0, 36, 0, 37, 75, + 0, 0, 38, 0, 76, 77, 78, 79, 80, 81, + 39, 40, 82, 83, 41, 84, 0, 85, 0, 0, + 86, 87, 0, 0, 88, 89, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, + 91, 92, 93, 94, 0, 0, 0, 0, 95, 0, + 0, 0, 96, 0, 0, 0, 0, 97, 98, 99, + 100, 101, 0, 0, 0, 102, 0, 103, 0, 0, + 0, 0, 0, 104, 105, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 0, 106, 592, + 108, 109, 0, 1165, 57, 24, 58, 25, 0, 0, + 26, 59, 0, 60, 61, 27, 62, 63, 64, 28, + 0, 0, 0, 0, 0, 65, 0, 66, 30, 67, + 68, 69, 70, 0, 0, 32, 0, 0, 0, 71, + 33, 0, 72, 73, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 74, 0, 36, 0, 37, 75, + 0, 0, 38, 0, 76, 77, 78, 79, 80, 81, + 39, 40, 82, 83, 41, 84, 0, 85, 0, 0, + 86, 87, 0, 0, 88, 89, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, + 91, 92, 93, 94, 0, 0, 0, 0, 95, 0, + 0, 0, 96, 0, 0, 0, 0, 97, 98, 99, + 100, 101, 0, 0, 0, 102, 0, 103, 0, 0, + 0, 0, 0, 104, 105, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 272, 0, 0, 0, 106, 107, + 108, 109, 57, 24, 58, 25, 0, 0, 26, 59, + 0, 60, 61, 27, 62, 63, 64, 28, 0, 0, + 0, 0, 0, 65, 0, 66, 30, 67, 68, 69, + 70, 0, 0, 32, 0, 0, 0, 71, 33, 0, + 72, 73, 34, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 74, 0, 36, 0, 37, 75, 0, 0, + 38, 0, 76, 77, 78, 79, 80, 81, 39, 40, + 82, 83, 41, 84, 0, 85, 0, 0, 86, 87, + 0, 0, 88, 89, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 91, 92, + 93, 94, 0, 0, 0, 0, 95, 0, 0, 0, + 96, 0, 0, 0, 0, 97, 98, 99, 100, 101, + 0, 0, 0, 102, 0, 103, 0, 0, 0, 0, + 0, 104, 105, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 591, 0, 0, 0, 106, 107, 108, 109, + 57, 24, 58, 25, 0, 0, 26, 59, 0, 60, + 61, 27, 62, 63, 64, 28, 0, 0, 0, 0, + 0, 65, 0, 66, 30, 67, 68, 69, 70, 0, + 0, 32, 0, 0, 0, 71, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 74, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 76, 77, 78, 79, 80, 81, 39, 40, 82, 83, + 41, 84, 0, 85, 0, 0, 86, 87, 0, 0, + 88, 89, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 90, 91, 92, 93, 94, + 0, 0, 0, 0, 95, 0, 0, 0, 96, 0, + 0, 0, 0, 97, 98, 99, 100, 101, 0, 0, + 0, 102, 0, 103, 0, 0, 0, 0, 0, 104, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1049, 0, 0, 0, 106, 592, 108, 109, 1049, 1049, + 1049, 1049, 0, 0, 1049, 1049, 0, 1049, 1049, 1049, + 1049, 1049, 1049, 1049, 0, 0, 0, 0, 0, 1049, + 0, 1049, 1049, 1049, 1049, 1049, 1049, 0, 0, 1049, + 0, 0, 0, 1049, 1049, 0, 1049, 1049, 1049, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1049, 0, + 1049, 0, 1049, 1049, 0, 0, 1049, 0, 1049, 1049, + 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, + 0, 1049, 0, 0, 1049, 1049, 0, 0, 1049, 1049, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1049, 1049, 1049, 1049, 1049, 0, 0, + 0, 0, 1049, 0, 0, 0, 1049, 0, 0, 0, + 0, 1049, 1049, 1049, 1049, 1049, 0, 0, 0, 1049, + 0, 1049, 0, 0, 0, 0, 0, 1049, 1049, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 635, 0, + 0, 0, 1049, 1049, 1049, 1049, 57, 24, 0, 25, + 0, 0, 26, 259, 0, 0, 0, 27, 62, 63, + 0, 28, 0, 0, 189, 0, 189, 65, 0, 189, + 30, 0, 0, 0, 189, 0, 0, 32, 189, 0, + 0, 0, 33, 0, 72, 73, 34, 189, 636, 0, + 0, 0, 0, 0, 189, 637, 0, 0, 36, 189, + 37, 75, 0, 189, 38, 0, 0, 77, 0, 79, + 0, 81, 39, 40, 260, 189, 41, 189, 0, 0, + 0, 189, 0, 638, 0, 0, 88, 89, 0, 189, + 189, 0, 0, 189, 0, 0, 189, 0, 0, 0, + 0, 90, 91, 92, 93, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 96, 0, 0, 639, 0, 0, + 98, 99, 100, 101, 0, 0, 0, 102, 0, 103, + 0, 0, 1074, 0, 0, 104, 105, 0, 0, 0, + 0, 0, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 106, 107, 108, 109, 65, 0, 0, 30, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 189, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 87, 0, 0, 88, 89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 93, 801, 0, 0, 0, 0, 802, 1088, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 0, 0, 0, + 0, 0, 104, 105, 0, 0, 0, 0, 0, 0, + 57, 24, 0, 25, 0, 0, 26, 259, 0, 0, + 0, 27, 62, 63, 0, 28, 0, 106, 803, 108, + 109, 65, 0, 804, 30, 0, 0, 0, 805, 0, + 0, 32, 0, 0, 0, 0, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 0, 77, 0, 79, 0, 81, 39, 40, 260, 0, + 41, 0, 0, 0, 0, 0, 0, 87, 0, 0, + 88, 89, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 90, 91, 92, 93, 801, + 0, 0, 0, 0, 802, 0, 0, 0, 96, 0, + 0, 0, 0, 0, 98, 99, 100, 101, 0, 0, + 0, 102, 0, 103, 0, 0, 0, 0, 0, 104, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 819, 106, 803, 108, 109, 0, 0, + 804, 57, 24, 0, 25, 805, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 189, + 0, 189, 65, 0, 189, 30, 0, 0, 0, 189, + 0, 0, 32, 189, 0, 0, 0, 33, 0, 72, + 73, 34, 189, 0, 0, 0, 0, 0, 0, 189, + 0, 0, 0, 36, 189, 37, 75, 0, 189, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 189, 41, 189, 0, 0, 0, 189, 0, 87, 0, + 0, 88, 89, 0, 189, 189, 0, 0, 189, 0, + 0, 189, 0, 0, 0, 0, 90, 91, 92, 93, + 308, 0, 0, 0, 0, 546, 820, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 1074, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1013, 0, 0, 0, 106, 309, 108, 109, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 189, 28, 0, 0, 24, 0, 25, + 65, 0, 26, 30, 0, 0, 0, 27, 0, 0, + 32, 28, 0, 0, 0, 33, 0, 72, 73, 34, + 30, 636, 0, 0, 0, 0, 0, 32, 637, 0, + 0, 36, 33, 37, 75, 0, 34, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 36, 41, + 37, 0, 0, 0, 38, 0, 638, 0, 0, 88, + 89, 0, 39, 40, 0, 0, 41, 0, 0, 85, + 0, 0, 0, 0, 90, 91, 92, 93, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 0, 0, 0, 0, 0, 104, 105, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 819, + 0, 0, 0, 106, 107, 108, 109, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 380, 28, 0, 0, 24, 0, 25, 65, 0, + 26, 30, 0, 0, 0, 27, 0, 0, 32, 28, + 0, 0, 0, 33, 0, 72, 73, 34, 30, 0, + 0, 0, 0, 0, 0, 32, 0, 0, 0, 36, + 33, 37, 75, 1019, 34, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 36, 41, 37, 0, + 0, 0, 38, 0, 87, 0, 0, 88, 89, 0, + 39, 40, 0, 0, 41, 0, 0, 608, 0, 0, + 0, 0, 90, 91, 92, 93, 308, 0, 0, 0, + 0, 546, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 0, 0, 0, 0, 0, 104, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 819, 0, 0, + 0, 106, 309, 108, 109, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 380, + 28, 0, 0, 0, 0, 0, 65, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 87, 0, 0, 88, 89, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 93, 308, 0, 0, 0, 0, 546, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 0, + 0, 0, 0, 0, 104, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 347, 0, 106, + 309, 108, 109, 348, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 349, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 350, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 352, 0, 0, 0, 98, + 99, 100, 101, 972, 0, 0, 102, 0, 103, 811, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 87, 0, 0, 88, 89, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 93, 308, 0, 0, 0, + 0, 0, 973, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 0, 0, 0, 0, 0, 104, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1016, 0, 0, + 0, 106, 309, 108, 109, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 24, 0, 25, 65, 0, 26, 30, + 0, 0, 0, 27, 0, 0, 32, 28, 0, 0, + 0, 33, 0, 72, 73, 34, 30, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 36, 33, 37, + 75, 0, 34, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 36, 41, 37, 0, 0, 0, + 38, 0, 87, 0, 0, 88, 89, 0, 39, 40, + 0, 0, 41, 0, 0, 563, 0, 0, 0, 0, + 90, 91, 92, 93, 308, 0, 0, 0, 0, 0, + 1017, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 0, + 0, 0, 0, 0, 104, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 307, 0, 0, 0, 106, + 309, 108, 109, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 380, 28, 0, + 0, 527, 0, 527, 65, 0, 527, 30, 0, 0, + 0, 527, 0, 0, 32, 527, 0, 0, 0, 33, + 0, 72, 73, 34, 527, 0, 0, 0, 0, 0, + 0, 527, 0, 0, 0, 36, 527, 37, 75, 0, + 527, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 527, 41, 527, 0, 0, 0, 527, 0, + 87, 0, 0, 88, 89, 0, 527, 527, 0, 0, + 527, 0, 0, 527, 0, 0, 0, 0, 90, 91, + 92, 93, 308, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 0, 0, 0, + 0, 0, 104, 105, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 316, 0, 0, 0, 106, 309, 108, + 109, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 527, 28, 0, 0, 0, + 0, 0, 65, 0, 0, 30, 0, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 87, 0, + 0, 88, 89, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 93, + 308, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 0, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 347, 0, 106, 309, 108, 109, 348, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 0, 0, 30, 0, 0, 0, 0, + 0, 0, 32, 0, 0, 349, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 350, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 352, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 633, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 87, 0, 0, 88, 89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 93, 94, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 0, 0, 0, + 0, 0, 104, 105, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 811, 0, 0, 0, 106, 107, 108, + 109, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 190, + 0, 190, 65, 0, 190, 30, 0, 0, 0, 190, + 0, 0, 32, 190, 0, 0, 0, 33, 0, 72, + 73, 34, 190, 0, 0, 0, 0, 0, 0, 190, + 0, 0, 0, 36, 190, 37, 75, 0, 190, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 190, 41, 190, 0, 0, 0, 190, 0, 87, 0, + 0, 88, 89, 0, 190, 190, 0, 0, 190, 0, + 0, 190, 0, 0, 0, 0, 90, 91, 92, 93, + 308, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 0, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1126, 0, 0, 0, 106, 309, 108, 109, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 190, 28, 0, 0, 189, 0, 189, + 65, 0, 189, 30, 0, 0, 0, 189, 0, 0, + 32, 189, 0, 0, 0, 33, 0, 72, 73, 34, + 189, 0, 0, 0, 0, 0, 0, 189, 0, 0, + 0, 36, 189, 37, 75, 0, 189, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 189, 41, + 189, 0, 0, 0, 189, 0, 87, 0, 0, 88, + 89, 0, 189, 189, 0, 0, 189, 0, 0, 189, + 0, 0, 0, 0, 90, 91, 92, 93, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 0, 0, 0, 0, 0, 104, 105, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1278, + 0, 0, 0, 106, 1127, 108, 109, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 189, 28, 0, 0, 199, 0, 199, 65, 0, + 199, 30, 0, 0, 0, 199, 0, 0, 32, 199, + 0, 0, 0, 33, 0, 72, 73, 34, 199, 0, + 0, 0, 0, 0, 0, 199, 0, 0, 0, 36, + 199, 37, 75, 0, 199, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 199, 41, 199, 0, + 0, 0, 199, 0, 87, 0, 0, 88, 89, 0, + 199, 199, 0, 0, 199, 0, 0, 199, 0, 0, + 0, 0, 90, 91, 92, 93, 308, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 0, 0, 0, 0, 0, 104, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, + 0, 106, 309, 108, 109, 83, 83, 0, 83, 0, + 0, 83, 83, 0, 0, 0, 83, 83, 83, 199, + 83, 0, 0, 0, 0, 0, 83, 0, 0, 83, + 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, + 0, 83, 0, 83, 83, 83, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 83, 0, 83, + 83, 0, 0, 83, 0, 0, 83, 0, 83, 0, + 83, 83, 83, 83, 0, 83, 0, 0, 0, 0, + 0, 0, 83, 0, 0, 83, 83, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 83, 83, 83, 83, 83, 0, 0, 0, 0, 0, + 0, 0, 0, 83, 0, 0, 0, 0, 0, 83, + 83, 83, 83, 0, 0, 0, 83, 0, 83, 0, + 0, 0, 0, 0, 83, 83, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 347, 0, 83, + 83, 83, 83, 348, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 349, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 352, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 347, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 351, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 352, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 356, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 351, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 358, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 360, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 351, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 362, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 364, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 351, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 366, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 351, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 368, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 370, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 351, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 658, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 660, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 351, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 662, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 351, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 675, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 677, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 351, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 679, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 682, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 680, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 684, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 680, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 686, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 680, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 688, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 680, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 690, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 692, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 680, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 694, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 680, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 696, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 680, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 698, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 680, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 700, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 702, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 680, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 704, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 680, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 869, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 0, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 210, 0, 0, 0, 0, 210, 0, + 0, 0, 210, 0, 210, 106, 263, 210, 109, 210, + 210, 0, 210, 0, 210, 0, 210, 0, 210, 210, + 210, 210, 0, 0, 210, 210, 0, 0, 0, 0, + 210, 0, 210, 210, 210, 0, 0, 210, 0, 210, + 0, 210, 0, 0, 210, 0, 210, 210, 210, 210, + 0, 0, 0, 210, 210, 210, 0, 0, 210, 210, + 210, 0, 0, 0, 0, 0, 0, 210, 210, 0, + 210, 210, 666, 210, 210, 210, 0, 0, 0, 210, + 57, 24, 0, 25, 0, 0, 26, 259, 0, 0, + 0, 27, 62, 63, 0, 28, 0, 0, 0, 210, + 0, 65, 0, 0, 30, 210, 210, 210, 0, 0, + 0, 32, 0, 0, 0, 210, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 0, 77, 0, 79, 0, 81, 39, 40, 260, 0, + 41, 0, 0, 85, 0, 0, 210, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 90, 91, 92, 261, 262, + 0, 0, 0, 525, 0, 0, 0, 0, 96, 0, + 364, 57, 24, 0, 25, 667, 668, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 0, 0, 30, 0, 0, 0, 51, + 0, 51, 32, 0, 0, 0, 364, 33, 0, 72, + 73, 34, 0, 0, 106, 263, 0, 0, 0, 0, + 0, 0, 51, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 51, 81, 39, 40, 260, + 51, 41, 0, 0, 0, 51, 0, 51, 51, 51, + 51, 0, 0, 51, 0, 51, 0, 0, 0, 51, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 526, 51, 364, 0, 51, 0, 51, 0, 0, 96, + 364, 364, 364, 364, 836, 0, 0, 364, 364, 0, + 0, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 51, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 0, 0, 106, 527, 0, 0, 364, + 0, 52, 364, 52, 0, 52, 0, 52, 0, 0, + 52, 0, 52, 52, 0, 52, 0, 52, 0, 52, + 0, 52, 52, 52, 52, 0, 0, 52, 52, 0, + 0, 0, 0, 52, 52, 52, 52, 52, 0, 0, + 52, 0, 52, 0, 52, 0, 52, 52, 0, 52, + 52, 52, 52, 0, 0, 52, 52, 52, 52, 0, + 0, 52, 52, 52, 0, 0, 0, 0, 0, 0, + 52, 52, 0, 52, 52, 0, 52, 52, 52, 0, + 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 52, 0, 0, 52, 52, 51, 0, 0, + 0, 51, 0, 51, 0, 0, 51, 0, 51, 51, + 0, 51, 0, 51, 0, 51, 0, 51, 51, 51, + 51, 0, 0, 51, 51, 0, 0, 0, 0, 51, + 0, 51, 51, 51, 0, 0, 51, 0, 51, 0, + 51, 0, 0, 51, 0, 51, 51, 51, 51, 52, + 0, 0, 51, 51, 51, 0, 0, 51, 51, 51, + 0, 0, 0, 0, 0, 0, 51, 51, 0, 51, + 51, 0, 51, 51, 51, 0, 0, 0, 51, 0, + 0, 51, 0, 0, 0, 51, 0, 51, 0, 0, + 51, 0, 51, 51, 0, 51, 0, 51, 51, 51, + 0, 51, 51, 51, 51, 86, 0, 51, 51, 0, + 0, 0, 0, 51, 51, 51, 51, 51, 0, 0, + 51, 0, 51, 0, 51, 0, 0, 51, 0, 51, + 51, 51, 51, 0, 0, 0, 51, 51, 51, 0, + 0, 51, 51, 51, 0, 0, 0, 0, 0, 0, + 51, 51, 0, 51, 51, 51, 51, 51, 51, 0, + 0, 0, 51, 0, 0, 52, 0, 0, 0, 52, + 0, 52, 0, 0, 52, 0, 52, 52, 0, 52, + 0, 52, 51, 52, 0, 52, 52, 52, 52, 87, + 0, 52, 52, 0, 0, 0, 0, 52, 51, 52, + 52, 52, 0, 0, 52, 0, 52, 0, 52, 0, + 0, 52, 0, 52, 52, 52, 52, 0, 0, 0, + 52, 52, 52, 0, 0, 52, 52, 52, 0, 0, + 0, 0, 0, 0, 52, 52, 0, 52, 52, 51, + 52, 52, 52, 0, 0, 0, 52, 0, 0, 51, + 0, 0, 0, 51, 0, 51, 0, 0, 51, 0, + 51, 51, 0, 51, 0, 51, 52, 51, 0, 51, + 51, 51, 51, 0, 0, 51, 51, 0, 0, 0, + 0, 51, 52, 51, 51, 51, 0, 0, 51, 0, + 51, 0, 51, 0, 0, 51, 0, 51, 51, 51, + 51, 0, 0, 0, 51, 51, 51, 0, 0, 51, + 51, 51, 0, 0, 0, 0, 0, 0, 51, 51, + 0, 51, 51, 52, 51, 51, 51, 0, 0, 0, + 51, 0, 0, 51, 0, 0, 0, 51, 0, 51, + 0, 0, 51, 0, 51, 51, 0, 51, 0, 51, + 51, 51, 0, 51, 51, 51, 51, 238, 0, 51, + 51, 0, 0, 0, 0, 51, 0, 51, 51, 51, + 0, 0, 51, 0, 51, 364, 51, 0, 0, 51, + 0, 51, 51, 51, 51, 0, 0, 0, 51, 51, + 51, 0, 0, 51, 51, 51, 0, 0, 364, 0, + 0, 0, 51, 51, 0, 51, 51, 51, 51, 51, + 51, 364, 0, 0, 51, 0, 364, 0, 0, 364, + 0, 364, 0, 364, 364, 364, 364, 0, 0, 0, + 0, 364, 0, 0, 51, 364, 0, 0, 0, 364, + 0, 239, 0, 0, 0, 0, 0, 364, 0, 0, + 364, 0, 364, 0, 57, 24, 0, 25, 0, 0, + 26, 259, 0, 0, 0, 27, 62, 63, 0, 28, + 0, 364, 0, 0, 0, 65, 364, 0, 30, 0, + 0, 0, 0, 364, 364, 32, 287, 0, 364, 0, + 33, 51, 72, 73, 34, 0, 636, 0, 0, 0, + 0, 364, 0, 637, 0, 0, 36, 0, 37, 75, + 0, 0, 38, 0, 0, 77, 0, 79, 0, 81, + 39, 40, 260, 0, 41, 0, 0, 0, 0, 0, + 0, 638, 0, 364, 88, 89, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, + 91, 92, 93, 94, 0, 0, 0, 0, 0, 0, + 0, 0, 96, 1011, 0, 639, 0, 0, 98, 99, + 100, 101, 0, 0, 0, 102, 0, 103, 0, 0, + 0, 0, 0, 104, 105, 0, 0, 0, 0, 0, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 106, 107, + 108, 109, 65, 0, 0, 30, 0, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 636, 0, 0, 0, 0, 0, 0, + 637, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 638, 0, + 0, 88, 89, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 93, + 94, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 639, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 0, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 57, 24, + 0, 25, 0, 0, 26, 259, 0, 0, 0, 27, + 62, 63, 0, 28, 0, 106, 107, 108, 109, 65, + 0, 0, 30, 0, 0, 0, 0, 0, 0, 32, + 0, 0, 0, 0, 33, 0, 72, 73, 34, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 0, 37, 75, 0, 0, 38, 0, 0, 77, + 0, 79, 0, 81, 39, 40, 260, 0, 41, 0, + 0, 85, 0, 0, 0, 87, 0, 0, 88, 89, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 90, 91, 92, 93, 308, 0, 0, + 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, + 0, 0, 98, 99, 100, 101, 0, 0, 0, 102, + 0, 103, 0, 0, 0, 0, 0, 104, 105, 0, + 0, 0, 0, 0, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 106, 309, 108, 109, 65, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 87, 0, 0, 88, 89, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 93, 308, 0, 0, 0, 0, 546, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 0, + 0, 0, 0, 0, 104, 105, 0, 0, 0, 0, + 0, 0, 57, 24, 0, 25, 0, 0, 26, 259, + 0, 0, 0, 27, 62, 63, 0, 28, 0, 106, + 309, 108, 109, 65, 0, 0, 30, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 0, 33, 0, + 72, 73, 34, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 36, 0, 37, 75, 0, 0, + 38, 0, 0, 77, 0, 79, 0, 81, 39, 40, + 260, 0, 41, 0, 0, 0, 0, 0, 0, 87, + 0, 0, 88, 89, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 91, 92, + 93, 308, 0, 0, 0, 0, 540, 0, 0, 0, + 96, 0, 0, 0, 0, 0, 98, 99, 100, 101, + 0, 0, 0, 102, 0, 103, 0, 0, 0, 0, + 0, 104, 105, 0, 0, 0, 0, 0, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 106, 309, 108, 109, + 65, 0, 0, 30, 0, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 87, 0, 0, 88, + 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 93, 308, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 0, 0, 0, 0, 0, 104, 105, + 0, 0, 0, 0, 0, 0, 57, 24, 0, 25, + 0, 0, 26, 259, 0, 0, 0, 27, 62, 63, + 0, 28, 0, 106, 309, 108, 109, 65, 0, 0, + 30, 0, 0, 0, 0, 0, 0, 32, 0, 0, + 0, 0, 33, 0, 72, 73, 34, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, + 37, 75, 0, 0, 38, 0, 0, 77, 0, 79, + 0, 81, 39, 40, 260, 0, 41, 0, 0, 0, + 0, 0, 0, 87, 0, 0, 88, 89, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 90, 91, 92, 93, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, + 98, 99, 100, 101, 0, 0, 0, 102, 0, 103, + 0, 0, 0, 0, 0, 104, 105, 0, 0, 0, + 0, 0, 0, 687, 687, 0, 687, 0, 0, 687, + 687, 0, 0, 0, 687, 687, 687, 0, 687, 0, + 106, 107, 108, 109, 687, 0, 0, 687, 0, 0, + 0, 0, 0, 0, 687, 0, 0, 0, 0, 687, + 0, 687, 687, 687, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 687, 0, 687, 687, 0, + 0, 687, 0, 0, 687, 0, 687, 0, 687, 687, + 687, 687, 0, 687, 0, 0, 0, 0, 0, 0, + 687, 0, 0, 687, 687, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 687, 687, + 687, 687, 687, 0, 0, 0, 0, 0, 0, 0, + 0, 687, 0, 0, 0, 0, 0, 687, 687, 687, + 687, 0, 0, 0, 687, 0, 687, 0, 0, 0, + 0, 0, 687, 687, 0, 0, 0, 0, 0, 0, + 146, 146, 0, 146, 0, 0, 146, 146, 0, 0, + 0, 146, 146, 146, 0, 146, 0, 687, 687, 687, + 687, 146, 0, 0, 146, 0, 0, 0, 0, 0, + 0, 146, 0, 0, 0, 0, 146, 0, 146, 146, + 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 146, 0, 146, 146, 0, 0, 146, 0, + 0, 146, 0, 146, 0, 146, 146, 146, 146, 0, + 146, 0, 0, 0, 0, 0, 0, 146, 0, 0, + 146, 146, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, + 0, 0, 0, 0, 0, 0, 0, 0, 146, 0, + 0, 0, 0, 0, 146, 146, 146, 146, 0, 0, + 0, 146, 0, 146, 0, 0, 0, 0, 0, 146, + 146, 0, 0, 0, 0, 0, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 146, 146, 146, 146, 65, 0, + 0, 30, 0, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 680, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 0, 57, 24, 0, 25, 104, 105, 26, 259, + 0, 0, 0, 27, 62, 63, 0, 28, 0, 0, + 0, 0, 0, 65, 0, 0, 30, 0, 0, 0, + 0, 106, 263, 32, 109, 0, 0, 0, 33, 0, + 72, 73, 34, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 36, 0, 37, 75, 0, 0, + 38, 0, 0, 77, 0, 79, 0, 81, 39, 40, + 260, 0, 41, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 91, 92, + 261, 351, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 0, 0, 0, 0, 0, 98, 99, 100, 101, + 0, 0, 0, 102, 0, 103, 0, 0, 0, 0, + 0, 104, 105, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 733, 0, 733, 0, 733, 106, 263, 733, 109, + 733, 733, 0, 733, 0, 733, 0, 733, 0, 733, + 733, 733, 0, 0, 0, 733, 733, 0, 0, 0, + 0, 733, 0, 733, 733, 0, 0, 0, 733, 0, + 0, 0, 733, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 733, 733, 0, 733, 0, 0, 0, + 733, 733, 0, 0, 0, 0, 0, 0, 733, 733, + 57, 24, 733, 25, 0, 733, 26, 259, 0, 0, + 733, 27, 62, 63, 0, 28, 0, 0, 0, 0, + 0, 65, 0, 0, 30, 0, 0, 0, 0, 0, + 0, 32, 0, 733, 733, 0, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 733, 0, 0, 0, + 0, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 0, 77, 0, 79, 0, 81, 39, 40, 260, 0, + 41, 0, 0, 85, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 733, 0, 0, + 0, 0, 0, 0, 0, 90, 91, 92, 261, 262, + 0, 0, 0, 0, 0, 732, 0, 732, 96, 0, + 732, 0, 732, 732, 0, 732, 0, 732, 0, 732, + 0, 732, 732, 732, 0, 0, 0, 732, 732, 0, + 0, 0, 0, 732, 0, 732, 732, 0, 0, 0, + 732, 0, 0, 0, 732, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 106, 263, 732, 0, 732, 0, + 0, 0, 732, 732, 0, 0, 0, 0, 0, 0, + 732, 732, 0, 24, 732, 25, 0, 732, 26, 0, + 0, 1399, 732, 27, 0, 757, 0, 28, 0, 758, + 1400, 1401, 0, 0, 0, 1402, 30, 0, 0, 0, + 0, 1403, 0, 32, 0, 51, 0, 51, 33, 0, + 51, 0, 34, 0, 0, 51, 0, 0, 732, 51, + 0, 0, 0, 0, 36, 0, 37, 0, 51, 0, + 38, 0, 0, 0, 0, 51, 0, 0, 39, 40, + 51, 0, 41, 0, 51, 1404, 51, 0, 51, 0, + 1405, 0, 0, 51, 0, 0, 51, 0, 51, 732, + 0, 0, 51, 0, 0, 51, 0, 0, 0, 0, + 51, 51, 0, 51, 51, 51, 0, 51, 51, 0, + 0, 0, 0, 51, 0, 0, 1406, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, + 0, 0, 0, 51, 0, 24, 0, 25, 51, 0, + 26, 0, 51, 169, 51, 27, 51, 0, 0, 28, + 0, 51, 0, 0, 51, 0, 51, 1407, 30, 0, + 51, 0, 0, 51, 0, 32, 0, 0, 51, 51, + 33, 0, 51, 0, 34, 51, 605, 0, 0, 0, + 0, 0, 0, 606, 0, 0, 36, 0, 37, 51, + 0, 0, 38, 0, 0, 607, 0, 0, 0, 0, + 39, 40, 0, 0, 41, 0, 0, 608, 52, 169, + 52, 0, 0, 52, 0, 0, 0, 0, 52, 0, + 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 0, 609, 0, 0, 0, 0, 52, 0, + 51, 0, 51, 52, 0, 51, 0, 52, 0, 52, + 51, 52, 0, 0, 51, 0, 52, 51, 0, 52, + 0, 52, 0, 51, 0, 52, 0, 0, 52, 0, + 51, 0, 0, 52, 52, 51, 0, 52, 0, 51, + 52, 51, 0, 51, 0, 24, 0, 25, 51, 610, + 26, 51, 0, 51, 0, 27, 0, 51, 0, 28, + 51, 0, 0, 29, 0, 51, 51, 0, 30, 51, + 0, 0, 51, 31, 0, 32, 0, 24, 0, 25, + 33, 0, 26, 0, 34, 35, 0, 27, 0, 0, + 0, 28, 0, 0, 0, 0, 36, 0, 37, 0, + 30, 0, 38, 0, 0, 0, 0, 32, 0, 0, + 39, 40, 33, 0, 41, 0, 34, 42, 0, 0, + 0, 37, 52, 0, 0, 0, 0, 0, 36, 0, + 37, 0, 37, 0, 38, 0, 0, 37, 0, 0, + 0, 37, 39, 40, 37, 0, 41, 0, 0, 85, + 0, 0, 0, 0, 51, 0, 37, 37, 0, 0, + 0, 37, 37, 0, 0, 0, 0, 37, 0, 37, + 37, 37, 37, 0, 0, 297, 0, 37, 0, 0, + 0, 37, 0, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 35, 37, 0, 37, 37, 0, 37, 43, + 0, 0, 37, 35, 0, 0, 0, 0, 35, 0, + 0, 0, 35, 0, 0, 35, 0, 0, 0, 0, + 0, 0, 37, 0, 0, 0, 0, 35, 35, 37, + 37, 330, 35, 35, 31, 0, 0, 0, 35, 0, + 35, 35, 35, 35, 0, 0, 0, 0, 35, 0, + 0, 0, 35, 0, 35, 0, 0, 31, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 35, 0, 35, + 31, 0, 31, 35, 31, 31, 0, 0, 0, 0, + 31, 0, 31, 31, 31, 31, 0, 0, 31, 0, + 31, 0, 0, 35, 31, 31, 0, 0, 0, 0, + 35, 35, 0, 0, 0, 0, 31, 0, 31, 31, + 0, 31, 0, 31, 0, 0, 0, 0, 31, 0, + 31, 31, 31, 31, 0, 0, 0, 0, 31, 0, + 0, 0, 31, 0, 0, 31, 0, 0, 0, 0, + 0, 0, 31, 31, 31, 0, 0, 31, 51, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 0, 0, 0, 0, 51, 0, 0, 0, 51, 0, + 0, 51, 0, 31, 0, 0, 0, 0, 0, 0, + 31, 31, 0, 51, 51, 0, 0, 0, 51, 51, + 0, 51, 0, 0, 51, 0, 51, 51, 51, 51, + 0, 0, 51, 0, 51, 0, 0, 51, 51, 0, + 51, 51, 0, 0, 51, 0, 0, 0, 0, 0, + 51, 0, 0, 51, 0, 51, 51, 51, 0, 51, + 0, 51, 51, 0, 51, 0, 0, 51, 0, 51, + 51, 51, 51, 0, 0, 0, 0, 51, 0, 51, + 0, 51, 0, 51, 0, 0, 39, 51, 0, 0, + 0, 0, 0, 51, 0, 0, 51, 0, 51, 0, + 51, 0, 51, 0, 51, 51, 0, 0, 0, 0, + 51, 0, 51, 51, 51, 51, 0, 0, 0, 0, + 51, 0, 51, 0, 51, 0, 0, 51, 0, 40, + 0, 0, 0, 0, 0, 0, 51, 0, 0, 51, + 51, 51, 0, 0, 51, 51, 0, 0, 0, 0, + 51, 0, 51, 51, 51, 51, 0, 0, 0, 0, + 51, 0, 0, 0, 51, 51, 0, 51, 51, 51, + 0, 0, 220, 0, 0, 0, 51, 0, 0, 51, + 51, 51, 0, 0, 51, 51, 51, 0, 0, 0, + 51, 0, 51, 51, 51, 51, 0, 0, 51, 0, + 51, 0, 0, 0, 51, 51, 0, 51, 51, 51, + 0, 0, 222, 0, 51, 0, 51, 0, 0, 51, + 51, 51, 0, 0, 0, 51, 0, 0, 0, 0, + 51, 0, 51, 51, 51, 51, 0, 51, 0, 0, + 51, 0, 0, 0, 51, 51, 0, 0, 0, 0, + 51, 0, 322, 0, 477, 51, 51, 0, 0, 51, + 51, 51, 51, 51, 51, 51, 0, 0, 0, 0, + 51, 0, 0, 0, 51, 0, 0, 478, 0, 0, + 0, 0, 0, 0, 477, 51, 51, 0, 0, 51, + 479, 51, 323, 0, 480, 481, 0, 0, 0, 0, + 482, 0, 483, 484, 485, 486, 0, 478, 0, 0, + 487, 0, 0, 0, 488, 51, 0, 0, 51, 51, + 479, 0, 0, 0, 0, 481, 489, 0, 0, 490, + 482, 491, 483, 484, 485, 486, 0, 0, 0, 0, + 487, 0, 0, 0, 488, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 492, 489, 0, 0, 490, + 0, 491, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 492, + }; + protected static readonly short [] yyCheck = { 17, + 306, 18, 4, 305, 320, 17, 17, 52, 6, 17, + 52, 385, 544, 295, 193, 192, 546, 239, 494, 354, + 237, 69, 329, 304, 252, 20, 516, 341, 161, 304, + 594, 601, 119, 1019, 302, 117, 60, 119, 60, 612, + 88, 89, 379, 396, 1196, 93, 78, 975, 59, 824, + 256, 395, 1236, 256, 256, 0, 74, 621, 268, 268, + 78, 336, 256, 379, 17, 113, 268, 292, 256, 80, + 256, 82, 256, 1470, 256, 256, 335, 1230, 96, 45, + 385, 1340, 48, 0, 88, 89, 256, 294, 282, 62, + 464, 1275, 1024, 66, 67, 68, 369, 70, 71, 306, + 1359, 17, 75, 76, 256, 109, 1290, 17, 81, 82, + 256, 84, 256, 86, 256, 369, 1076, 1305, 91, 92, + 314, 269, 357, 21, 325, 499, 369, 17, 176, 792, + 256, 794, 256, 374, 377, 256, 377, 256, 286, 375, + 256, 1294, 1102, 161, 17, 118, 256, 372, 193, 161, + 161, 193, 387, 161, 20, 53, 1314, 430, 731, 464, + 369, 436, 256, 256, 17, 419, 372, 395, 256, 339, + 373, 430, 375, 17, 344, 838, 346, 268, 841, 349, + 350, 417, 352, 353, 368, 276, 368, 256, 372, 17, + 372, 17, 237, 256, 1382, 237, 421, 17, 256, 203, + 204, 382, 1599, 251, 252, 375, 392, 60, 161, 17, + 17, 64, 419, 423, 423, 421, 590, 265, 419, 17, + 1152, 423, 88, 89, 368, 419, 540, 430, 256, 415, + 372, 419, 1160, 377, 252, 233, 422, 421, 460, 421, + 258, 543, 459, 109, 331, 161, 328, 363, 585, 331, + 343, 161, 626, 363, 302, 1413, 338, 631, 632, 594, + 430, 227, 60, 1421, 268, 795, 64, 419, 292, 585, + 292, 161, 363, 419, 368, 1534, 370, 295, 372, 421, + 302, 851, 300, 301, 628, 333, 621, 260, 161, 358, + 294, 264, 340, 419, 382, 419, 612, 315, 419, 392, + 419, 325, 1561, 419, 352, 323, 0, 325, 161, 419, + 314, 329, 257, 324, 1573, 256, 1575, 161, 376, 382, + 1478, 256, 415, 256, 342, 343, 631, 421, 419, 422, + 424, 304, 256, 161, 1191, 161, 429, 203, 204, 387, + 257, 161, 354, 391, 348, 349, 354, 395, 396, 377, + 1125, 383, 726, 161, 161, 272, 425, 426, 427, 428, + 277, 256, 256, 161, 281, 383, 375, 256, 256, 256, + 374, 256, 256, 277, 1231, 369, 1528, 395, 396, 296, + 953, 399, 400, 401, 402, 403, 404, 405, 406, 407, + 408, 409, 414, 441, 442, 732, 256, 445, 256, 674, + 628, 256, 268, 392, 256, 256, 323, 256, 417, 413, + 1594, 256, 363, 431, 459, 731, 1402, 459, 1081, 256, + 256, 726, 363, 256, 469, 342, 415, 469, 294, 256, + 371, 256, 373, 368, 375, 370, 430, 441, 442, 292, + 306, 757, 1626, 256, 377, 257, 256, 256, 314, 266, + 256, 339, 376, 306, 320, 266, 344, 987, 346, 363, + 256, 349, 350, 256, 352, 353, 461, 515, 419, 517, + 262, 264, 325, 780, 256, 965, 370, 325, 419, 474, + 305, 376, 348, 349, 373, 369, 371, 375, 363, 376, + 375, 375, 516, 292, 292, 340, 544, 314, 509, 465, + 466, 354, 876, 314, 341, 471, 298, 257, 374, 372, + 370, 256, 256, 379, 372, 419, 420, 739, 358, 370, + 375, 370, 570, 375, 542, 17, 544, 325, 546, 797, + 867, 277, 325, 370, 887, 281, 1106, 765, 511, 375, + 373, 351, 430, 1206, 419, 1075, 373, 413, 559, 294, + 772, 867, 1215, 305, 429, 368, 354, 575, 421, 376, + 578, 414, 256, 611, 373, 376, 369, 373, 60, 368, + 369, 419, 64, 377, 343, 441, 442, 373, 589, 627, + 628, 1244, 594, 369, 671, 558, 594, 379, 380, 671, + 601, 373, 335, 369, 373, 461, 342, 710, 369, 343, + 363, 369, 715, 716, 375, 1175, 1463, 375, 474, 621, + 628, 1320, 1321, 621, 382, 952, 414, 374, 636, 637, + 377, 363, 373, 392, 368, 673, 430, 430, 372, 369, + 374, 375, 256, 377, 1343, 1492, 1493, 953, 382, 1496, + 419, 1304, 373, 369, 430, 339, 415, 1211, 950, 343, + 373, 377, 1509, 422, 430, 1512, 419, 1563, 1564, 430, + 429, 1370, 430, 516, 712, 893, 714, 981, 372, 161, + 1527, 263, 416, 256, 368, 723, 809, 419, 372, 430, + 374, 375, 376, 377, 1393, 373, 263, 429, 382, 374, + 430, 370, 710, 256, 1551, 374, 294, 715, 716, 430, + 718, 369, 339, 272, 430, 986, 272, 430, 277, 984, + 369, 341, 281, 1019, 1620, 339, 375, 765, 516, 585, + 344, 419, 346, 315, 339, 349, 350, 296, 352, 353, + 296, 368, 256, 1321, 782, 372, 399, 400, 315, 424, + 370, 594, 430, 1321, 1321, 424, 612, 765, 1193, 797, + 272, 419, 305, 368, 323, 1245, 339, 323, 256, 357, + 758, 344, 780, 346, 343, 363, 349, 350, 621, 352, + 353, 1078, 370, 342, 296, 797, 374, 795, 272, 1321, + 343, 305, 1370, 1321, 802, 1317, 804, 339, 1233, 387, + 1092, 306, 1370, 1370, 1425, 1321, 594, 372, 313, 374, + 292, 323, 296, 382, 1321, 368, 430, 256, 1439, 372, + 325, 374, 375, 392, 377, 1043, 368, 1321, 866, 382, + 831, 419, 833, 621, 376, 843, 830, 845, 1370, 323, + 1461, 256, 1370, 325, 1169, 948, 415, 855, 849, 887, + 851, 339, 846, 422, 1370, 893, 344, 430, 346, 339, + 429, 349, 350, 1370, 352, 353, 829, 343, 1035, 863, + 339, 369, 354, 364, 370, 731, 1370, 375, 368, 887, + 339, 372, 985, 374, 382, 893, 1211, 377, 368, 392, + 343, 369, 1164, 372, 902, 930, 904, 375, 930, 368, + 339, 757, 393, 394, 1196, 344, 382, 346, 382, 368, + 349, 350, 415, 352, 353, 368, 392, 371, 392, 422, + 377, 375, 413, 1026, 339, 1028, 429, 1199, 1031, 344, + 421, 346, 414, 424, 349, 350, 343, 352, 353, 415, + 948, 415, 950, 256, 402, 357, 422, 343, 422, 792, + 935, 794, 940, 429, 992, 256, 414, 368, 343, 967, + 954, 368, 374, 343, 972, 339, 377, 975, 269, 343, + 1242, 1189, 368, 1439, 830, 387, 377, 985, 392, 987, + 1305, 377, 990, 368, 1022, 286, 415, 368, 368, 370, + 846, 430, 377, 422, 368, 838, 1516, 377, 841, 343, + 357, 415, 306, 377, 792, 1043, 794, 863, 422, 313, + 294, 867, 1115, 1408, 1117, 430, 1119, 374, 1026, 368, + 1028, 1059, 306, 1031, 368, 1420, 339, 1065, 383, 384, + 387, 344, 256, 346, 516, 1043, 349, 350, 1383, 352, + 353, 387, 397, 398, 306, 1390, 308, 372, 1260, 1256, + 838, 313, 369, 841, 1048, 1040, 1328, 1382, 1578, 1228, + 377, 370, 1050, 325, 1052, 385, 1054, 1075, 1408, 357, + 1078, 374, 1436, 382, 377, 363, 375, 1383, 377, 935, + 1420, 386, 370, 382, 1390, 373, 374, 375, 373, 1453, + 375, 368, 369, 368, 369, 1615, 390, 953, 954, 387, + 377, 372, 377, 374, 401, 1106, 1402, 1115, 1472, 1117, + 1474, 1119, 594, 391, 1122, 339, 1219, 430, 1640, 1641, + 344, 373, 346, 375, 370, 349, 350, 369, 352, 353, + 369, 419, 419, 375, 373, 377, 375, 416, 377, 621, + 369, 1133, 256, 373, 373, 375, 375, 377, 377, 369, + 1253, 1189, 1160, 373, 371, 372, 1164, 377, 375, 395, + 396, 371, 372, 1019, 374, 375, 376, 1169, 371, 1177, + 1178, 1169, 375, 371, 1175, 373, 1019, 375, 371, 419, + 373, 1189, 375, 1190, 1040, 371, 371, 277, 1196, 375, + 375, 1199, 1048, 1228, 374, 1230, 1228, 371, 1230, 373, + 373, 368, 377, 1306, 377, 372, 430, 374, 375, 1211, + 377, 1219, 377, 1211, 371, 382, 256, 1225, 375, 369, + 370, 1256, 371, 1326, 1256, 265, 375, 267, 371, 375, + 270, 377, 375, 372, 1242, 275, 1528, 376, 1081, 279, + 419, 420, 421, 422, 382, 1253, 1254, 6, 288, 416, + 373, 368, 373, 371, 377, 295, 377, 375, 17, 1294, + 300, 371, 1294, 373, 304, 403, 404, 405, 406, 407, + 408, 409, 410, 411, 412, 256, 316, 373, 318, 1317, + 374, 377, 322, 368, 369, 1320, 1321, 256, 1320, 1321, + 330, 331, 294, 1081, 334, 354, 355, 337, 1306, 377, + 371, 60, 373, 1305, 377, 64, 20, 1305, 1343, 1317, + 792, 1343, 794, 387, 388, 389, 294, 371, 1326, 373, + 1328, 373, 373, 375, 375, 1319, 1169, 354, 355, 88, + 89, 375, 343, 377, 256, 1370, 1374, 371, 1370, 373, + 371, 373, 373, 265, 377, 267, 356, 375, 270, 377, + 109, 419, 375, 275, 377, 419, 838, 279, 1393, 841, + 1652, 1393, 375, 1206, 377, 802, 288, 804, 1211, 369, + 370, 419, 1215, 295, 88, 89, 1232, 370, 300, 419, + 1382, 1169, 304, 373, 1382, 1386, 415, 1381, 376, 415, + 416, 365, 366, 373, 316, 109, 318, 368, 369, 382, + 322, 1244, 161, 365, 366, 1399, 1400, 377, 330, 331, + 415, 416, 334, 417, 418, 337, 423, 424, 1206, 369, + 364, 375, 430, 1211, 373, 369, 373, 1215, 372, 294, + 374, 375, 294, 1427, 375, 373, 1430, 373, 382, 1447, + 375, 1297, 373, 1444, 203, 204, 256, 377, 372, 393, + 394, 256, 429, 429, 294, 294, 1244, 382, 373, 375, + 374, 1304, 1305, 1319, 376, 375, 374, 419, 373, 413, + 430, 1506, 375, 382, 1506, 375, 375, 421, 375, 424, + 424, 373, 375, 368, 375, 373, 430, 343, 1523, 203, + 204, 1523, 375, 294, 294, 375, 419, 419, 0, 371, + 419, 1536, 1537, 372, 1536, 1537, 376, 368, 1516, 268, + 256, 256, 375, 1514, 256, 256, 1304, 1305, 382, 280, + 1528, 256, 368, 373, 369, 1381, 343, 1383, 1563, 1564, + 371, 1563, 1564, 292, 1390, 294, 372, 1545, 375, 1382, + 0, 375, 377, 1399, 1400, 377, 1402, 306, 373, 382, + 371, 373, 1408, 373, 268, 314, 424, 373, 382, 1402, + 347, 320, 368, 382, 1420, 256, 325, 382, 256, 1425, + 1578, 1427, 369, 377, 1430, 373, 256, 373, 256, 347, + 294, 375, 371, 1439, 376, 1620, 372, 371, 1620, 348, + 349, 292, 306, 371, 1382, 354, 373, 348, 369, 1081, + 314, 339, 1640, 1641, 373, 1461, 320, 1615, 419, 375, + 372, 419, 348, 373, 256, 374, 369, 382, 368, 368, + 379, 377, 1623, 1624, 368, 356, 369, 375, 337, 1630, + 1631, 305, 1640, 1641, 348, 349, 373, 377, 372, 369, + 369, 373, 369, 419, 1652, 370, 372, 419, 368, 419, + 372, 372, 419, 377, 413, 414, 372, 374, 368, 339, + 374, 372, 343, 382, 344, 379, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 372, 377, 382, + 370, 373, 441, 442, 373, 372, 256, 1169, 374, 369, + 375, 371, 374, 373, 377, 375, 376, 377, 375, 413, + 375, 419, 375, 373, 373, 371, 419, 419, 373, 377, + 377, 391, 419, 377, 373, 373, 369, 371, 373, 382, + 382, 369, 315, 263, 1206, 372, 372, 441, 442, 1211, + 94, 382, 369, 1215, 98, 99, 100, 101, 102, 103, + 104, 105, 373, 373, 377, 0, 0, 461, 368, 377, + 430, 369, 377, 0, 373, 257, 369, 516, 373, 261, + 474, 368, 1244, 373, 369, 371, 369, 419, 377, 373, + 272, 371, 368, 374, 419, 277, 419, 369, 373, 281, + 377, 369, 284, 373, 373, 369, 377, 368, 373, 369, + 377, 373, 369, 315, 296, 297, 256, 263, 377, 301, + 302, 261, 262, 377, 377, 307, 377, 309, 310, 311, + 312, 377, 377, 377, 377, 317, 377, 51, 12, 321, + 5, 323, 1304, 1305, 284, 1040, 585, 935, 1189, 0, + 1189, 333, 1393, 335, 336, 594, 338, 297, 298, 1343, + 342, 1398, 302, 1238, 1568, 305, 1531, 307, 1519, 309, + 310, 311, 312, 612, 1444, 1584, 1548, 317, 1514, 957, + 362, 321, 621, 952, 957, 325, 957, 369, 370, 771, + 1425, 585, 1358, 333, 1631, 1370, 336, 1445, 338, 339, + 1625, 1541, 1537, 343, 344, 1536, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 612, 1624, + 1382, 1294, 362, 363, 1474, 930, 320, 1297, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, 414, 379, + 380, 981, 637, 383, 384, 385, 386, 387, 893, 1092, + 390, 391, 72, 261, 765, 395, 396, 397, 398, 399, + 400, 401, 402, 797, 308, 0, 344, 992, 425, 427, + 426, 430, 1195, 428, 414, 429, 284, 417, 585, 419, + 867, 421, 1272, 1381, 424, 1169, 161, 1154, 1077, 297, + 430, 1178, 731, 1102, 302, 1061, 1167, 341, 1245, 307, + 1169, 309, 310, 311, 312, 560, 1214, 351, 450, 317, + 996, 450, 1376, 321, 1254, 932, 925, 325, 757, -1, + -1, -1, -1, -1, -1, 333, -1, -1, 336, -1, + 338, -1, -1, 721, -1, -1, -1, 731, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 792, 362, 794, 0, -1, -1, -1, + -1, 369, -1, 757, -1, -1, 410, 411, 412, -1, + -1, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 425, 426, 427, 428, 429, 430, -1, -1, -1, + -1, 830, -1, -1, -1, -1, -1, -1, -1, 838, + -1, -1, 841, -1, -1, -1, 257, 846, -1, -1, + 261, 419, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 272, -1, -1, 863, -1, 277, -1, 867, -1, + 281, -1, -1, 284, -1, -1, 830, 256, -1, -1, + -1, -1, -1, -1, -1, 296, 297, -1, -1, -1, + 301, 302, 846, -1, -1, -1, 307, -1, 309, 310, + 311, 312, -1, -1, -1, -1, 317, -1, -1, 863, + 321, -1, 323, 867, -1, -1, -1, 0, -1, -1, + -1, -1, 333, -1, -1, 336, -1, 338, -1, -1, + -1, 342, -1, -1, -1, -1, 540, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 362, -1, -1, 953, 954, -1, 368, 369, 370, + 339, -1, 257, -1, -1, 344, 261, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 272, -1, -1, + -1, 935, 277, -1, -1, -1, 281, -1, -1, 284, + 369, -1, 371, -1, 373, -1, 375, 376, 377, 953, + 954, 296, 297, -1, -1, -1, 301, 302, -1, -1, + -1, -1, 307, 617, 309, 310, 311, 312, -1, -1, + 1019, -1, 317, -1, -1, -1, 321, -1, 323, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 333, -1, + 335, 336, -1, 338, -1, -1, -1, 342, -1, 1048, + -1, 430, -1, 257, -1, -1, -1, 261, -1, -1, + -1, 364, -1, 667, 668, 1019, 369, 362, 272, 372, + -1, 374, 375, 277, -1, 370, 680, 281, -1, -1, + 284, -1, 1081, -1, -1, -1, 1040, -1, -1, -1, + 393, 394, 296, 297, 1048, -1, -1, 301, 302, -1, + -1, -1, -1, 307, -1, 309, 310, 311, 312, -1, + 413, -1, -1, 317, -1, -1, -1, 321, 421, 323, + -1, 424, -1, -1, -1, -1, -1, 430, 256, 333, + -1, -1, 336, -1, 338, -1, -1, 265, 342, 267, + -1, -1, 270, -1, -1, -1, -1, 275, -1, -1, + -1, 279, -1, 0, -1, -1, -1, -1, 362, -1, + 288, -1, -1, -1, -1, 369, 370, 295, -1, -1, + 1169, -1, 300, 256, -1, -1, 304, -1, 261, 262, + -1, -1, -1, -1, -1, -1, -1, -1, 316, -1, + 318, -1, -1, -1, 322, -1, -1, 801, -1, -1, + -1, 284, 330, 331, -1, -1, 334, 1206, -1, 337, + -1, -1, 1211, -1, 297, 298, 1215, -1, -1, 302, + -1, -1, 305, -1, 307, -1, 309, 310, 311, 312, + -1, -1, -1, 1232, 317, -1, -1, -1, 321, -1, + -1, -1, 325, -1, -1, 1244, -1, -1, -1, -1, + 333, -1, -1, 336, -1, 338, 339, -1, -1, -1, + 343, 344, -1, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, -1, -1, -1, -1, 1232, 362, + 363, -1, -1, -1, -1, 368, 369, 370, 371, 372, + 373, 419, 375, 376, 377, -1, 379, 380, 1297, -1, + 383, 384, 385, 386, -1, 1304, 1305, 390, 391, -1, + -1, -1, 395, 396, 397, 398, 399, 400, 401, 402, + 1319, -1, 0, 265, -1, 267, -1, -1, 270, -1, + 272, 414, -1, 275, 417, -1, 419, 279, 421, -1, + -1, 424, -1, 1297, -1, -1, 288, 430, -1, -1, + -1, -1, -1, 295, -1, -1, -1, -1, 300, -1, + 302, -1, 304, -1, -1, 1319, -1, -1, -1, -1, + -1, -1, -1, -1, 316, -1, 318, 981, -1, -1, + 322, 323, 1381, 1382, 1383, -1, -1, -1, 330, 331, + -1, 1390, 334, -1, -1, 337, -1, -1, -1, -1, + 1399, 1400, -1, 1402, -1, -1, -1, -1, -1, 256, + -1, -1, -1, -1, 261, 262, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1381, 1427, 1383, + -1, 1430, -1, -1, -1, -1, 1390, 284, -1, -1, + -1, -1, -1, -1, -1, 1399, 1400, -1, 1402, -1, + 297, 298, -1, -1, -1, 302, -1, -1, 305, -1, + 307, -1, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, -1, 1427, 321, -1, 1430, 419, 325, -1, + -1, -1, -1, -1, -1, -1, 333, -1, -1, 336, + 0, 338, 339, -1, -1, -1, 343, 344, -1, 346, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + -1, -1, -1, -1, -1, 362, 363, 364, -1, -1, + -1, 368, 369, -1, 371, 372, 373, 374, 375, 376, + 377, -1, 379, 380, -1, 382, 383, 384, 385, 386, + 387, 388, 389, 390, 391, -1, 393, 394, 395, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, -1, -1, + 417, -1, 419, -1, 421, -1, -1, 424, 256, 257, + -1, -1, -1, 430, -1, -1, 264, 265, 266, 267, + 268, -1, 270, 271, -1, 273, 274, 275, 276, 277, + 278, 279, 280, -1, -1, -1, -1, 285, -1, 287, + 288, 289, 290, 291, 292, -1, -1, 295, -1, -1, + 0, 299, 300, -1, 302, 303, 304, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 314, -1, 316, -1, + 318, 319, -1, -1, 322, -1, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 335, -1, 337, + -1, -1, 340, 341, -1, -1, 344, 345, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 359, 360, 361, 362, 363, -1, -1, -1, -1, + 368, 369, -1, -1, 372, -1, -1, -1, -1, 377, + 378, 379, 380, 381, -1, -1, -1, 385, -1, 387, + -1, -1, -1, -1, -1, 393, 394, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 418, 419, 420, 421, -1, 423, 256, 257, -1, -1, + -1, 0, 430, -1, 264, 265, 266, 267, 268, -1, + 270, 271, -1, 273, 274, 275, 276, 277, 278, 279, + -1, -1, -1, -1, -1, 285, -1, 287, 288, 289, + 290, 291, 292, -1, -1, 295, -1, -1, -1, 299, + 300, -1, 302, 303, 304, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 314, -1, 316, -1, 318, 319, + -1, -1, 322, -1, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, -1, 337, -1, -1, + 340, 341, -1, -1, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 359, + 360, 361, 362, 363, -1, -1, 0, -1, 368, 369, + -1, -1, 372, -1, -1, -1, -1, 377, 378, 379, + 380, 381, -1, -1, -1, 385, 256, 387, -1, -1, + -1, 261, 262, 393, 394, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 284, -1, -1, -1, 418, 419, + 420, 421, -1, 423, -1, -1, -1, 297, 298, -1, + 430, -1, 302, -1, -1, 305, -1, 307, -1, 309, + 310, 311, 312, -1, -1, -1, -1, 317, -1, -1, + -1, 321, -1, -1, -1, 325, -1, -1, -1, -1, + -1, -1, -1, 333, -1, -1, 336, -1, 338, 339, + -1, 0, -1, 343, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, 362, 363, -1, -1, -1, -1, 368, 369, + 370, 371, 372, 373, -1, 375, 376, 377, -1, 379, + 380, -1, -1, 383, 384, 385, 386, 256, -1, -1, + 390, 391, 261, 262, -1, 395, 396, 397, 398, 399, + 400, 401, 402, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 414, 284, -1, 417, -1, 419, + -1, 421, -1, -1, 424, -1, -1, -1, 297, 298, + 430, -1, -1, 302, -1, -1, 305, -1, 307, -1, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + -1, -1, 321, -1, -1, -1, 325, -1, -1, -1, + -1, -1, -1, -1, 333, -1, -1, 336, -1, 338, + 339, -1, -1, -1, 343, 344, -1, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, -1, + -1, -1, 256, 362, 363, -1, -1, -1, 262, 368, + 369, -1, 371, 372, 373, -1, 375, 376, 377, -1, + 379, 380, 0, -1, 383, 384, 385, 386, -1, -1, + -1, 390, 391, -1, -1, -1, 395, 396, 397, 398, + 399, 400, 401, 402, 298, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 414, -1, -1, 417, -1, + 419, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 430, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 339, -1, -1, -1, 343, + 344, -1, 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, 357, -1, -1, -1, 256, -1, 363, + -1, -1, -1, 262, 368, 369, 370, 371, 372, 373, + 374, 375, 376, 377, -1, 379, 380, -1, -1, 383, + 384, 385, 386, 387, -1, -1, 390, 391, 0, -1, + -1, 395, 396, 397, 398, 399, 400, 401, 402, 298, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 414, -1, -1, 417, -1, 419, -1, 421, -1, -1, + 424, -1, -1, -1, -1, -1, 430, -1, -1, -1, + -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, + 339, -1, -1, -1, 343, 344, -1, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, -1, + -1, -1, -1, -1, 363, -1, -1, -1, -1, 368, + 369, 370, 371, 372, 373, -1, 375, 376, 377, -1, + 379, 380, -1, -1, 383, 384, 385, 386, -1, -1, + -1, 390, 391, -1, -1, -1, 395, 396, 397, 398, + 399, 400, 401, 402, -1, -1, -1, -1, -1, -1, + -1, 0, -1, -1, -1, 414, -1, -1, 417, -1, + 419, -1, 421, -1, -1, 424, -1, -1, 256, 257, + -1, 430, -1, 261, -1, -1, -1, 265, -1, 267, + -1, -1, 270, -1, 272, 273, -1, 275, -1, 277, + -1, 279, -1, 281, 282, 283, 284, -1, 0, 287, + 288, -1, -1, -1, -1, 293, -1, 295, 296, 297, + -1, -1, 300, 301, 302, -1, 304, -1, -1, 307, + -1, 309, 310, 311, 312, -1, -1, -1, 316, 317, + 318, -1, -1, 321, 322, 323, -1, -1, -1, -1, + -1, -1, 330, 331, -1, 333, 334, -1, 336, 337, + 338, -1, -1, -1, 342, -1, -1, -1, -1, -1, + -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 362, -1, -1, -1, -1, -1, + 368, 369, 370, -1, -1, 257, -1, -1, -1, 261, + 378, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 272, -1, -1, -1, 0, 277, -1, -1, -1, 281, + -1, -1, 284, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 296, 297, -1, -1, 257, 301, + 302, 419, 261, -1, -1, 307, -1, 309, 310, 311, + 312, -1, -1, 272, -1, 317, -1, 0, 277, 321, + -1, 323, 281, -1, -1, 284, -1, -1, -1, -1, + -1, 333, -1, -1, 336, -1, 338, 296, 297, -1, + 342, -1, 301, 302, -1, -1, -1, -1, 307, -1, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + 362, -1, 321, -1, 323, -1, -1, 369, 370, -1, + -1, -1, -1, -1, 333, -1, -1, 336, 257, 338, + 0, -1, 261, 342, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 272, -1, -1, -1, -1, 277, -1, + -1, -1, 281, 362, -1, 284, -1, -1, -1, -1, + -1, 370, -1, -1, -1, -1, -1, 296, 297, -1, + -1, -1, 301, 302, -1, 257, -1, -1, 307, 261, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + 272, -1, 321, -1, 323, 277, -1, -1, -1, 281, + -1, -1, 284, -1, 333, -1, -1, 336, -1, 338, + -1, -1, -1, 342, 296, 297, -1, -1, -1, 301, + 302, -1, -1, -1, -1, 307, -1, 309, 310, 311, + 312, -1, -1, 362, -1, 317, -1, -1, 257, 321, + -1, 323, 261, -1, -1, -1, -1, -1, -1, -1, + -1, 333, -1, 272, 336, -1, 338, -1, 277, -1, + 342, -1, 281, -1, -1, 284, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 296, 297, -1, + 362, 257, 301, 302, -1, 261, -1, -1, 307, -1, + 309, 310, 311, 312, -1, -1, 272, -1, 317, -1, + -1, 277, 321, -1, 323, 281, -1, -1, 284, -1, + -1, -1, -1, -1, 333, -1, -1, 336, -1, 338, + 296, 297, -1, 342, 257, 301, 302, -1, 261, -1, + -1, 307, -1, 309, 310, 311, 312, -1, -1, 272, + -1, 317, -1, 362, 277, 321, -1, 323, 281, -1, + -1, 284, -1, -1, -1, -1, -1, 333, -1, -1, + 336, -1, 338, 296, 297, -1, 342, -1, 301, 302, + -1, -1, -1, -1, 307, -1, 309, 310, 311, 312, + -1, -1, -1, -1, 317, -1, 362, 257, 321, -1, + 323, 261, -1, -1, -1, -1, -1, -1, -1, -1, + 333, -1, 272, 336, -1, 338, -1, 277, -1, 342, + -1, 281, -1, -1, 284, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 296, 297, -1, 362, + -1, 301, 302, -1, -1, -1, -1, 307, -1, 309, + 310, 311, 312, -1, -1, -1, -1, 317, -1, -1, + -1, 321, -1, 323, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 333, -1, 256, 336, -1, 338, -1, + -1, -1, 342, 264, 265, 266, 267, -1, -1, 270, + 271, -1, 273, 274, 275, 276, 277, 278, 279, -1, + -1, -1, 362, -1, 285, -1, 287, 288, 289, 290, + 291, 292, -1, -1, 295, -1, -1, -1, 299, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 314, -1, 316, -1, 318, 319, -1, + -1, 322, -1, 324, 325, 326, 327, 328, 329, 330, + 331, 332, 333, 334, 335, -1, 337, -1, -1, 340, + 341, -1, -1, 344, 345, 256, -1, -1, -1, 261, + -1, 262, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, 368, -1, -1, + -1, 372, 284, -1, -1, -1, 377, 378, 379, 380, + 381, -1, -1, -1, 385, 297, 387, 298, -1, -1, + 302, -1, 393, 394, -1, 307, -1, 309, 310, 311, + 312, -1, -1, -1, -1, 317, -1, -1, -1, 321, + -1, -1, -1, 325, -1, -1, -1, 418, 419, 420, + 421, 333, -1, -1, 336, -1, 338, -1, 339, 430, + -1, -1, -1, 344, 256, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, 357, -1, -1, -1, + 362, -1, 363, 364, -1, -1, -1, -1, 369, 370, + 371, 372, 373, 374, 375, 376, 377, -1, 379, 380, + -1, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, -1, 393, 394, 395, 396, 397, 398, 399, 400, + 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, -1, 256, 419, 419, -1, + 421, 422, 262, 424, -1, -1, -1, 339, -1, 430, + -1, -1, 344, -1, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 369, 298, 371, + -1, 373, -1, 375, 376, 377, -1, -1, -1, -1, + -1, 383, 384, 385, 386, -1, -1, -1, 390, 391, + -1, -1, -1, 395, 396, 397, 398, 399, 400, 401, + 402, -1, -1, -1, -1, -1, -1, -1, -1, 339, + -1, -1, 414, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 430, -1, + -1, -1, -1, 363, 364, -1, -1, -1, -1, 369, + 370, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 256, 256, -1, 419, + -1, 421, 262, -1, 424, -1, 265, -1, 267, -1, + 430, 270, -1, -1, -1, -1, 275, -1, -1, -1, + 279, -1, -1, -1, -1, -1, -1, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, 298, -1, + -1, 300, -1, -1, -1, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + -1, -1, -1, 322, -1, -1, -1, -1, -1, -1, + -1, 330, 331, -1, -1, 334, -1, -1, 337, 339, + -1, -1, -1, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, -1, -1, + -1, -1, -1, 363, 364, -1, -1, -1, -1, 369, + 370, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, 256, -1, 419, + 419, 421, 262, -1, 424, -1, 265, -1, 267, -1, + 430, 270, -1, -1, -1, -1, 275, -1, -1, -1, + 279, -1, -1, -1, -1, -1, -1, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, 298, -1, + -1, 300, -1, -1, -1, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + -1, -1, -1, 322, -1, -1, -1, -1, -1, -1, + -1, 330, 331, -1, -1, 334, -1, -1, 337, 339, + -1, -1, -1, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, -1, 363, 364, -1, -1, -1, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, -1, 419, + 419, 421, 262, -1, -1, 261, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 284, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 298, -1, + -1, 297, -1, -1, -1, -1, 302, -1, -1, -1, + -1, 307, -1, 309, 310, 311, 312, -1, -1, -1, + -1, 317, -1, -1, -1, 321, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 333, -1, 339, + 336, -1, 338, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, -1, 363, 364, -1, 362, -1, 368, 369, + -1, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, -1, 419, + 261, 421, 262, 419, 424, -1, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 284, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 297, -1, 298, -1, + -1, 302, -1, -1, 305, -1, 307, -1, 309, 310, + 311, 312, -1, -1, -1, -1, 317, -1, -1, -1, + 321, -1, -1, -1, 325, -1, -1, -1, -1, -1, + -1, -1, 333, -1, -1, 336, -1, 338, -1, 339, + -1, -1, -1, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, 362, -1, 363, 364, -1, -1, -1, 369, 369, + -1, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, 419, 419, + 261, 421, 262, -1, 424, -1, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 284, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 297, -1, 298, -1, + -1, 302, -1, -1, 305, -1, 307, -1, 309, 310, + 311, 312, -1, -1, -1, -1, 317, -1, -1, -1, + 321, -1, -1, -1, 325, -1, -1, -1, -1, -1, + -1, -1, 333, -1, -1, 336, -1, 338, -1, 339, + -1, -1, -1, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, 362, -1, 363, 364, -1, -1, -1, 369, 369, + -1, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, 419, 419, + -1, 421, 262, -1, 424, -1, 265, -1, 267, -1, + 430, 270, -1, 272, 273, -1, 275, -1, 277, -1, + 279, -1, 281, 282, 283, -1, -1, -1, 287, 288, + -1, -1, -1, -1, 293, -1, 295, 296, 298, -1, + -1, 300, -1, -1, -1, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + -1, -1, -1, 322, 323, -1, -1, -1, -1, -1, + -1, 330, 331, -1, -1, 334, -1, -1, 337, 339, + -1, -1, -1, 342, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, -1, 363, 364, -1, -1, -1, -1, 369, + -1, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, -1, 419, + 419, 421, 262, -1, 424, -1, 265, -1, 267, -1, + 430, 270, -1, -1, 273, -1, 275, -1, 277, -1, + 279, -1, 281, 282, 283, -1, -1, -1, 287, 288, + -1, -1, -1, -1, 293, -1, 295, -1, 298, -1, + -1, 300, -1, -1, -1, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 256, -1, -1, 322, -1, -1, 262, -1, -1, -1, + 266, 330, 331, -1, -1, 334, -1, -1, 337, 339, + -1, -1, -1, 342, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, 298, 363, -1, -1, -1, -1, -1, 369, + -1, 371, -1, 373, -1, 375, 376, 377, 314, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, -1, -1, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, -1, 414, 256, -1, -1, -1, 419, + 419, 357, -1, -1, -1, -1, -1, 363, 364, -1, + 430, -1, -1, 369, 370, 371, 372, 373, 374, 375, + 376, 377, -1, 379, 380, 285, 382, 383, 384, 385, + 386, 387, 388, 389, 390, 391, -1, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, 410, 411, 412, 413, 414, -1, + 256, -1, -1, 419, -1, 421, 262, 327, 424, -1, + -1, -1, -1, -1, 430, -1, -1, -1, 339, -1, + -1, -1, -1, 344, -1, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, -1, -1, -1, -1, + -1, -1, 298, -1, -1, -1, -1, -1, 369, -1, + 371, -1, 373, -1, 375, 376, 377, -1, 378, 379, + 380, 381, -1, 383, 384, 385, 386, 387, 388, 389, + 390, -1, -1, 393, 394, 395, 396, 397, 398, 399, + 400, -1, -1, 339, -1, -1, -1, -1, 344, -1, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, + 356, -1, -1, -1, -1, 256, -1, 363, 364, 430, + -1, 262, -1, 369, 370, 371, 372, 373, 374, 375, + 376, 377, -1, 379, 380, -1, -1, 383, 384, 385, + 386, -1, -1, -1, 390, 391, -1, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 298, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 413, 414, -1, + -1, -1, -1, 419, 256, 421, -1, -1, 424, -1, + 262, -1, -1, -1, 430, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 339, -1, + -1, -1, -1, 344, -1, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, 298, -1, -1, -1, + -1, -1, 363, 364, -1, -1, -1, -1, 369, -1, + 371, 372, 373, 374, 375, 376, 377, -1, 379, 380, + -1, -1, 383, 384, 385, 386, -1, -1, -1, 390, + 391, -1, 393, 394, 395, 396, 397, 398, 399, 400, + 401, 402, -1, 256, -1, -1, -1, -1, -1, 262, + -1, -1, 413, 414, -1, -1, -1, -1, 419, -1, + 421, 363, 364, 424, -1, -1, -1, 369, 370, 430, + 372, 373, 374, 375, -1, 377, -1, 379, 380, -1, + 382, 383, 384, 385, 386, 298, 388, 389, 390, 391, + -1, 393, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, -1, -1, -1, -1, 419, -1, 421, + -1, -1, 424, -1, -1, -1, 339, -1, 430, -1, + -1, 344, -1, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, -1, -1, -1, -1, 256, -1, + 363, -1, -1, -1, 262, -1, 369, 370, 371, -1, + 373, -1, 375, 376, 377, -1, 379, 380, -1, -1, + 383, 384, 385, 386, -1, -1, -1, 390, 391, -1, + -1, -1, 395, 396, 397, 398, 399, 400, 401, 402, + 298, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 414, -1, -1, -1, -1, 419, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 430, -1, -1, + 256, -1, -1, -1, -1, -1, 262, -1, -1, -1, + -1, 339, -1, -1, -1, -1, 344, -1, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, 356, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 369, 298, 371, -1, 373, -1, 375, 376, 377, + -1, 379, 380, -1, -1, 383, 384, 385, 386, 387, + 388, 389, 390, 391, -1, -1, -1, 395, 396, 397, + 398, 399, 400, 401, 402, -1, -1, -1, -1, -1, + -1, -1, -1, 339, -1, -1, 414, -1, 344, -1, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, + 356, -1, 430, -1, -1, 256, -1, 363, -1, -1, + -1, 262, -1, 369, -1, 371, -1, 373, -1, 375, + 376, 377, -1, 379, 380, -1, -1, 383, 384, 385, + 386, -1, -1, -1, 390, 391, -1, -1, -1, 395, + 396, 397, 398, 399, 400, 401, 402, 298, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 414, -1, + -1, -1, -1, 419, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 430, -1, -1, 256, -1, -1, + -1, -1, -1, 262, -1, -1, -1, -1, 339, -1, + -1, -1, -1, 344, -1, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 369, 298, + 371, -1, 373, -1, 375, 376, 377, -1, 379, 380, + -1, -1, 383, 384, 385, 386, -1, -1, -1, 390, + 391, -1, -1, -1, 395, 396, 397, 398, 399, 400, + 401, 402, -1, -1, -1, -1, -1, -1, -1, -1, + 339, -1, -1, 414, -1, 344, -1, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, 430, + -1, -1, 256, -1, -1, -1, -1, -1, 262, -1, + 369, -1, 371, -1, 373, -1, 375, 376, 377, -1, + 379, 380, -1, -1, 383, 384, 385, 386, -1, -1, + -1, 390, 391, -1, -1, -1, 395, 396, 397, 398, + 399, 400, 401, 402, 298, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 414, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 430, -1, -1, 256, -1, -1, -1, -1, -1, + 262, -1, -1, -1, -1, 339, -1, -1, -1, -1, + 344, -1, 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 369, 298, 371, -1, 373, + -1, 375, 376, 377, -1, 379, 380, -1, -1, 383, + 384, 385, 386, -1, -1, -1, 390, 391, -1, -1, + -1, 395, 396, 397, 398, 399, 400, 401, 402, -1, + -1, -1, -1, -1, -1, -1, -1, 339, -1, -1, + 414, -1, 344, -1, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, -1, 430, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 369, -1, 371, + -1, 373, -1, 375, 376, 377, -1, 379, 380, -1, + -1, 383, 384, 385, 386, -1, -1, -1, 390, 391, + -1, -1, -1, 395, 396, 397, 398, 399, 400, 401, + 402, -1, 256, -1, 256, -1, -1, -1, -1, -1, + 264, 265, 414, 267, -1, -1, 270, 271, -1, -1, + -1, 275, 276, 277, -1, 279, -1, -1, 430, -1, + -1, 285, -1, -1, 288, -1, -1, -1, -1, -1, + -1, 295, -1, -1, -1, -1, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, -1, -1, -1, -1, 339, -1, 256, + -1, -1, 344, -1, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 359, 360, 361, 362, 363, + -1, -1, -1, -1, -1, -1, -1, 369, 372, 371, + -1, 373, -1, 375, 376, 377, -1, -1, -1, -1, + -1, 383, 384, 385, 386, -1, -1, -1, 390, 391, + -1, -1, -1, 395, 396, 397, 398, 399, 400, 401, + 402, -1, -1, -1, -1, -1, -1, -1, 256, -1, + -1, -1, 414, -1, 418, 419, -1, -1, -1, -1, + -1, -1, 339, -1, -1, 429, 430, 344, 430, 346, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 369, -1, 371, -1, 373, -1, 375, 376, + 377, -1, -1, -1, -1, -1, 383, 384, 385, 386, + -1, -1, -1, 390, 391, -1, -1, 256, 395, 396, + 397, 398, 399, 400, 401, 402, -1, -1, -1, -1, + -1, 339, -1, -1, -1, -1, 344, 414, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, 356, -1, + -1, -1, -1, 430, -1, -1, -1, -1, -1, -1, + -1, 369, -1, 371, -1, 373, -1, 375, 376, 377, + -1, -1, -1, -1, -1, 383, 384, 385, 386, -1, + -1, -1, 390, 391, -1, -1, 256, -1, -1, 397, + 398, 399, 400, 401, 402, -1, -1, -1, -1, -1, + 339, -1, -1, -1, -1, 344, 414, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, -1, + -1, -1, 430, -1, -1, -1, -1, -1, -1, -1, + 369, -1, 371, -1, 373, -1, 375, 376, 377, -1, + -1, -1, -1, -1, 383, 384, 385, 386, -1, -1, + -1, 390, 391, -1, -1, 256, -1, -1, 397, 398, + 399, 400, 401, 402, -1, -1, -1, -1, -1, 339, + -1, -1, -1, -1, 344, 414, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, 430, -1, -1, -1, -1, -1, -1, -1, 369, + -1, 371, -1, 373, -1, 375, 376, 377, -1, -1, + -1, -1, -1, 383, 384, 385, 386, -1, -1, -1, + 390, 391, -1, -1, 256, -1, -1, 397, 398, 399, + 400, 401, 402, -1, -1, -1, -1, -1, 339, -1, + -1, -1, -1, 344, 414, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, 369, -1, + 371, -1, 373, -1, 375, 376, 377, -1, -1, -1, + -1, -1, 383, 384, 385, 386, 256, -1, -1, 390, + 391, -1, 262, -1, -1, -1, 397, 398, 399, 400, + 401, 402, -1, -1, -1, -1, -1, 339, -1, -1, + -1, -1, 344, 414, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, -1, -1, -1, 298, 430, + -1, -1, -1, -1, -1, -1, -1, 369, -1, 371, + -1, 373, -1, 375, 376, 377, -1, -1, -1, -1, + -1, 383, 384, 385, 386, -1, -1, -1, 390, 391, + -1, -1, -1, -1, -1, 397, 398, 399, 400, 401, + 402, -1, -1, 256, -1, -1, -1, -1, -1, -1, + -1, -1, 414, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 364, -1, -1, -1, 430, 369, + -1, -1, 372, -1, 374, 375, -1, -1, -1, 379, + 380, -1, -1, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 256, 413, 414, -1, -1, -1, -1, -1, + -1, 421, -1, -1, 424, -1, 339, -1, -1, -1, + 430, 344, -1, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 369, -1, 371, -1, + 373, -1, 375, 376, 377, -1, -1, -1, -1, -1, + -1, -1, 385, 386, -1, -1, -1, 390, 391, -1, + -1, 256, -1, -1, -1, -1, 399, 400, 401, 402, + -1, -1, -1, -1, -1, 339, -1, -1, -1, -1, + 344, 414, 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, -1, -1, -1, -1, 430, -1, -1, + -1, -1, -1, -1, -1, 369, -1, 371, -1, 373, + -1, 375, 376, 377, -1, -1, -1, -1, -1, -1, + -1, 385, 386, -1, -1, -1, 390, 391, -1, -1, + 256, -1, -1, -1, -1, 399, 400, 401, 402, -1, + -1, -1, -1, -1, 339, -1, -1, -1, -1, 344, + 414, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, -1, -1, -1, -1, 430, -1, -1, -1, + -1, -1, -1, -1, 369, -1, 371, -1, 373, -1, + 375, 376, 377, -1, -1, -1, -1, -1, -1, -1, + 385, 386, -1, -1, -1, 390, 391, -1, -1, 256, + -1, -1, -1, -1, 399, 400, 401, 402, -1, -1, + -1, -1, -1, 339, -1, -1, -1, -1, 344, 414, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, + 356, -1, -1, -1, -1, 430, -1, -1, -1, -1, + -1, -1, -1, 369, -1, 371, -1, 373, -1, 375, + 376, 377, -1, -1, -1, -1, -1, -1, -1, 385, + 386, -1, -1, -1, 390, 391, -1, -1, 256, -1, + -1, -1, -1, -1, -1, 401, 402, -1, -1, -1, + -1, -1, 339, -1, -1, -1, -1, 344, 414, 346, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + -1, -1, -1, -1, 430, -1, -1, -1, -1, -1, + -1, -1, 369, -1, 371, -1, 373, -1, 375, 376, + 377, -1, -1, -1, -1, -1, -1, -1, 385, 386, + -1, -1, -1, 390, 391, -1, -1, 256, -1, -1, + -1, -1, -1, -1, 401, 402, -1, -1, -1, -1, + -1, 339, -1, -1, -1, -1, 344, 414, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, 356, -1, + -1, -1, -1, 430, -1, -1, -1, -1, -1, -1, + -1, 369, -1, 371, -1, 373, -1, 375, 376, 377, + -1, -1, -1, -1, -1, -1, -1, -1, 386, -1, + -1, -1, 390, 391, -1, -1, 256, -1, -1, -1, + -1, -1, -1, 401, 402, -1, -1, -1, -1, -1, + 339, -1, -1, -1, -1, 344, 414, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, -1, + -1, -1, 430, -1, -1, -1, -1, -1, -1, -1, + 369, -1, 371, -1, 373, -1, 375, 376, 377, -1, + -1, -1, -1, -1, -1, -1, -1, 386, -1, -1, + -1, 390, 391, -1, -1, 256, -1, -1, -1, -1, + -1, -1, 401, 402, -1, -1, -1, -1, -1, 339, + -1, -1, -1, -1, 344, 414, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, 430, -1, -1, -1, -1, -1, -1, -1, 369, + -1, 371, -1, 373, -1, 375, 376, 377, -1, -1, + -1, -1, -1, -1, -1, -1, 386, -1, -1, -1, + -1, 391, -1, -1, 256, -1, -1, -1, -1, -1, + -1, 401, 402, -1, -1, -1, -1, -1, 339, -1, + -1, -1, -1, 344, 414, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, 369, -1, + 371, -1, 373, -1, 375, 376, 377, -1, -1, -1, + -1, -1, -1, -1, -1, 386, -1, -1, -1, -1, + 391, -1, -1, 256, -1, -1, -1, -1, -1, -1, + 401, 402, -1, -1, -1, -1, -1, 339, -1, -1, + -1, -1, 344, 414, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, -1, -1, -1, -1, 430, + -1, -1, -1, -1, -1, -1, -1, 369, -1, 371, + -1, 373, -1, 375, 376, 377, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 391, + -1, -1, 256, -1, -1, -1, -1, -1, -1, 401, + 402, -1, -1, -1, -1, -1, 339, -1, -1, -1, + -1, 344, 414, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, -1, -1, -1, -1, 430, -1, + -1, -1, -1, -1, -1, -1, 369, -1, 371, -1, + 373, -1, 375, 376, 377, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 391, -1, + -1, 256, -1, -1, -1, -1, -1, -1, 401, 402, + -1, -1, -1, -1, -1, 339, -1, -1, -1, -1, + 344, 414, 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, -1, -1, -1, -1, 430, -1, -1, + -1, -1, -1, -1, -1, 369, -1, 371, -1, 373, + -1, 375, 376, 377, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 391, -1, -1, + -1, 262, -1, -1, -1, 266, -1, -1, 402, -1, + -1, -1, -1, -1, 339, -1, -1, -1, -1, 344, + 414, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, -1, -1, -1, -1, 430, 298, -1, -1, + -1, -1, -1, -1, 369, -1, 371, -1, 373, -1, + 375, 376, 377, 314, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 391, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 402, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 414, + -1, -1, -1, -1, -1, -1, 357, -1, -1, -1, + -1, -1, 363, 364, -1, 430, -1, -1, 369, 370, + -1, 372, -1, 374, -1, 376, 377, -1, 379, 380, + -1, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, -1, 393, 394, 395, 396, 397, 398, 399, 400, + 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, + 411, 412, 413, 414, -1, -1, 256, -1, 419, -1, + 421, -1, -1, 424, 264, 265, 266, 267, 268, 430, + 270, 271, -1, 273, 274, 275, 276, 277, 278, 279, + -1, -1, -1, -1, -1, 285, -1, 287, 288, 289, + 290, 291, 292, -1, -1, 295, -1, -1, -1, 299, + 300, -1, 302, 303, 304, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 314, -1, 316, -1, 318, 319, + -1, -1, 322, -1, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, -1, 337, -1, -1, + 340, 341, -1, -1, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 359, + 360, 361, 362, 363, -1, -1, -1, -1, 368, -1, + -1, -1, 372, -1, -1, -1, -1, 377, 378, 379, + 380, 381, -1, -1, -1, 385, -1, 387, -1, -1, + -1, -1, -1, 393, 394, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 256, -1, 418, 419, + 420, 421, -1, 423, 264, 265, 266, 267, -1, -1, + 270, 271, -1, 273, 274, 275, 276, 277, 278, 279, + -1, -1, -1, -1, -1, 285, -1, 287, 288, 289, + 290, 291, 292, -1, -1, 295, -1, -1, -1, 299, + 300, -1, 302, 303, 304, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 314, -1, 316, -1, 318, 319, + -1, -1, 322, -1, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, -1, 337, -1, -1, + 340, 341, -1, -1, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 359, + 360, 361, 362, 363, -1, -1, -1, -1, 368, -1, + -1, -1, 372, -1, -1, -1, -1, 377, 378, 379, + 380, 381, -1, -1, -1, 385, -1, 387, -1, -1, + -1, -1, -1, 393, 394, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 256, -1, -1, -1, 418, 419, + 420, 421, 264, 265, 266, 267, -1, -1, 270, 271, + -1, 273, 274, 275, 276, 277, 278, 279, -1, -1, + -1, -1, -1, 285, -1, 287, 288, 289, 290, 291, + 292, -1, -1, 295, -1, -1, -1, 299, 300, -1, + 302, 303, 304, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 314, -1, 316, -1, 318, 319, -1, -1, + 322, -1, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, 335, -1, 337, -1, -1, 340, 341, + -1, -1, 344, 345, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 359, 360, 361, + 362, 363, -1, -1, -1, -1, 368, -1, -1, -1, + 372, -1, -1, -1, -1, 377, 378, 379, 380, 381, + -1, -1, -1, 385, -1, 387, -1, -1, -1, -1, + -1, 393, 394, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 256, -1, -1, -1, 418, 419, 420, 421, + 264, 265, 266, 267, -1, -1, 270, 271, -1, 273, + 274, 275, 276, 277, 278, 279, -1, -1, -1, -1, + -1, 285, -1, 287, 288, 289, 290, 291, 292, -1, + -1, 295, -1, -1, -1, 299, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 314, -1, 316, -1, 318, 319, -1, -1, 322, -1, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 334, 335, -1, 337, -1, -1, 340, 341, -1, -1, + 344, 345, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, -1, 368, -1, -1, -1, 372, -1, + -1, -1, -1, 377, 378, 379, 380, 381, -1, -1, + -1, 385, -1, 387, -1, -1, -1, -1, -1, 393, + 394, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 256, -1, -1, -1, 418, 419, 420, 421, 264, 265, + 266, 267, -1, -1, 270, 271, -1, 273, 274, 275, + 276, 277, 278, 279, -1, -1, -1, -1, -1, 285, + -1, 287, 288, 289, 290, 291, 292, -1, -1, 295, + -1, -1, -1, 299, 300, -1, 302, 303, 304, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 314, -1, + 316, -1, 318, 319, -1, -1, 322, -1, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + -1, 337, -1, -1, 340, 341, -1, -1, 344, 345, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 359, 360, 361, 362, 363, -1, -1, + -1, -1, 368, -1, -1, -1, 372, -1, -1, -1, + -1, 377, 378, 379, 380, 381, -1, -1, -1, 385, + -1, 387, -1, -1, -1, -1, -1, 393, 394, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 256, -1, + -1, -1, 418, 419, 420, 421, 264, 265, -1, 267, + -1, -1, 270, 271, -1, -1, -1, 275, 276, 277, + -1, 279, -1, -1, 265, -1, 267, 285, -1, 270, + 288, -1, -1, -1, 275, -1, -1, 295, 279, -1, + -1, -1, 300, -1, 302, 303, 304, 288, 306, -1, + -1, -1, -1, -1, 295, 313, -1, -1, 316, 300, + 318, 319, -1, 304, 322, -1, -1, 325, -1, 327, + -1, 329, 330, 331, 332, 316, 334, 318, -1, -1, + -1, 322, -1, 341, -1, -1, 344, 345, -1, 330, + 331, -1, -1, 334, -1, -1, 337, -1, -1, -1, + -1, 359, 360, 361, 362, 363, -1, -1, -1, -1, + -1, -1, -1, -1, 372, -1, -1, 375, -1, -1, + 378, 379, 380, 381, -1, -1, -1, 385, -1, 387, + -1, -1, 373, -1, -1, 393, 394, -1, -1, -1, + -1, -1, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + 418, 419, 420, 421, 285, -1, -1, 288, -1, -1, + -1, -1, -1, -1, 295, -1, -1, -1, 419, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + 341, -1, -1, 344, 345, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, 368, 369, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, -1, -1, -1, + -1, -1, 393, 394, -1, -1, -1, -1, -1, -1, + 264, 265, -1, 267, -1, -1, 270, 271, -1, -1, + -1, 275, 276, 277, -1, 279, -1, 418, 419, 420, + 421, 285, -1, 424, 288, -1, -1, -1, 429, -1, + -1, 295, -1, -1, -1, -1, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, -1, -1, -1, -1, 341, -1, -1, + 344, 345, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, -1, 368, -1, -1, -1, 372, -1, + -1, -1, -1, -1, 378, 379, 380, 381, -1, -1, + -1, 385, -1, 387, -1, -1, -1, -1, -1, 393, + 394, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, 418, 419, 420, 421, -1, -1, + 424, 264, 265, -1, 267, 429, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, 265, + -1, 267, 285, -1, 270, 288, -1, -1, -1, 275, + -1, -1, 295, 279, -1, -1, -1, 300, -1, 302, + 303, 304, 288, -1, -1, -1, -1, -1, -1, 295, + -1, -1, -1, 316, 300, 318, 319, -1, 304, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + 316, 334, 318, -1, -1, -1, 322, -1, 341, -1, + -1, 344, 345, -1, 330, 331, -1, -1, 334, -1, + -1, 337, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, 368, 369, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 371, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 256, -1, -1, -1, 418, 419, 420, 421, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, 419, 279, -1, -1, 265, -1, 267, + 285, -1, 270, 288, -1, -1, -1, 275, -1, -1, + 295, 279, -1, -1, -1, 300, -1, 302, 303, 304, + 288, 306, -1, -1, -1, -1, -1, 295, 313, -1, + -1, 316, 300, 318, 319, -1, 304, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, 316, 334, + 318, -1, -1, -1, 322, -1, 341, -1, -1, 344, + 345, -1, 330, 331, -1, -1, 334, -1, -1, 337, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, -1, -1, -1, -1, -1, 393, 394, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 256, + -1, -1, -1, 418, 419, 420, 421, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, 419, 279, -1, -1, 265, -1, 267, 285, -1, + 270, 288, -1, -1, -1, 275, -1, -1, 295, 279, + -1, -1, -1, 300, -1, 302, 303, 304, 288, -1, + -1, -1, -1, -1, -1, 295, -1, -1, -1, 316, + 300, 318, 319, 320, 304, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, 316, 334, 318, -1, + -1, -1, 322, -1, 341, -1, -1, 344, 345, -1, + 330, 331, -1, -1, 334, -1, -1, 337, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, 368, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, -1, -1, -1, -1, -1, 393, 394, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, -1, + -1, 418, 419, 420, 421, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, 419, + 279, -1, -1, -1, -1, -1, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, 341, -1, -1, 344, 345, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, 368, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, -1, + -1, -1, -1, -1, 393, 394, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, 418, + 419, 420, 421, 262, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, 298, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, 343, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, 374, -1, -1, -1, 378, + 379, 380, 381, 382, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, 341, -1, -1, 344, 345, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, 369, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, -1, -1, -1, -1, -1, 393, 394, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, -1, + -1, 418, 419, 420, 421, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, 265, -1, 267, 285, -1, 270, 288, + -1, -1, -1, 275, -1, -1, 295, 279, -1, -1, + -1, 300, -1, 302, 303, 304, 288, -1, -1, -1, + -1, -1, -1, 295, -1, -1, -1, 316, 300, 318, + 319, -1, 304, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, 316, 334, 318, -1, -1, -1, + 322, -1, 341, -1, -1, 344, 345, -1, 330, 331, + -1, -1, 334, -1, -1, 337, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + 369, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, -1, + -1, -1, -1, -1, 393, 394, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 256, -1, -1, -1, 418, + 419, 420, 421, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, 419, 279, -1, + -1, 265, -1, 267, 285, -1, 270, 288, -1, -1, + -1, 275, -1, -1, 295, 279, -1, -1, -1, 300, + -1, 302, 303, 304, 288, -1, -1, -1, -1, -1, + -1, 295, -1, -1, -1, 316, 300, 318, 319, -1, + 304, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, 316, 334, 318, -1, -1, -1, 322, -1, + 341, -1, -1, 344, 345, -1, 330, 331, -1, -1, + 334, -1, -1, 337, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, -1, -1, -1, + -1, -1, 393, 394, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, -1, -1, -1, 418, 419, 420, + 421, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, 419, 279, -1, -1, -1, + -1, -1, 285, -1, -1, 288, -1, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, 341, -1, + -1, 344, 345, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, -1, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, -1, 418, 419, 420, 421, 262, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, -1, -1, 288, -1, -1, -1, -1, + -1, -1, 295, -1, -1, 298, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + 343, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, 374, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + 341, -1, -1, 344, 345, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, -1, -1, -1, + -1, -1, 393, 394, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, -1, -1, -1, 418, 419, 420, + 421, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, 265, + -1, 267, 285, -1, 270, 288, -1, -1, -1, 275, + -1, -1, 295, 279, -1, -1, -1, 300, -1, 302, + 303, 304, 288, -1, -1, -1, -1, -1, -1, 295, + -1, -1, -1, 316, 300, 318, 319, -1, 304, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + 316, 334, 318, -1, -1, -1, 322, -1, 341, -1, + -1, 344, 345, -1, 330, 331, -1, -1, 334, -1, + -1, 337, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, -1, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 256, -1, -1, -1, 418, 419, 420, 421, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, 419, 279, -1, -1, 265, -1, 267, + 285, -1, 270, 288, -1, -1, -1, 275, -1, -1, + 295, 279, -1, -1, -1, 300, -1, 302, 303, 304, + 288, -1, -1, -1, -1, -1, -1, 295, -1, -1, + -1, 316, 300, 318, 319, -1, 304, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, 316, 334, + 318, -1, -1, -1, 322, -1, 341, -1, -1, 344, + 345, -1, 330, 331, -1, -1, 334, -1, -1, 337, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, -1, -1, -1, -1, -1, 393, 394, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 256, + -1, -1, -1, 418, 419, 420, 421, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, 419, 279, -1, -1, 265, -1, 267, 285, -1, + 270, 288, -1, -1, -1, 275, -1, -1, 295, 279, + -1, -1, -1, 300, -1, 302, 303, 304, 288, -1, + -1, -1, -1, -1, -1, 295, -1, -1, -1, 316, + 300, 318, 319, -1, 304, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, 316, 334, 318, -1, + -1, -1, 322, -1, 341, -1, -1, 344, 345, -1, + 330, 331, -1, -1, 334, -1, -1, 337, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, -1, -1, -1, -1, -1, 393, 394, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, -1, + -1, 418, 419, 420, 421, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, 419, + 279, -1, -1, -1, -1, -1, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, 341, -1, -1, 344, 345, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, -1, + -1, -1, -1, -1, 393, 394, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, 418, + 419, 420, 421, 262, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, 298, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, 374, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, 374, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, -1, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, -1, -1, -1, -1, 261, -1, + -1, -1, 265, -1, 267, 418, 419, 270, 421, 272, + 273, -1, 275, -1, 277, -1, 279, -1, 281, 282, + 283, 284, -1, -1, 287, 288, -1, -1, -1, -1, + 293, -1, 295, 296, 297, -1, -1, 300, -1, 302, + -1, 304, -1, -1, 307, -1, 309, 310, 311, 312, + -1, -1, -1, 316, 317, 318, -1, -1, 321, 322, + 323, -1, -1, -1, -1, -1, -1, 330, 331, -1, + 333, 334, 256, 336, 337, 338, -1, -1, -1, 342, + 264, 265, -1, 267, -1, -1, 270, 271, -1, -1, + -1, 275, 276, 277, -1, 279, -1, -1, -1, 362, + -1, 285, -1, -1, 288, 368, 369, 370, -1, -1, + -1, 295, -1, -1, -1, 378, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, 337, -1, -1, 419, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, 256, -1, -1, -1, -1, 372, -1, + 262, 264, 265, -1, 267, 379, 380, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, -1, -1, 288, -1, -1, -1, 261, + -1, 263, 295, -1, -1, -1, 298, 300, -1, 302, + 303, 304, -1, -1, 418, 419, -1, -1, -1, -1, + -1, -1, 284, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, 297, 329, 330, 331, 332, + 302, 334, -1, -1, -1, 307, -1, 309, 310, 311, + 312, -1, -1, 315, -1, 317, -1, -1, -1, 321, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, 333, 364, -1, 336, -1, 338, -1, -1, 372, + 372, 373, 374, 375, 376, -1, -1, 379, 380, -1, + -1, 383, 384, 385, 386, 387, 388, 389, 390, 391, + 362, 393, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, -1, -1, 418, 419, -1, -1, 421, + -1, 261, 424, 263, -1, 265, -1, 267, -1, -1, + 270, -1, 272, 273, -1, 275, -1, 277, -1, 279, + -1, 281, 282, 283, 284, -1, -1, 287, 288, -1, + -1, -1, -1, 293, 294, 295, 296, 297, -1, -1, + 300, -1, 302, -1, 304, -1, 306, 307, -1, 309, + 310, 311, 312, -1, -1, 315, 316, 317, 318, -1, + -1, 321, 322, 323, -1, -1, -1, -1, -1, -1, + 330, 331, -1, 333, 334, -1, 336, 337, 338, -1, + -1, -1, 342, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 362, -1, -1, 365, 366, 261, -1, -1, + -1, 265, -1, 267, -1, -1, 270, -1, 272, 273, + -1, 275, -1, 277, -1, 279, -1, 281, 282, 283, + 284, -1, -1, 287, 288, -1, -1, -1, -1, 293, + -1, 295, 296, 297, -1, -1, 300, -1, 302, -1, + 304, -1, -1, 307, -1, 309, 310, 311, 312, 419, + -1, -1, 316, 317, 318, -1, -1, 321, 322, 323, + -1, -1, -1, -1, -1, -1, 330, 331, -1, 333, + 334, -1, 336, 337, 338, -1, -1, -1, 342, -1, + -1, 261, -1, -1, -1, 265, -1, 267, -1, -1, + 270, -1, 272, 273, -1, 275, -1, 277, 362, 279, + -1, 281, 282, 283, 284, 369, -1, 287, 288, -1, + -1, -1, -1, 293, 378, 295, 296, 297, -1, -1, + 300, -1, 302, -1, 304, -1, -1, 307, -1, 309, + 310, 311, 312, -1, -1, -1, 316, 317, 318, -1, + -1, 321, 322, 323, -1, -1, -1, -1, -1, -1, + 330, 331, -1, 333, 334, 419, 336, 337, 338, -1, + -1, -1, 342, -1, -1, 261, -1, -1, -1, 265, + -1, 267, -1, -1, 270, -1, 272, 273, -1, 275, + -1, 277, 362, 279, -1, 281, 282, 283, 284, 369, + -1, 287, 288, -1, -1, -1, -1, 293, 378, 295, + 296, 297, -1, -1, 300, -1, 302, -1, 304, -1, + -1, 307, -1, 309, 310, 311, 312, -1, -1, -1, + 316, 317, 318, -1, -1, 321, 322, 323, -1, -1, + -1, -1, -1, -1, 330, 331, -1, 333, 334, 419, + 336, 337, 338, -1, -1, -1, 342, -1, -1, 261, + -1, -1, -1, 265, -1, 267, -1, -1, 270, -1, + 272, 273, -1, 275, -1, 277, 362, 279, -1, 281, + 282, 283, 284, -1, -1, 287, 288, -1, -1, -1, + -1, 293, 378, 295, 296, 297, -1, -1, 300, -1, + 302, -1, 304, -1, -1, 307, -1, 309, 310, 311, + 312, -1, -1, -1, 316, 317, 318, -1, -1, 321, + 322, 323, -1, -1, -1, -1, -1, -1, 330, 331, + -1, 333, 334, 419, 336, 337, 338, -1, -1, -1, + 342, -1, -1, 261, -1, -1, -1, 265, -1, 267, + -1, -1, 270, -1, 272, 273, -1, 275, -1, 277, + 362, 279, -1, 281, 282, 283, 284, 369, -1, 287, + 288, -1, -1, -1, -1, 293, -1, 295, 296, 297, + -1, -1, 300, -1, 302, 261, 304, -1, -1, 307, + -1, 309, 310, 311, 312, -1, -1, -1, 316, 317, + 318, -1, -1, 321, 322, 323, -1, -1, 284, -1, + -1, -1, 330, 331, -1, 333, 334, 419, 336, 337, + 338, 297, -1, -1, 342, -1, 302, -1, -1, 305, + -1, 307, -1, 309, 310, 311, 312, -1, -1, -1, + -1, 317, -1, -1, 362, 321, -1, -1, -1, 325, + -1, 369, -1, -1, -1, -1, -1, 333, -1, -1, + 336, -1, 338, -1, 264, 265, -1, 267, -1, -1, + 270, 271, -1, -1, -1, 275, 276, 277, -1, 279, + -1, 357, -1, -1, -1, 285, 362, -1, 288, -1, + -1, -1, -1, 369, 370, 295, 372, -1, 374, -1, + 300, 419, 302, 303, 304, -1, 306, -1, -1, -1, + -1, 387, -1, 313, -1, -1, 316, -1, 318, 319, + -1, -1, 322, -1, -1, 325, -1, 327, -1, 329, + 330, 331, 332, -1, 334, -1, -1, -1, -1, -1, + -1, 341, -1, 419, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 359, + 360, 361, 362, 363, -1, -1, -1, -1, -1, -1, + -1, -1, 372, 373, -1, 375, -1, -1, 378, 379, + 380, 381, -1, -1, -1, 385, -1, 387, -1, -1, + -1, -1, -1, 393, 394, -1, -1, -1, -1, -1, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, 418, 419, + 420, 421, 285, -1, -1, 288, -1, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, 306, -1, -1, -1, -1, -1, -1, + 313, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, 341, -1, + -1, 344, 345, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, 375, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, -1, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, 264, 265, + -1, 267, -1, -1, 270, 271, -1, -1, -1, 275, + 276, 277, -1, 279, -1, 418, 419, 420, 421, 285, + -1, -1, 288, -1, -1, -1, -1, -1, -1, 295, + -1, -1, -1, -1, 300, -1, 302, 303, 304, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 316, -1, 318, 319, -1, -1, 322, -1, -1, 325, + -1, 327, -1, 329, 330, 331, 332, -1, 334, -1, + -1, 337, -1, -1, -1, 341, -1, -1, 344, 345, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 359, 360, 361, 362, 363, -1, -1, + -1, -1, -1, -1, -1, -1, 372, -1, -1, -1, + -1, -1, 378, 379, 380, 381, -1, -1, -1, 385, + -1, 387, -1, -1, -1, -1, -1, 393, 394, -1, + -1, -1, -1, -1, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, 418, 419, 420, 421, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, 341, -1, -1, 344, 345, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, 368, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, -1, + -1, -1, -1, -1, 393, 394, -1, -1, -1, -1, + -1, -1, 264, 265, -1, 267, -1, -1, 270, 271, + -1, -1, -1, 275, 276, 277, -1, 279, -1, 418, + 419, 420, 421, 285, -1, -1, 288, -1, -1, -1, + -1, -1, -1, 295, -1, -1, -1, -1, 300, -1, + 302, 303, 304, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 316, -1, 318, 319, -1, -1, + 322, -1, -1, 325, -1, 327, -1, 329, 330, 331, + 332, -1, 334, -1, -1, -1, -1, -1, -1, 341, + -1, -1, 344, 345, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 359, 360, 361, + 362, 363, -1, -1, -1, -1, 368, -1, -1, -1, + 372, -1, -1, -1, -1, -1, 378, 379, 380, 381, + -1, -1, -1, 385, -1, 387, -1, -1, -1, -1, + -1, 393, 394, -1, -1, -1, -1, -1, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, 418, 419, 420, 421, + 285, -1, -1, 288, -1, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, 341, -1, -1, 344, + 345, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, -1, -1, -1, -1, -1, 393, 394, + -1, -1, -1, -1, -1, -1, 264, 265, -1, 267, + -1, -1, 270, 271, -1, -1, -1, 275, 276, 277, + -1, 279, -1, 418, 419, 420, 421, 285, -1, -1, + 288, -1, -1, -1, -1, -1, -1, 295, -1, -1, + -1, -1, 300, -1, 302, 303, 304, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 316, -1, + 318, 319, -1, -1, 322, -1, -1, 325, -1, 327, + -1, 329, 330, 331, 332, -1, 334, -1, -1, -1, + -1, -1, -1, 341, -1, -1, 344, 345, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 359, 360, 361, 362, 363, -1, -1, -1, -1, + -1, -1, -1, -1, 372, -1, -1, -1, -1, -1, + 378, 379, 380, 381, -1, -1, -1, 385, -1, 387, + -1, -1, -1, -1, -1, 393, 394, -1, -1, -1, + -1, -1, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + 418, 419, 420, 421, 285, -1, -1, 288, -1, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + 341, -1, -1, 344, 345, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, -1, -1, -1, + -1, -1, 393, 394, -1, -1, -1, -1, -1, -1, + 264, 265, -1, 267, -1, -1, 270, 271, -1, -1, + -1, 275, 276, 277, -1, 279, -1, 418, 419, 420, + 421, 285, -1, -1, 288, -1, -1, -1, -1, -1, + -1, 295, -1, -1, -1, -1, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, -1, -1, -1, -1, 341, -1, -1, + 344, 345, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, -1, -1, -1, -1, -1, 372, -1, + -1, -1, -1, -1, 378, 379, 380, 381, -1, -1, + -1, 385, -1, 387, -1, -1, -1, -1, -1, 393, + 394, -1, -1, -1, -1, -1, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, 418, 419, 420, 421, 285, -1, + -1, 288, -1, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, -1, 264, 265, -1, 267, 393, 394, 270, 271, + -1, -1, -1, 275, 276, 277, -1, 279, -1, -1, + -1, -1, -1, 285, -1, -1, 288, -1, -1, -1, + -1, 418, 419, 295, 421, -1, -1, -1, 300, -1, + 302, 303, 304, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 316, -1, 318, 319, -1, -1, + 322, -1, -1, 325, -1, 327, -1, 329, 330, 331, + 332, -1, 334, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 359, 360, 361, + 362, 363, -1, -1, -1, -1, -1, -1, -1, -1, + 372, -1, -1, -1, -1, -1, 378, 379, 380, 381, + -1, -1, -1, 385, -1, 387, -1, -1, -1, -1, + -1, 393, 394, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 263, -1, 265, -1, 267, 418, 419, 270, 421, + 272, 273, -1, 275, -1, 277, -1, 279, -1, 281, + 282, 283, -1, -1, -1, 287, 288, -1, -1, -1, + -1, 293, -1, 295, 296, -1, -1, -1, 300, -1, + -1, -1, 304, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 315, 316, -1, 318, -1, -1, -1, + 322, 323, -1, -1, -1, -1, -1, -1, 330, 331, + 264, 265, 334, 267, -1, 337, 270, 271, -1, -1, + 342, 275, 276, 277, -1, 279, -1, -1, -1, -1, + -1, 285, -1, -1, 288, -1, -1, -1, -1, -1, + -1, 295, -1, 365, 366, -1, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, 378, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, 337, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 419, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, -1, -1, 265, -1, 267, 372, -1, + 270, -1, 272, 273, -1, 275, -1, 277, -1, 279, + -1, 281, 282, 283, -1, -1, -1, 287, 288, -1, + -1, -1, -1, 293, -1, 295, 296, -1, -1, -1, + 300, -1, -1, -1, 304, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 418, 419, 316, -1, 318, -1, + -1, -1, 322, 323, -1, -1, -1, -1, -1, -1, + 330, 331, -1, 265, 334, 267, -1, 337, 270, -1, + -1, 273, 342, 275, -1, 277, -1, 279, -1, 281, + 282, 283, -1, -1, -1, 287, 288, -1, -1, -1, + -1, 293, -1, 295, -1, 265, -1, 267, 300, -1, + 270, -1, 304, -1, -1, 275, -1, -1, 378, 279, + -1, -1, -1, -1, 316, -1, 318, -1, 288, -1, + 322, -1, -1, -1, -1, 295, -1, -1, 330, 331, + 300, -1, 334, -1, 304, 337, 306, -1, 308, -1, + 342, -1, -1, 313, -1, -1, 316, -1, 318, 419, + -1, -1, 322, -1, -1, 325, -1, -1, -1, -1, + 330, 331, -1, 265, 334, 267, -1, 337, 270, -1, + -1, -1, -1, 275, -1, -1, 378, 279, -1, -1, + -1, -1, -1, -1, -1, -1, 288, -1, -1, -1, + -1, -1, -1, 295, -1, 265, -1, 267, 300, -1, + 270, -1, 304, 373, 306, 275, 308, -1, -1, 279, + -1, 313, -1, -1, 316, -1, 318, 419, 288, -1, + 322, -1, -1, 325, -1, 295, -1, -1, 330, 331, + 300, -1, 334, -1, 304, 337, 306, -1, -1, -1, + -1, -1, -1, 313, -1, -1, 316, -1, 318, 419, + -1, -1, 322, -1, -1, 325, -1, -1, -1, -1, + 330, 331, -1, -1, 334, -1, -1, 337, 265, 371, + 267, -1, -1, 270, -1, -1, -1, -1, 275, -1, + -1, -1, 279, -1, -1, -1, -1, -1, -1, -1, + -1, 288, -1, 363, -1, -1, -1, -1, 295, -1, + 265, -1, 267, 300, -1, 270, -1, 304, -1, 306, + 275, 308, -1, -1, 279, -1, 313, 419, -1, 316, + -1, 318, -1, 288, -1, 322, -1, -1, 325, -1, + 295, -1, -1, 330, 331, 300, -1, 334, -1, 304, + 337, 306, -1, 308, -1, 265, -1, 267, 313, 419, + 270, 316, -1, 318, -1, 275, -1, 322, -1, 279, + 325, -1, -1, 283, -1, 330, 331, -1, 288, 334, + -1, -1, 337, 293, -1, 295, -1, 265, -1, 267, + 300, -1, 270, -1, 304, 305, -1, 275, -1, -1, + -1, 279, -1, -1, -1, -1, 316, -1, 318, -1, + 288, -1, 322, -1, -1, -1, -1, 295, -1, -1, + 330, 331, 300, -1, 334, -1, 304, 337, -1, -1, + -1, 261, 419, -1, -1, -1, -1, -1, 316, -1, + 318, -1, 272, -1, 322, -1, -1, 277, -1, -1, + -1, 281, 330, 331, 284, -1, 334, -1, -1, 337, + -1, -1, -1, -1, 419, -1, 296, 297, -1, -1, + -1, 301, 302, -1, -1, -1, -1, 307, -1, 309, + 310, 311, 312, -1, -1, 363, -1, 317, -1, -1, + -1, 321, -1, 323, -1, -1, -1, -1, -1, -1, + -1, -1, 261, 333, -1, 335, 336, -1, 338, 419, + -1, -1, 342, 272, -1, -1, -1, -1, 277, -1, + -1, -1, 281, -1, -1, 284, -1, -1, -1, -1, + -1, -1, 362, -1, -1, -1, -1, 296, 297, 369, + 370, 419, 301, 302, 261, -1, -1, -1, 307, -1, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + -1, -1, 321, -1, 323, -1, -1, 284, -1, -1, + -1, -1, -1, -1, 333, -1, -1, 336, -1, 338, + 297, -1, 261, 342, 263, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, -1, 315, -1, + 317, -1, -1, 362, 321, 284, -1, -1, -1, -1, + 369, 370, -1, -1, -1, -1, 333, -1, 297, 336, + -1, 338, -1, 302, -1, -1, -1, -1, 307, -1, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + -1, -1, 321, -1, -1, 362, -1, -1, -1, -1, + -1, -1, 369, 370, 333, -1, -1, 336, 261, 338, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 272, + -1, -1, -1, -1, 277, -1, -1, -1, 281, -1, + -1, 284, -1, 362, -1, -1, -1, -1, -1, -1, + 369, 370, -1, 296, 297, -1, -1, -1, 301, 302, + -1, 261, -1, -1, 307, -1, 309, 310, 311, 312, + -1, -1, 272, -1, 317, -1, -1, 277, 321, -1, + 323, 281, -1, -1, 284, -1, -1, -1, -1, -1, + 333, -1, -1, 336, -1, 338, 296, 297, -1, 342, + -1, 301, 302, -1, 261, -1, -1, 307, -1, 309, + 310, 311, 312, -1, -1, -1, -1, 317, -1, 362, + -1, 321, -1, 323, -1, -1, 369, 284, -1, -1, + -1, -1, -1, 333, -1, -1, 336, -1, 338, -1, + 297, -1, 342, -1, 261, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, 362, -1, 321, -1, -1, 284, -1, 369, + -1, -1, -1, -1, -1, -1, 333, -1, -1, 336, + 297, 338, -1, -1, 261, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, -1, -1, 321, 362, -1, 284, 365, 366, + -1, -1, 369, -1, -1, -1, 333, -1, -1, 336, + 297, 338, -1, -1, 261, 302, 263, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, -1, 315, -1, + 317, -1, -1, -1, 321, 362, -1, 284, 365, 366, + -1, -1, 369, -1, 261, -1, 333, -1, -1, 336, + 297, 338, -1, -1, -1, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, 284, -1, -1, + 317, -1, -1, -1, 321, 362, -1, -1, -1, -1, + 297, -1, 369, -1, 261, 302, 333, -1, -1, 336, + 307, 338, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, -1, -1, 321, -1, -1, 284, -1, -1, + -1, -1, -1, -1, 261, 362, 333, -1, -1, 336, + 297, 338, 369, -1, 301, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, 284, -1, -1, + 317, -1, -1, -1, 321, 362, -1, -1, 365, 366, + 297, -1, -1, -1, -1, 302, 333, -1, -1, 336, + 307, 338, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, -1, -1, 321, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 362, 333, -1, -1, 336, + -1, 338, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 362, + }; + +#line 7338 "cs-parser.jay" + +// +// A class used to hold info about an operator declarator +// +class OperatorDeclaration { + public readonly Operator.OpType optype; + public readonly FullNamedExpression ret_type; + public readonly Location location; + + public OperatorDeclaration (Operator.OpType op, FullNamedExpression ret_type, Location location) + { + optype = op; + this.ret_type = ret_type; + this.location = location; + } +} + +void Error_ExpectingTypeName (Expression expr) +{ + if (expr is Invocation){ + report.Error (1002, expr.Location, "Expecting `;'"); + } else { + expr.Error_InvalidExpressionStatement (report); + } +} + +void Error_ParameterModifierNotValid (string modifier, Location loc) +{ + report.Error (631, loc, "The parameter modifier `{0}' is not valid in this context", + modifier); +} + +void Error_DuplicateParameterModifier (Location loc, Parameter.Modifier mod) +{ + report.Error (1107, loc, "Duplicate parameter modifier `{0}'", + Parameter.GetModifierSignature (mod)); +} + +void Error_TypeExpected (Location loc) +{ + report.Error (1031, loc, "Type expected"); +} + +void Error_UnsafeCodeNotAllowed (Location loc) +{ + report.Error (227, loc, "Unsafe code requires the `unsafe' command line option to be specified"); +} + +void Warning_EmptyStatement (Location loc) +{ + report.Warning (642, 3, loc, "Possible mistaken empty statement"); +} + +void Error_NamedArgumentExpected (NamedArgument a) +{ + report.Error (1738, a.Location, "Named arguments must appear after the positional arguments"); +} + +void Error_MissingInitializer (Location loc) +{ + report.Error (210, loc, "You must provide an initializer in a fixed or using statement declaration"); +} + +object Error_AwaitAsIdentifier (object token) +{ + if (async_block) { + report.Error (4003, GetLocation (token), "`await' cannot be used as an identifier within an async method or lambda expression"); + return new LocatedToken ("await", GetLocation (token)); + } + + return token; +} + +void push_current_container (TypeDefinition tc, object partial_token) +{ + if (module.Evaluator != null){ + tc.Definition.Modifiers = tc.ModFlags = (tc.ModFlags & ~Modifiers.AccessibilityMask) | Modifiers.PUBLIC; + if (undo == null) + undo = new Undo (); + + undo.AddTypeContainer (current_container, tc); + } + + if (partial_token != null) + current_container.AddPartial (tc); + else + current_container.AddTypeContainer (tc); + + ++lexer.parsing_declaration; + current_container = tc; + current_type = tc; +} + +TypeContainer pop_current_class () +{ + var retval = current_container; + + current_container = current_container.Parent; + current_type = current_type.Parent as TypeDefinition; + + return retval; +} + +[System.Diagnostics.Conditional ("FULL_AST")] +void StoreModifierLocation (object token, Location loc) +{ + if (lbag == null) + return; + + if (mod_locations == null) + mod_locations = new List> (); + + mod_locations.Add (Tuple.Create ((Modifiers) token, loc)); +} + +List> GetModifierLocations () +{ + var result = mod_locations; + mod_locations = null; + return result; +} + +[System.Diagnostics.Conditional ("FULL_AST")] +void PushLocation (Location loc) +{ + if (location_stack == null) + location_stack = new Stack (); + + location_stack.Push (loc); +} + +Location PopLocation () +{ + if (location_stack == null) + return Location.Null; + + return location_stack.Pop (); +} + +string CheckAttributeTarget (int token, string a, Location l) +{ + switch (a) { + case "assembly" : case "module" : case "field" : case "method" : case "param" : case "property" : case "type" : + return a; + } + + if (!Tokenizer.IsValidIdentifier (a)) { + Error_SyntaxError (token); + } else { + report.Warning (658, 1, l, + "`{0}' is invalid attribute target. All attributes in this attribute section will be ignored", a); + } + + return string.Empty; +} + +static bool IsUnaryOperator (Operator.OpType op) +{ + switch (op) { + + case Operator.OpType.LogicalNot: + case Operator.OpType.OnesComplement: + case Operator.OpType.Increment: + case Operator.OpType.Decrement: + case Operator.OpType.True: + case Operator.OpType.False: + case Operator.OpType.UnaryPlus: + case Operator.OpType.UnaryNegation: + return true; + } + return false; +} + +void syntax_error (Location l, string msg) +{ + report.Error (1003, l, "Syntax error, " + msg); +} + +Tokenizer lexer; + +public Tokenizer Lexer { + get { + return lexer; + } +} + +public CSharpParser (SeekableStreamReader reader, CompilationSourceFile file, ParserSession session) + : this (reader, file, file.Compiler.Report, session) +{ +} + +public CSharpParser (SeekableStreamReader reader, CompilationSourceFile file, Report report, ParserSession session) +{ + this.file = file; + current_container = current_namespace = file; + + this.module = file.Module; + this.compiler = file.Compiler; + this.settings = compiler.Settings; + this.report = report; + + lang_version = settings.Version; + yacc_verbose_flag = settings.VerboseParserFlag; + doc_support = settings.DocumentationFile != null; + lexer = new Tokenizer (reader, file, session, report); + oob_stack = new Stack (); + lbag = session.LocationsBag; + use_global_stacks = session.UseJayGlobalArrays; + parameters_bucket = session.ParametersStack; +} + +public void parse () +{ + eof_token = Token.EOF; + + try { + if (yacc_verbose_flag > 1) + yyparse (lexer, new yydebug.yyDebugSimple ()); + else + yyparse (lexer); + + Tokenizer tokenizer = lexer as Tokenizer; + tokenizer.cleanup (); + } catch (Exception e){ + if (e is yyParser.yyUnexpectedEof) { + Error_SyntaxError (yyToken); + UnexpectedEOF = true; + return; + } + + if (e is yyParser.yyException) { + if (report.Errors == 0) + report.Error (-25, lexer.Location, "Parsing error"); + } else { + // Used by compiler-tester to test internal errors + if (yacc_verbose_flag > 0 || e is FatalException) + throw; + + report.Error (589, lexer.Location, "Internal compiler error during parsing" + e); + } + } +} + +void CheckToken (int error, int yyToken, string msg, Location loc) +{ + if (yyToken >= Token.FIRST_KEYWORD && yyToken <= Token.LAST_KEYWORD) + report.Error (error, loc, "{0}: `{1}' is a keyword", msg, GetTokenName (yyToken)); + else + report.Error (error, loc, msg); +} + +string ConsumeStoredComment () +{ + string s = tmpComment; + tmpComment = null; + Lexer.doc_state = XmlCommentState.Allowed; + return s; +} + +void FeatureIsNotAvailable (Location loc, string feature) +{ + report.FeatureIsNotAvailable (compiler, loc, feature); +} + +Location GetLocation (object obj) +{ + var lt = obj as LocatedToken; + if (lt != null) + return lt.Location; + + var mn = obj as MemberName; + if (mn != null) + return mn.Location; + + var expr = obj as Expression; + if (expr != null) + return expr.Location; + + return lexer.Location; +} + +void start_block (Location loc) +{ + if (current_block == null) { + current_block = new ToplevelBlock (compiler, current_local_parameters, loc); + parsing_anonymous_method = false; + } else if (parsing_anonymous_method) { + current_block = new ParametersBlock (current_block, current_local_parameters, loc); + parsing_anonymous_method = false; + } else { + current_block = new ExplicitBlock (current_block, loc, Location.Null); + } +} + +Block +end_block (Location loc) +{ + Block retval = current_block.Explicit; + retval.SetEndLocation (loc); + current_block = retval.Parent; + return retval; +} + +void start_anonymous (bool isLambda, ParametersCompiled parameters, bool isAsync, Location loc) +{ + oob_stack.Push (current_anonymous_method); + oob_stack.Push (current_local_parameters); + oob_stack.Push (current_variable); + oob_stack.Push (async_block); + + current_local_parameters = parameters; + if (isLambda) { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (loc, "lambda expressions"); + + current_anonymous_method = new LambdaExpression (loc); + } else { + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (loc, "anonymous methods"); + + current_anonymous_method = new AnonymousMethodExpression (loc); + } + current_anonymous_method.IsAsync = isAsync; + + async_block = isAsync; + // Force the next block to be created as a ToplevelBlock + parsing_anonymous_method = true; +} + +/* + * Completes the anonymous method processing, if lambda_expr is null, this + * means that we have a Statement instead of an Expression embedded + */ +AnonymousMethodExpression end_anonymous (ParametersBlock anon_block) +{ + AnonymousMethodExpression retval; + + if (async_block) + anon_block.IsAsync = true; + + current_anonymous_method.Block = anon_block; + retval = current_anonymous_method; + + async_block = (bool) oob_stack.Pop (); + current_variable = (BlockVariable) oob_stack.Pop (); + current_local_parameters = (ParametersCompiled) oob_stack.Pop (); + current_anonymous_method = (AnonymousMethodExpression) oob_stack.Pop (); + + return retval; +} + +void Error_SyntaxError (int token) +{ + Error_SyntaxError (0, token); +} + +void Error_SyntaxError (int error_code, int token) +{ + Error_SyntaxError (error_code, token, "Unexpected symbol"); +} + +void Error_SyntaxError (int error_code, int token, string msg) +{ + Lexer.CompleteOnEOF = false; + + // An error message has been reported by tokenizer + if (token == Token.ERROR) + return; + + // Avoid duplicit error message after unterminated string literals + if (token == Token.LITERAL && lexer.Location.Column == 0) + return; + + string symbol = GetSymbolName (token); + string expecting = GetExpecting (); + var loc = lexer.Location - symbol.Length; + + if (error_code == 0) { + if (expecting == "`identifier'") { + if (token > Token.FIRST_KEYWORD && token < Token.LAST_KEYWORD) { + report.Error (1041, loc, "Identifier expected, `{0}' is a keyword", symbol); + return; + } + + error_code = 1001; + expecting = "identifier"; + } else if (expecting == "`)'") { + error_code = 1026; + } else { + error_code = 1525; + } + } + + if (string.IsNullOrEmpty (expecting)) + report.Error (error_code, loc, "{1} `{0}'", symbol, msg); + else + report.Error (error_code, loc, "{2} `{0}', expecting {1}", symbol, expecting, msg); +} + +string GetExpecting () +{ + int [] tokens = yyExpectingTokens (yyExpectingState); + var names = new List (tokens.Length); + bool has_type = false; + bool has_identifier = false; + for (int i = 0; i < tokens.Length; i++){ + int token = tokens [i]; + has_identifier |= token == Token.IDENTIFIER; + + string name = GetTokenName (token); + if (name == "") + continue; + + has_type |= name == "type"; + if (names.Contains (name)) + continue; + + names.Add (name); + } + + // + // Too many tokens to enumerate + // + if (names.Count > 8) + return null; + + if (has_type && has_identifier) + names.Remove ("identifier"); + + if (names.Count == 1) + return "`" + GetTokenName (tokens [0]) + "'"; + + StringBuilder sb = new StringBuilder (); + names.Sort (); + int count = names.Count; + for (int i = 0; i < count; i++){ + bool last = i + 1 == count; + if (last) + sb.Append ("or "); + sb.Append ('`'); + sb.Append (names [i]); + sb.Append (last ? "'" : count < 3 ? "' " : "', "); + } + return sb.ToString (); +} + + +string GetSymbolName (int token) +{ + switch (token){ + case Token.LITERAL: + return ((Constant)lexer.Value).GetValue ().ToString (); + case Token.IDENTIFIER: + return ((LocatedToken)lexer.Value).Value; + + case Token.BOOL: + return "bool"; + case Token.BYTE: + return "byte"; + case Token.CHAR: + return "char"; + case Token.VOID: + return "void"; + case Token.DECIMAL: + return "decimal"; + case Token.DOUBLE: + return "double"; + case Token.FLOAT: + return "float"; + case Token.INT: + return "int"; + case Token.LONG: + return "long"; + case Token.SBYTE: + return "sbyte"; + case Token.SHORT: + return "short"; + case Token.STRING: + return "string"; + case Token.UINT: + return "uint"; + case Token.ULONG: + return "ulong"; + case Token.USHORT: + return "ushort"; + case Token.OBJECT: + return "object"; + + case Token.PLUS: + return "+"; + case Token.UMINUS: + case Token.MINUS: + return "-"; + case Token.BANG: + return "!"; + case Token.BITWISE_AND: + return "&"; + case Token.BITWISE_OR: + return "|"; + case Token.STAR: + return "*"; + case Token.PERCENT: + return "%"; + case Token.DIV: + return "/"; + case Token.CARRET: + return "^"; + case Token.OP_INC: + return "++"; + case Token.OP_DEC: + return "--"; + case Token.OP_SHIFT_LEFT: + return "<<"; + case Token.OP_SHIFT_RIGHT: + return ">>"; + case Token.OP_LT: + return "<"; + case Token.OP_GT: + return ">"; + case Token.OP_LE: + return "<="; + case Token.OP_GE: + return ">="; + case Token.OP_EQ: + return "=="; + case Token.OP_NE: + return "!="; + case Token.OP_AND: + return "&&"; + case Token.OP_OR: + return "||"; + case Token.OP_PTR: + return "->"; + case Token.OP_COALESCING: + return "??"; + case Token.OP_MULT_ASSIGN: + return "*="; + case Token.OP_DIV_ASSIGN: + return "/="; + case Token.OP_MOD_ASSIGN: + return "%="; + case Token.OP_ADD_ASSIGN: + return "+="; + case Token.OP_SUB_ASSIGN: + return "-="; + case Token.OP_SHIFT_LEFT_ASSIGN: + return "<<="; + case Token.OP_SHIFT_RIGHT_ASSIGN: + return ">>="; + case Token.OP_AND_ASSIGN: + return "&="; + case Token.OP_XOR_ASSIGN: + return "^="; + case Token.OP_OR_ASSIGN: + return "|="; + } + + return GetTokenName (token); +} + +static string GetTokenName (int token) +{ + switch (token){ + case Token.ABSTRACT: + return "abstract"; + case Token.AS: + return "as"; + case Token.ADD: + return "add"; + case Token.ASYNC: + return "async"; + case Token.BASE: + return "base"; + case Token.BREAK: + return "break"; + case Token.CASE: + return "case"; + case Token.CATCH: + return "catch"; + case Token.CHECKED: + return "checked"; + case Token.CLASS: + return "class"; + case Token.CONST: + return "const"; + case Token.CONTINUE: + return "continue"; + case Token.DEFAULT: + return "default"; + case Token.DELEGATE: + return "delegate"; + case Token.DO: + return "do"; + case Token.ELSE: + return "else"; + case Token.ENUM: + return "enum"; + case Token.EVENT: + return "event"; + case Token.EXPLICIT: + return "explicit"; + case Token.EXTERN: + case Token.EXTERN_ALIAS: + return "extern"; + case Token.FALSE: + return "false"; + case Token.FINALLY: + return "finally"; + case Token.FIXED: + return "fixed"; + case Token.FOR: + return "for"; + case Token.FOREACH: + return "foreach"; + case Token.GOTO: + return "goto"; + case Token.IF: + return "if"; + case Token.IMPLICIT: + return "implicit"; + case Token.IN: + return "in"; + case Token.INTERFACE: + return "interface"; + case Token.INTERNAL: + return "internal"; + case Token.IS: + return "is"; + case Token.LOCK: + return "lock"; + case Token.NAMESPACE: + return "namespace"; + case Token.NEW: + return "new"; + case Token.NULL: + return "null"; + case Token.OPERATOR: + return "operator"; + case Token.OUT: + return "out"; + case Token.OVERRIDE: + return "override"; + case Token.PARAMS: + return "params"; + case Token.PRIVATE: + return "private"; + case Token.PROTECTED: + return "protected"; + case Token.PUBLIC: + return "public"; + case Token.READONLY: + return "readonly"; + case Token.REF: + return "ref"; + case Token.RETURN: + return "return"; + case Token.REMOVE: + return "remove"; + case Token.SEALED: + return "sealed"; + case Token.SIZEOF: + return "sizeof"; + case Token.STACKALLOC: + return "stackalloc"; + case Token.STATIC: + return "static"; + case Token.STRUCT: + return "struct"; + case Token.SWITCH: + return "switch"; + case Token.THIS: + return "this"; + case Token.THROW: + return "throw"; + case Token.TRUE: + return "true"; + case Token.TRY: + return "try"; + case Token.TYPEOF: + return "typeof"; + case Token.UNCHECKED: + return "unchecked"; + case Token.UNSAFE: + return "unsafe"; + case Token.USING: + return "using"; + case Token.VIRTUAL: + return "virtual"; + case Token.VOLATILE: + return "volatile"; + case Token.WHERE: + return "where"; + case Token.WHILE: + return "while"; + case Token.ARGLIST: + return "__arglist"; + case Token.REFVALUE: + return "__refvalue"; + case Token.REFTYPE: + return "__reftype"; + case Token.MAKEREF: + return "__makeref"; + case Token.PARTIAL: + return "partial"; + case Token.ARROW: + return "=>"; + case Token.FROM: + case Token.FROM_FIRST: + return "from"; + case Token.JOIN: + return "join"; + case Token.ON: + return "on"; + case Token.EQUALS: + return "equals"; + case Token.SELECT: + return "select"; + case Token.GROUP: + return "group"; + case Token.BY: + return "by"; + case Token.LET: + return "let"; + case Token.ORDERBY: + return "orderby"; + case Token.ASCENDING: + return "ascending"; + case Token.DESCENDING: + return "descending"; + case Token.INTO: + return "into"; + case Token.GET: + return "get"; + case Token.SET: + return "set"; + case Token.OPEN_BRACE: + return "{"; + case Token.CLOSE_BRACE: + return "}"; + case Token.OPEN_BRACKET: + case Token.OPEN_BRACKET_EXPR: + return "["; + case Token.CLOSE_BRACKET: + return "]"; + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + case Token.OPEN_PARENS: + return "("; + case Token.CLOSE_PARENS: + return ")"; + case Token.DOT: + return "."; + case Token.COMMA: + return ","; + case Token.DEFAULT_COLON: + return "default:"; + case Token.COLON: + return ":"; + case Token.SEMICOLON: + return ";"; + case Token.TILDE: + return "~"; + + case Token.PLUS: + case Token.UMINUS: + case Token.MINUS: + case Token.BANG: + case Token.OP_LT: + case Token.OP_GT: + case Token.BITWISE_AND: + case Token.BITWISE_OR: + case Token.STAR: + case Token.PERCENT: + case Token.DIV: + case Token.CARRET: + case Token.OP_INC: + case Token.OP_DEC: + case Token.OP_SHIFT_LEFT: + case Token.OP_SHIFT_RIGHT: + case Token.OP_LE: + case Token.OP_GE: + case Token.OP_EQ: + case Token.OP_NE: + case Token.OP_AND: + case Token.OP_OR: + case Token.OP_PTR: + case Token.OP_COALESCING: + case Token.OP_MULT_ASSIGN: + case Token.OP_DIV_ASSIGN: + case Token.OP_MOD_ASSIGN: + case Token.OP_ADD_ASSIGN: + case Token.OP_SUB_ASSIGN: + case Token.OP_SHIFT_LEFT_ASSIGN: + case Token.OP_SHIFT_RIGHT_ASSIGN: + case Token.OP_AND_ASSIGN: + case Token.OP_XOR_ASSIGN: + case Token.OP_OR_ASSIGN: + return ""; + + case Token.BOOL: + case Token.BYTE: + case Token.CHAR: + case Token.VOID: + case Token.DECIMAL: + case Token.DOUBLE: + case Token.FLOAT: + case Token.INT: + case Token.LONG: + case Token.SBYTE: + case Token.SHORT: + case Token.STRING: + case Token.UINT: + case Token.ULONG: + case Token.USHORT: + case Token.OBJECT: + return "type"; + + case Token.ASSIGN: + return "="; + case Token.OP_GENERICS_LT: + case Token.GENERIC_DIMENSION: + return "<"; + case Token.OP_GENERICS_GT: + return ">"; + case Token.INTERR: + case Token.INTERR_NULLABLE: + return "?"; + case Token.DOUBLE_COLON: + return "::"; + case Token.LITERAL: + return "value"; + case Token.IDENTIFIER: + case Token.AWAIT: + return "identifier"; + + case Token.EOF: + return "end-of-file"; + + // All of these are internal. + case Token.NONE: + case Token.ERROR: + case Token.FIRST_KEYWORD: + case Token.EVAL_COMPILATION_UNIT_PARSER: + case Token.EVAL_USING_DECLARATIONS_UNIT_PARSER: + case Token.EVAL_STATEMENT_PARSER: + case Token.LAST_KEYWORD: + case Token.GENERATE_COMPLETION: + case Token.COMPLETE_COMPLETION: + return ""; + + // A bit more robust. + default: + return yyNames [token]; + } +} + +/* end end end */ +} +#line default +namespace yydebug { + using System; + internal interface yyDebug { + void push (int state, Object value); + void lex (int state, int token, string name, Object value); + void shift (int from, int to, int errorFlag); + void pop (int state); + void discard (int state, int token, string name, Object value); + void reduce (int from, int to, int rule, string text, int len); + void shift (int from, int to); + void accept (Object value); + void error (string message); + void reject (); + } + + class yyDebugSimple : yyDebug { + void println (string s){ + Console.Error.WriteLine (s); + } + + public void push (int state, Object value) { + println ("push\tstate "+state+"\tvalue "+value); + } + + public void lex (int state, int token, string name, Object value) { + println("lex\tstate "+state+"\treading "+name+"\tvalue "+value); + } + + public void shift (int from, int to, int errorFlag) { + switch (errorFlag) { + default: // normally + println("shift\tfrom state "+from+" to "+to); + break; + case 0: case 1: case 2: // in error recovery + println("shift\tfrom state "+from+" to "+to + +"\t"+errorFlag+" left to recover"); + break; + case 3: // normally + println("shift\tfrom state "+from+" to "+to+"\ton error"); + break; + } + } + + public void pop (int state) { + println("pop\tstate "+state+"\ton error"); + } + + public void discard (int state, int token, string name, Object value) { + println("discard\tstate "+state+"\ttoken "+name+"\tvalue "+value); + } + + public void reduce (int from, int to, int rule, string text, int len) { + println("reduce\tstate "+from+"\tuncover "+to + +"\trule ("+rule+") "+text); + } + + public void shift (int from, int to) { + println("goto\tfrom state "+from+" to "+to); + } + + public void accept (Object value) { + println("accept\tvalue "+value); + } + + public void error (string message) { + println("error\t"+message); + } + + public void reject () { + println("reject"); + } + + } +} +// %token constants + class Token { + public const int EOF = 257; + public const int NONE = 258; + public const int ERROR = 259; + public const int FIRST_KEYWORD = 260; + public const int ABSTRACT = 261; + public const int AS = 262; + public const int ADD = 263; + public const int BASE = 264; + public const int BOOL = 265; + public const int BREAK = 266; + public const int BYTE = 267; + public const int CASE = 268; + public const int CATCH = 269; + public const int CHAR = 270; + public const int CHECKED = 271; + public const int CLASS = 272; + public const int CONST = 273; + public const int CONTINUE = 274; + public const int DECIMAL = 275; + public const int DEFAULT = 276; + public const int DELEGATE = 277; + public const int DO = 278; + public const int DOUBLE = 279; + public const int ELSE = 280; + public const int ENUM = 281; + public const int EVENT = 282; + public const int EXPLICIT = 283; + public const int EXTERN = 284; + public const int FALSE = 285; + public const int FINALLY = 286; + public const int FIXED = 287; + public const int FLOAT = 288; + public const int FOR = 289; + public const int FOREACH = 290; + public const int GOTO = 291; + public const int IF = 292; + public const int IMPLICIT = 293; + public const int IN = 294; + public const int INT = 295; + public const int INTERFACE = 296; + public const int INTERNAL = 297; + public const int IS = 298; + public const int LOCK = 299; + public const int LONG = 300; + public const int NAMESPACE = 301; + public const int NEW = 302; + public const int NULL = 303; + public const int OBJECT = 304; + public const int OPERATOR = 305; + public const int OUT = 306; + public const int OVERRIDE = 307; + public const int PARAMS = 308; + public const int PRIVATE = 309; + public const int PROTECTED = 310; + public const int PUBLIC = 311; + public const int READONLY = 312; + public const int REF = 313; + public const int RETURN = 314; + public const int REMOVE = 315; + public const int SBYTE = 316; + public const int SEALED = 317; + public const int SHORT = 318; + public const int SIZEOF = 319; + public const int STACKALLOC = 320; + public const int STATIC = 321; + public const int STRING = 322; + public const int STRUCT = 323; + public const int SWITCH = 324; + public const int THIS = 325; + public const int THROW = 326; + public const int TRUE = 327; + public const int TRY = 328; + public const int TYPEOF = 329; + public const int UINT = 330; + public const int ULONG = 331; + public const int UNCHECKED = 332; + public const int UNSAFE = 333; + public const int USHORT = 334; + public const int USING = 335; + public const int VIRTUAL = 336; + public const int VOID = 337; + public const int VOLATILE = 338; + public const int WHERE = 339; + public const int WHILE = 340; + public const int ARGLIST = 341; + public const int PARTIAL = 342; + public const int ARROW = 343; + public const int FROM = 344; + public const int FROM_FIRST = 345; + public const int JOIN = 346; + public const int ON = 347; + public const int EQUALS = 348; + public const int SELECT = 349; + public const int GROUP = 350; + public const int BY = 351; + public const int LET = 352; + public const int ORDERBY = 353; + public const int ASCENDING = 354; + public const int DESCENDING = 355; + public const int INTO = 356; + public const int INTERR_NULLABLE = 357; + public const int EXTERN_ALIAS = 358; + public const int REFVALUE = 359; + public const int REFTYPE = 360; + public const int MAKEREF = 361; + public const int ASYNC = 362; + public const int AWAIT = 363; + public const int INTERR_OPERATOR = 364; + public const int GET = 365; + public const int SET = 366; + public const int LAST_KEYWORD = 367; + public const int OPEN_BRACE = 368; + public const int CLOSE_BRACE = 369; + public const int OPEN_BRACKET = 370; + public const int CLOSE_BRACKET = 371; + public const int OPEN_PARENS = 372; + public const int CLOSE_PARENS = 373; + public const int DOT = 374; + public const int COMMA = 375; + public const int COLON = 376; + public const int SEMICOLON = 377; + public const int TILDE = 378; + public const int PLUS = 379; + public const int MINUS = 380; + public const int BANG = 381; + public const int ASSIGN = 382; + public const int OP_LT = 383; + public const int OP_GT = 384; + public const int BITWISE_AND = 385; + public const int BITWISE_OR = 386; + public const int STAR = 387; + public const int PERCENT = 388; + public const int DIV = 389; + public const int CARRET = 390; + public const int INTERR = 391; + public const int DOUBLE_COLON = 392; + public const int OP_INC = 393; + public const int OP_DEC = 394; + public const int OP_SHIFT_LEFT = 395; + public const int OP_SHIFT_RIGHT = 396; + public const int OP_LE = 397; + public const int OP_GE = 398; + public const int OP_EQ = 399; + public const int OP_NE = 400; + public const int OP_AND = 401; + public const int OP_OR = 402; + public const int OP_MULT_ASSIGN = 403; + public const int OP_DIV_ASSIGN = 404; + public const int OP_MOD_ASSIGN = 405; + public const int OP_ADD_ASSIGN = 406; + public const int OP_SUB_ASSIGN = 407; + public const int OP_SHIFT_LEFT_ASSIGN = 408; + public const int OP_SHIFT_RIGHT_ASSIGN = 409; + public const int OP_AND_ASSIGN = 410; + public const int OP_XOR_ASSIGN = 411; + public const int OP_OR_ASSIGN = 412; + public const int OP_PTR = 413; + public const int OP_COALESCING = 414; + public const int OP_GENERICS_LT = 415; + public const int OP_GENERICS_LT_DECL = 416; + public const int OP_GENERICS_GT = 417; + public const int LITERAL = 418; + public const int IDENTIFIER = 419; + public const int OPEN_PARENS_LAMBDA = 420; + public const int OPEN_PARENS_CAST = 421; + public const int GENERIC_DIMENSION = 422; + public const int DEFAULT_COLON = 423; + public const int OPEN_BRACKET_EXPR = 424; + public const int EVAL_STATEMENT_PARSER = 425; + public const int EVAL_COMPILATION_UNIT_PARSER = 426; + public const int EVAL_USING_DECLARATIONS_UNIT_PARSER = 427; + public const int DOC_SEE = 428; + public const int GENERATE_COMPLETION = 429; + public const int COMPLETE_COMPLETION = 430; + public const int UMINUS = 431; + public const int yyErrorCode = 256; + } + namespace yyParser { + using System; + /** thrown for irrecoverable syntax errors and stack overflow. + */ + internal class yyException : System.Exception { + public yyException (string message) : base (message) { + } + } + internal class yyUnexpectedEof : yyException { + public yyUnexpectedEof (string message) : base (message) { + } + public yyUnexpectedEof () : base ("") { + } + } + + /** must be implemented by a scanner object to supply input to the parser. + */ + internal interface yyInput { + /** move on to next token. + @return false if positioned beyond tokens. + @throws IOException on input error. + */ + bool advance (); // throws java.io.IOException; + /** classifies current token. + Should not be called if advance() returned false. + @return current %token or single character. + */ + int token (); + /** associated with current token. + Should not be called if advance() returned false. + @return value for token(). + */ + Object value (); + } + } +} // close outermost namespace, that MUST HAVE BEEN opened in the prolog diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.jay b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.jay new file mode 100644 index 000000000..25df449fb --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.jay @@ -0,0 +1,8195 @@ +%{ +// +// cs-parser.jay: The Parser for the C# compiler +// +// Authors: Miguel de Icaza (miguel@gnome.org) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual Licensed under the terms of the GNU GPL and the MIT X11 license +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// (C) 2004-2011 Novell, Inc +// Copyright 2011-2012 Xamarin Inc. +// + +using System.Text; +using System.IO; +using System; +using System.Collections.Generic; + +namespace Mono.CSharp +{ + /// + /// The C# Parser + /// + public class CSharpParser + { + [Flags] + enum ParameterModifierType + { + Ref = 1 << 1, + Out = 1 << 2, + This = 1 << 3, + Params = 1 << 4, + Arglist = 1 << 5, + DefaultValue = 1 << 6, + + All = Ref | Out | This | Params | Arglist | DefaultValue, + PrimaryConstructor = Ref | Out | Params | DefaultValue + } + + static readonly object ModifierNone = 0; + + NamespaceContainer current_namespace; + TypeContainer current_container; + TypeDefinition current_type; + PropertyBase current_property; + EventProperty current_event; + EventField current_event_field; + FieldBase current_field; + + /// + /// Current block is used to add statements as we find + /// them. + /// + Block current_block; + + BlockVariable current_variable; + + Delegate current_delegate; + + AnonymousMethodExpression current_anonymous_method; + + /// + /// This is used by the unary_expression code to resolve + /// a name against a parameter. + /// + + // FIXME: This is very ugly and it's very hard to reset it correctly + // on all places, especially when some parameters are autogenerated. + ParametersCompiled current_local_parameters; + + bool parsing_anonymous_method; + + bool async_block; + + /// + /// An out-of-band stack. + /// + Stack oob_stack; + + /// + /// Controls the verbosity of the errors produced by the parser + /// + int yacc_verbose_flag; + + /// + /// Used by the interactive shell, flags whether EOF was reached + /// and an error was produced + /// + public bool UnexpectedEOF; + + /// + /// The current file. + /// + readonly CompilationSourceFile file; + + /// + /// Temporary Xml documentation cache. + /// For enum types, we need one more temporary store. + /// + string tmpComment; + string enumTypeComment; + + /// Current attribute target + string current_attr_target; + + ParameterModifierType valid_param_mod; + + bool default_parameter_used; + + /// When using the interactive parser, this holds the + /// resulting expression + public Class InteractiveResult; + + // + // Keeps track of global data changes to undo on parser error + // + public Undo undo; + + bool? interactive_async; + + Stack linq_clause_blocks; + + ModuleContainer module; + + readonly CompilerContext compiler; + readonly LanguageVersion lang_version; + readonly bool doc_support; + readonly CompilerSettings settings; + readonly Report report; + + // + // Instead of allocating carrier array everytime we + // share the bucket for very common constructs which can never + // be recursive + // + List parameters_bucket; + + // + // Full AST support members + // + LocationsBag lbag; + List> mod_locations; + Location parameterModifierLocation, savedLocation, savedEventAssignLocation; + Location savedAttrParenOpenLocation, savedAttrParenCloseLocation, savedOperatorLocation; + Stack> locationListStack = new Stack> (); // used for type parameters + Stack opt_intoStack = new Stack (); + + bool HadAttributeParens; + List attributeArgumentCommas = new List (); + List parameterListCommas = new List (); + Stack location_stack; +%} + +%token EOF +%token NONE /* This token is never returned by our lexer */ +%token ERROR // This is used not by the parser, but by the tokenizer. + // do not remove. + +/* + *These are the C# keywords + */ +%token FIRST_KEYWORD +%token ABSTRACT +%token AS +%token ADD +%token BASE +%token BOOL +%token BREAK +%token BYTE +%token CASE +%token CATCH +%token CHAR +%token CHECKED +%token CLASS +%token CONST +%token CONTINUE +%token DECIMAL +%token DEFAULT +%token DELEGATE +%token DO +%token DOUBLE +%token ELSE +%token ENUM +%token EVENT +%token EXPLICIT +%token EXTERN +%token FALSE +%token FINALLY +%token FIXED +%token FLOAT +%token FOR +%token FOREACH +%token GOTO +%token IF +%token IMPLICIT +%token IN +%token INT +%token INTERFACE +%token INTERNAL +%token IS +%token LOCK +%token LONG +%token NAMESPACE +%token NEW +%token NULL +%token OBJECT +%token OPERATOR +%token OUT +%token OVERRIDE +%token PARAMS +%token PRIVATE +%token PROTECTED +%token PUBLIC +%token READONLY +%token REF +%token RETURN +%token REMOVE +%token SBYTE +%token SEALED +%token SHORT +%token SIZEOF +%token STACKALLOC +%token STATIC +%token STRING +%token STRUCT +%token SWITCH +%token THIS +%token THROW +%token TRUE +%token TRY +%token TYPEOF +%token UINT +%token ULONG +%token UNCHECKED +%token UNSAFE +%token USHORT +%token USING +%token VIRTUAL +%token VOID +%token VOLATILE +%token WHERE +%token WHILE +%token ARGLIST +%token PARTIAL +%token ARROW +%token FROM +%token FROM_FIRST +%token JOIN +%token ON +%token EQUALS +%token SELECT +%token GROUP +%token BY +%token LET +%token ORDERBY +%token ASCENDING +%token DESCENDING +%token INTO +%token INTERR_NULLABLE +%token EXTERN_ALIAS +%token REFVALUE +%token REFTYPE +%token MAKEREF +%token ASYNC +%token AWAIT +%token INTERR_OPERATOR + +/* C# keywords which are not really keywords */ +%token GET +%token SET + +%left LAST_KEYWORD + +/* C# single character operators/punctuation. */ +%token OPEN_BRACE +%token CLOSE_BRACE +%token OPEN_BRACKET +%token CLOSE_BRACKET +%token OPEN_PARENS +%token CLOSE_PARENS + +%token DOT +%token COMMA +%token COLON +%token SEMICOLON +%token TILDE + +%token PLUS +%token MINUS +%token BANG +%token ASSIGN +%token OP_LT +%token OP_GT +%token BITWISE_AND +%token BITWISE_OR +%token STAR +%token PERCENT +%token DIV +%token CARRET +%token INTERR + +/* C# multi-character operators. */ +%token DOUBLE_COLON +%token OP_INC +%token OP_DEC +%token OP_SHIFT_LEFT +%token OP_SHIFT_RIGHT +%token OP_LE +%token OP_GE +%token OP_EQ +%token OP_NE +%token OP_AND +%token OP_OR +%token OP_MULT_ASSIGN +%token OP_DIV_ASSIGN +%token OP_MOD_ASSIGN +%token OP_ADD_ASSIGN +%token OP_SUB_ASSIGN +%token OP_SHIFT_LEFT_ASSIGN +%token OP_SHIFT_RIGHT_ASSIGN +%token OP_AND_ASSIGN +%token OP_XOR_ASSIGN +%token OP_OR_ASSIGN +%token OP_PTR +%token OP_COALESCING + +/* Generics <,> tokens */ +%token OP_GENERICS_LT +%token OP_GENERICS_LT_DECL +%token OP_GENERICS_GT + +%token LITERAL + +%token IDENTIFIER +%token OPEN_PARENS_LAMBDA +%token OPEN_PARENS_CAST +%token GENERIC_DIMENSION +%token DEFAULT_COLON +%token OPEN_BRACKET_EXPR + +// Make the parser go into eval mode parsing (statements and compilation units). +%token EVAL_STATEMENT_PARSER +%token EVAL_COMPILATION_UNIT_PARSER +%token EVAL_USING_DECLARATIONS_UNIT_PARSER + +%token DOC_SEE + +// +// This token is generated to trigger the completion engine at this point +// +%token GENERATE_COMPLETION + +// +// This token is return repeatedly after the first GENERATE_COMPLETION +// token is produced and before the final EOF +// +%token COMPLETE_COMPLETION + +/* Add precedence rules to solve dangling else s/r conflict */ +%nonassoc IF +%nonassoc ELSE + +/* Define the operator tokens and their precedences */ +%right ASSIGN +%right OP_COALESCING +%right INTERR +%left OP_OR +%left OP_AND +%left BITWISE_OR +%left BITWISE_AND +%left OP_SHIFT_LEFT OP_SHIFT_RIGHT +%left PLUS MINUS +%left STAR DIV PERCENT +%right BANG CARRET UMINUS +%nonassoc OP_INC OP_DEC +%left OPEN_PARENS +%left OPEN_BRACKET OPEN_BRACE +%left DOT + +%start compilation_unit +%% + +compilation_unit + : outer_declaration opt_EOF + { + Lexer.check_incorrect_doc_comment (); + } + | interactive_parsing { Lexer.CompleteOnEOF = false; } opt_EOF + | documentation_parsing + ; + +outer_declaration + : opt_extern_alias_directives opt_using_directives + | opt_extern_alias_directives opt_using_directives namespace_or_type_declarations opt_attributes + { + if ($4 != null) { + Attributes attrs = (Attributes) $4; + report.Error (1730, attrs.Attrs [0].Location, + "Assembly and module attributes must precede all other elements except using clauses and extern alias declarations"); + + current_namespace.UnattachedAttributes = attrs; + } + } + | opt_extern_alias_directives opt_using_directives attribute_sections + { + module.AddAttributes ((Attributes) $3, current_namespace); + } + | error + { + if (yyToken == Token.EXTERN_ALIAS) + report.Error (439, lexer.Location, "An extern alias declaration must precede all other elements"); + else + Error_SyntaxError (yyToken); + } + ; + +opt_EOF + : /* empty */ + | EOF + ; + +extern_alias_directives + : extern_alias_directive + | extern_alias_directives extern_alias_directive + ; + +extern_alias_directive + : EXTERN_ALIAS IDENTIFIER IDENTIFIER SEMICOLON + { + var lt = (LocatedToken) $2; + string s = lt.Value; + if (s != "alias") { + syntax_error (lt.Location, "`alias' expected"); + } else { + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (lt.Location, "external alias"); + + lt = (LocatedToken) $3; + if (lt.Value == QualifiedAliasMember.GlobalAlias) { + RootNamespace.Error_GlobalNamespaceRedefined (report, lt.Location); + } + + var na = new UsingExternAlias (new SimpleMemberName (lt.Value, lt.Location), GetLocation ($1)); + current_namespace.AddUsing (na); + + lbag.AddLocation (na, GetLocation ($2), GetLocation ($4)); + } + } + | EXTERN_ALIAS error + { + Error_SyntaxError (yyToken); + } + ; + +using_directives + : using_directive + | using_directives using_directive + ; + +using_directive + : using_namespace + { + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + ; + +using_namespace + : USING namespace_or_type_expr SEMICOLON + { + var un = new UsingNamespace ((ATypeNameExpression) $2, GetLocation ($1)); + current_namespace.AddUsing (un); + + lbag.AddLocation (un, GetLocation ($3)); + } + | USING IDENTIFIER ASSIGN namespace_or_type_expr SEMICOLON + { + var lt = (LocatedToken) $2; + if (lang_version != LanguageVersion.ISO_1 && lt.Value == "global") { + report.Warning (440, 2, lt.Location, + "An alias named `global' will not be used when resolving `global::'. The global namespace will be used instead"); + } + + var un = new UsingAliasNamespace (new SimpleMemberName (lt.Value, lt.Location), (ATypeNameExpression) $4, GetLocation ($1)); + current_namespace.AddUsing (un); + lbag.AddLocation (un, GetLocation ($3), GetLocation ($5)); + } + | USING error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +// +// Strictly speaking, namespaces don't have attributes but +// we parse global attributes along with namespace declarations and then +// detach them +// +namespace_declaration + : opt_attributes NAMESPACE namespace_name + { + Attributes attrs = (Attributes) $1; + var name = (MemberName) $3; + if (attrs != null) { + bool valid_global_attrs = true; + if ((current_namespace.DeclarationFound || current_namespace != file)) { + valid_global_attrs = false; + } else { + foreach (var a in attrs.Attrs) { + if (a.ExplicitTarget == "assembly" || a.ExplicitTarget == "module") + continue; + + valid_global_attrs = false; + break; + } + } + + if (!valid_global_attrs) + report.Error (1671, name.Location, "A namespace declaration cannot have modifiers or attributes"); + } + + module.AddAttributes (attrs, current_namespace); + + var ns = new NamespaceContainer (name, current_namespace); + current_namespace.AddTypeContainer (ns); + current_container = current_namespace = ns; + } + OPEN_BRACE + { + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_extern_alias_directives opt_using_directives opt_namespace_or_type_declarations CLOSE_BRACE opt_semicolon_error + { + if ($11 != null) + lbag.AddLocation (current_container, GetLocation ($2), GetLocation ($5), GetLocation ($10), GetLocation ($11)); + else + lbag.AddLocation (current_container, GetLocation ($2), GetLocation ($5), GetLocation ($10)); + + current_container = current_namespace = current_namespace.Parent; + } + | opt_attributes NAMESPACE namespace_name + { + report.Error (1514, lexer.Location, "Unexpected symbol `{0}', expecting `.' or `{{'", GetSymbolName (yyToken)); + + var name = (MemberName) $3; + var ns = new NamespaceContainer (name, current_namespace); + lbag.AddLocation (ns, GetLocation ($2)); + current_namespace.AddTypeContainer (ns); + } + ; + +opt_semicolon_error + : /* empty */ + | SEMICOLON + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +namespace_name + : IDENTIFIER + { + var lt = (LocatedToken) $1; + $$ = new MemberName (lt.Value, lt.Location); + } + | namespace_name DOT IDENTIFIER + { + var lt = (LocatedToken) $3; + $$ = new MemberName ((MemberName) $1, lt.Value, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | error + { + Error_SyntaxError (yyToken); + $$ = new MemberName ("", lexer.Location); + } + ; + +opt_semicolon + : /* empty */ + | SEMICOLON + ; + +opt_comma + : /* empty */ + | COMMA + ; + +opt_using_directives + : /* empty */ + | using_directives + ; + +opt_extern_alias_directives + : /* empty */ + | extern_alias_directives + ; + +opt_namespace_or_type_declarations + : /* empty */ + | namespace_or_type_declarations + ; + +namespace_or_type_declarations + : namespace_or_type_declaration + | namespace_or_type_declarations namespace_or_type_declaration + ; + +namespace_or_type_declaration + : type_declaration + { + if ($1 != null) { + TypeContainer ds = (TypeContainer)$1; + + if ((ds.ModFlags & (Modifiers.PRIVATE | Modifiers.PROTECTED)) != 0){ + report.Error (1527, ds.Location, + "Namespace elements cannot be explicitly declared as private, protected or protected internal"); + } + + // Here is a trick, for explicit attributes we don't know where they belong to until + // we parse succeeding declaration hence we parse them as normal and re-attach them + // when we know whether they are global (assembly:, module:) or local (type:). + if (ds.OptAttributes != null) { + ds.OptAttributes.ConvertGlobalAttributes (ds, current_namespace, !current_namespace.DeclarationFound && current_namespace == file); + } + } + current_namespace.DeclarationFound = true; + } + | namespace_declaration + { + current_namespace.DeclarationFound = true; + } + | attribute_sections CLOSE_BRACE { + current_namespace.UnattachedAttributes = (Attributes) $1; + report.Error (1518, lexer.Location, "Attributes must be attached to class, delegate, enum, interface or struct"); + lexer.putback ('}'); + } + ; + +type_declaration + : class_declaration + | struct_declaration + | interface_declaration + | enum_declaration + | delegate_declaration +// +// Enable this when we have handled all errors, because this acts as a generic fallback +// +// | error { +// Console.WriteLine ("Token=" + yyToken); +// report.Error (1518, GetLocation ($1), "Expected class, struct, interface, enum or delegate"); +// } + ; + +// +// Attributes +// + +opt_attributes + : /* empty */ + | attribute_sections + ; + +attribute_sections + : attribute_section + { + var sect = (List) $1; + $$ = new Attributes (sect); + } + | attribute_sections attribute_section + { + Attributes attrs = $1 as Attributes; + var sect = (List) $2; + if (attrs == null) + attrs = new Attributes (sect); + else if (sect != null) + attrs.AddAttributes (sect); + $$ = attrs; + } + ; + +attribute_section + : OPEN_BRACKET + { + PushLocation (GetLocation ($1)); + lexer.parsing_attribute_section = true; + } + attribute_section_cont + { + lexer.parsing_attribute_section = false; + $$ = $3; + } + ; + +attribute_section_cont + : attribute_target COLON + { + current_attr_target = (string) $1; + if (current_attr_target == "assembly" || current_attr_target == "module") { + Lexer.check_incorrect_doc_comment (); + } + } + attribute_list opt_comma CLOSE_BRACKET + { + // when attribute target is invalid + if (current_attr_target == string.Empty) + $$ = new List (0); + else + $$ = $4; + lbag.InsertLocation ($$, 0, GetLocation ($2)); + lbag.InsertLocation ($$, 0, PopLocation ()); + lbag.InsertLocation ($$, 0, PopLocation ()); + if ($5 != null) { + lbag.AddLocation ($$, GetLocation ($5), GetLocation ($6)); + } else { + lbag.AddLocation ($$, GetLocation ($6)); + } + + current_attr_target = null; + lexer.parsing_attribute_section = false; + } + | attribute_list opt_comma CLOSE_BRACKET + { + $$ = $1; + lbag.InsertLocation ($$, 0, PopLocation ()); + if ($2 != null) { + lbag.AddLocation ($$, GetLocation($2), GetLocation ($3)); + } else { + lbag.AddLocation ($$, GetLocation($3)); + } + } + | IDENTIFIER error + { + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) $1; + var tne = new SimpleName (lt.Value, null, lt.Location); + + $$ = new List () { + new Attribute (null, tne, null, GetLocation ($1), false) + }; + } + | error + { + CheckAttributeTarget (yyToken, GetTokenName (yyToken), GetLocation ($1)); + $$ = null; + } + ; + +attribute_target + : IDENTIFIER + { + var lt = (LocatedToken) $1; + $$ = CheckAttributeTarget (yyToken, lt.Value, lt.Location); + PushLocation (GetLocation ($1)); + } + | EVENT { $$ = "event"; PushLocation (GetLocation ($1)); } + | RETURN { $$ = "return"; PushLocation (GetLocation ($1)); } + ; + +attribute_list + : attribute + { + $$ = new List (4) { (Attribute) $1 }; + } + | attribute_list COMMA attribute + { + var attrs = (List) $1; + if (attrs != null) { + attrs.Add ((Attribute) $3); + lbag.AddLocation (attrs, GetLocation ($2)); + } + + $$ = attrs; + } + ; + +attribute + : attribute_name + { + ++lexer.parsing_block; + } + opt_attribute_arguments + { + --lexer.parsing_block; + + var tne = (ATypeNameExpression) $1; + if (tne.HasTypeArguments) { + report.Error (404, tne.Location, "Attributes cannot be generic"); + } + Arguments [] arguments = (Arguments []) $3; + + $$ = new Attribute (current_attr_target, tne, (Arguments[]) $3, GetLocation ($1), lexer.IsEscapedIdentifier (tne)); + if (arguments != null) { + attributeArgumentCommas.Insert (0, savedAttrParenOpenLocation); + attributeArgumentCommas.Add (savedAttrParenCloseLocation); + lbag.AddLocation ($$, attributeArgumentCommas); + attributeArgumentCommas.Clear (); + } else if (HadAttributeParens) { + lbag.AddLocation ($$, savedAttrParenOpenLocation, savedAttrParenCloseLocation); + } + } + ; + +attribute_name + : namespace_or_type_expr + ; + +opt_attribute_arguments + : /* empty */ { $$ = null; HadAttributeParens = false; } + | OPEN_PARENS attribute_arguments CLOSE_PARENS + { + savedAttrParenOpenLocation = GetLocation ($1); + savedAttrParenCloseLocation = GetLocation ($3); + $$ = $2; + HadAttributeParens = true; + } + ; + + +attribute_arguments + : /* empty */ { $$ = null; } + | positional_or_named_argument + { + Arguments a = new Arguments (4); + a.Add ((Argument) $1); + $$ = new Arguments [] { a, null }; + } + | named_attribute_argument + { + Arguments a = new Arguments (4); + a.Add ((Argument) $1); + $$ = new Arguments [] { null, a }; + } + | attribute_arguments COMMA positional_or_named_argument + { + Arguments[] o = (Arguments[]) $1; + if (o [1] != null) { + report.Error (1016, ((Argument) $3).Expr.Location, "Named attribute arguments must appear after the positional arguments"); + o [0] = new Arguments (4); + } + + Arguments args = ((Arguments) o [0]); + if (args.Count > 0 && !($3 is NamedArgument) && args [args.Count - 1] is NamedArgument) + Error_NamedArgumentExpected ((NamedArgument) args [args.Count - 1]); + + args.Add ((Argument) $3); + attributeArgumentCommas.Add (GetLocation ($2)); + } + | attribute_arguments COMMA named_attribute_argument + { + Arguments[] o = (Arguments[]) $1; + if (o [1] == null) { + o [1] = new Arguments (4); + } + + ((Arguments) o [1]).Add ((Argument) $3); + attributeArgumentCommas.Add (GetLocation ($2)); + } + ; + +positional_or_named_argument + : expression + { + $$ = new Argument ((Expression) $1); + } + | named_argument + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +named_attribute_argument + : IDENTIFIER ASSIGN + { + ++lexer.parsing_block; + } + expression + { + --lexer.parsing_block; + var lt = (LocatedToken) $1; + $$ = new NamedArgument (lt.Value, lt.Location, (Expression) $4); + lbag.AddLocation ($$, GetLocation($2)); + } + ; + +named_argument + : identifier_inside_body COLON opt_named_modifier expression_or_error + { + if (lang_version <= LanguageVersion.V_3) + FeatureIsNotAvailable (GetLocation ($1), "named argument"); + + // Avoid boxing in common case (no modifier) + var arg_mod = $3 == null ? Argument.AType.None : (Argument.AType) $3; + + var lt = (LocatedToken) $1; + $$ = new NamedArgument (lt.Value, lt.Location, (Expression) $4, arg_mod); + lbag.AddLocation ($$, GetLocation($2)); + } + ; + +opt_named_modifier + : /* empty */ { $$ = null; } + | REF + { + $$ = Argument.AType.Ref; + } + | OUT + { + $$ = Argument.AType.Out; + } + ; + +opt_class_member_declarations + : /* empty */ + | class_member_declarations + ; + +class_member_declarations + : class_member_declaration + { + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + | class_member_declarations class_member_declaration + { + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + ; + +class_member_declaration + : constant_declaration + | field_declaration + | method_declaration + | property_declaration + | event_declaration + | indexer_declaration + | operator_declaration + | constructor_declaration + | primary_constructor_body + | destructor_declaration + | type_declaration + | attributes_without_members + | incomplete_member + | error + { + report.Error (1519, lexer.Location, "Unexpected symbol `{0}' in class, struct, or interface member declaration", + GetSymbolName (yyToken)); + $$ = null; + lexer.parsing_generic_declaration = false; + } + ; + +primary_constructor_body + : OPEN_BRACE + { + current_local_parameters = current_type.PrimaryConstructorParameters; + if (current_local_parameters == null) { + report.Error (9010, GetLocation ($1), "Primary constructor body is not allowed"); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + + ++lexer.parsing_block; + start_block (GetLocation ($1)); + } + opt_statement_list block_end + { + current_local_parameters = null; + var t = current_type as ClassOrStruct; + if (t != null) { + var b = (ToplevelBlock) $4; + if (t.PrimaryConstructorBlock != null) { + report.Error (8041, b.StartLocation, "Primary constructor already has a body"); + } else { + t.PrimaryConstructorBlock = b; + } + } + } + ; + +struct_declaration + : opt_attributes + opt_modifiers + opt_partial + STRUCT + { + } + type_declaration_name + { + lexer.ConstraintsParsing = true; + valid_param_mod = ParameterModifierType.PrimaryConstructor; + push_current_container (new Struct (current_container, (MemberName) $6, (Modifiers) $2, (Attributes) $1), $3); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($4)); + } + opt_primary_parameters + opt_class_base + opt_type_parameter_constraints_clauses + { + valid_param_mod = 0; + lexer.ConstraintsParsing = false; + + if ($8 != null) + current_type.PrimaryConstructorParameters = (ParametersCompiled) $8; + + if ($10 != null) + current_container.SetConstraints ((List) $10); + + if (doc_support) + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + + + lexer.parsing_modifiers = true; + } + OPEN_BRACE + { + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_class_member_declarations CLOSE_BRACE + { + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_semicolon + { + if ($16 == null) { + lbag.AppendToMember (current_container, GetLocation ($12), GetLocation ($15)); + } else { + lbag.AppendToMember (current_container, GetLocation ($12), GetLocation ($15), GetLocation ($17)); + } + $$ = pop_current_class (); + } + | opt_attributes opt_modifiers opt_partial STRUCT error + { + Error_SyntaxError (yyToken); + } + ; + +constant_declaration + : opt_attributes + opt_modifiers + CONST type IDENTIFIER + { + var lt = (LocatedToken) $5; + var mod = (Modifiers) $2; + current_field = new Const (current_type, (FullNamedExpression) $4, mod, new MemberName (lt.Value, lt.Location), (Attributes) $1); + current_type.AddMember (current_field); + + if ((mod & Modifiers.STATIC) != 0) { + report.Error (504, current_field.Location, "The constant `{0}' cannot be marked static", current_field.GetSignatureForError ()); + } + + $$ = current_field; + } + constant_initializer opt_constant_declarators SEMICOLON + { + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + current_field.Initializer = (ConstInitializer) $7; + lbag.AddMember (current_field, GetModifierLocations (), GetLocation ($3), GetLocation ($9)); + current_field = null; + } + | opt_attributes + opt_modifiers + CONST type error + { + Error_SyntaxError (yyToken); + + current_type.AddMember (new Const (current_type, (FullNamedExpression) $4, (Modifiers) $2, MemberName.Null, (Attributes) $1)); + } + ; + +opt_constant_declarators + : /* empty */ + | constant_declarators + ; + +constant_declarators + : constant_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $1); + } + | constant_declarators constant_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $2); + } + ; + +constant_declarator + : COMMA IDENTIFIER constant_initializer + { + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (ConstInitializer) $3); + lbag.AddLocation ($$, GetLocation ($1)); + } + ; + +constant_initializer + : ASSIGN + { + ++lexer.parsing_block; + } + constant_initializer_expr + { + --lexer.parsing_block; + $$ = new ConstInitializer (current_field, (Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($1)); + } + | error + { + report.Error (145, lexer.Location, "A const field requires a value to be provided"); + $$ = null; + } + ; + +constant_initializer_expr + : constant_expression + | array_initializer + ; + +field_declaration + : opt_attributes + opt_modifiers + member_type IDENTIFIER + { + lexer.parsing_generic_declaration = false; + + FullNamedExpression type = (FullNamedExpression) $3; + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (670, GetLocation ($3), "Fields cannot have void type"); + + var lt = (LocatedToken) $4; + current_field = new Field (current_type, type, (Modifiers) $2, new MemberName (lt.Value, lt.Location), (Attributes) $1); + current_type.AddField (current_field); + $$ = current_field; + } + opt_field_initializer + opt_field_declarators + SEMICOLON + { + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lbag.AddMember (current_field, GetModifierLocations (), GetLocation ($8)); + $$ = current_field; + current_field = null; + } + | opt_attributes + opt_modifiers + FIXED simple_type IDENTIFIER + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($3), "fixed size buffers"); + + var lt = (LocatedToken) $5; + current_field = new FixedField (current_type, (FullNamedExpression) $4, (Modifiers) $2, + new MemberName (lt.Value, lt.Location), (Attributes) $1); + + current_type.AddField (current_field); + } + fixed_field_size opt_fixed_field_declarators SEMICOLON + { + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + current_field.Initializer = (ConstInitializer) $7; + lbag.AddMember (current_field, GetModifierLocations (), GetLocation ($3), GetLocation ($9)); + $$ = current_field; + current_field = null; + } + | opt_attributes + opt_modifiers + FIXED simple_type error + SEMICOLON + { + report.Error (1641, GetLocation ($5), "A fixed size buffer field must have the array size specifier after the field name"); + } + ; + +opt_field_initializer + : /* empty */ + | ASSIGN + { + ++lexer.parsing_block; + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + start_block (GetLocation ($1)); + } + variable_initializer + { + --lexer.parsing_block; + current_field.Initializer = (Expression) $3; + lbag.AppendToMember (current_field, GetLocation ($1)); + end_block (lexer.Location); + current_local_parameters = null; + } + ; + +opt_field_declarators + : /* empty */ + | field_declarators + ; + +field_declarators + : field_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $1); + } + | field_declarators field_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $2); + } + ; + +field_declarator + : COMMA IDENTIFIER + { + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null); + lbag.AddLocation ($$, GetLocation ($1)); + } + | COMMA IDENTIFIER ASSIGN + { + ++lexer.parsing_block; + } + variable_initializer + { + --lexer.parsing_block; + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) $5); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3)); + } + ; + +opt_fixed_field_declarators + : /* empty */ + | fixed_field_declarators + ; + +fixed_field_declarators + : fixed_field_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $1); + } + | fixed_field_declarators fixed_field_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $2); + } + ; + +fixed_field_declarator + : COMMA IDENTIFIER fixed_field_size + { + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (ConstInitializer) $3); + lbag.AddLocation ($$, GetLocation ($1)); + } + ; + +fixed_field_size + : OPEN_BRACKET + { + ++lexer.parsing_block; + } + expression CLOSE_BRACKET + { + --lexer.parsing_block; + $$ = new ConstInitializer (current_field, (Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | OPEN_BRACKET error + { + report.Error (443, lexer.Location, "Value or constant expected"); + $$ = null; + } + ; + +variable_initializer + : expression + | array_initializer + | error + { + // It has to be here for the parent to safely restore artificial block + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +method_declaration + : method_header + { + if (doc_support) + Lexer.doc_state = XmlCommentState.NotAllowed; + + // Was added earlier in the case of body being eof for full ast + } + method_body_expression_block + { + Method method = (Method) $1; + method.Block = (ToplevelBlock) $3; + async_block = false; + + if (method.Block == null) { + lbag.AppendToMember (method, savedLocation); // semicolon + method.ParameterInfo.CheckParameters (method); + + if ((method.ModFlags & Modifiers.ASYNC) != 0) { + report.Error (1994, method.Location, "`{0}': The async modifier can only be used with methods that have a body", + method.GetSignatureForError ()); + } + } else { + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, method.Location, "`{0}': interface members cannot have a definition", + method.GetSignatureForError ()); + } + } + + current_local_parameters = null; + + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + ; + +method_header + : opt_attributes + opt_modifiers + member_type + method_declaration_name OPEN_PARENS + { + valid_param_mod = ParameterModifierType.All; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + MemberName name = (MemberName) $4; + current_local_parameters = (ParametersCompiled) $7; + + var method = Method.Create (current_type, (FullNamedExpression) $3, (Modifiers) $2, + name, current_local_parameters, (Attributes) $1); + + current_type.AddMember (method); + + async_block = (method.ModFlags & Modifiers.ASYNC) != 0; + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + lbag.AddMember (method, GetModifierLocations (), GetLocation ($5), GetLocation ($8)); + $$ = method; + + lexer.ConstraintsParsing = true; + } + opt_type_parameter_constraints_clauses + { + lexer.ConstraintsParsing = false; + + if ($10 != null) { + var method = (Method) $9; + method.SetConstraints ((List) $10); + } + + $$ = $9; + } + | opt_attributes + opt_modifiers + PARTIAL + VOID + { + lexer.parsing_generic_declaration = true; + } + method_declaration_name + OPEN_PARENS + { + lexer.parsing_generic_declaration = false; + valid_param_mod = ParameterModifierType.All; + } + opt_formal_parameter_list CLOSE_PARENS + { + lexer.ConstraintsParsing = true; + } + opt_type_parameter_constraints_clauses + { + lexer.ConstraintsParsing = false; + valid_param_mod = 0; + + MemberName name = (MemberName) $6; + current_local_parameters = (ParametersCompiled) $9; + + var modifiers = (Modifiers) $2; + modifiers |= Modifiers.PARTIAL; + + var method = Method.Create (current_type, new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($4)), + modifiers, name, current_local_parameters, (Attributes) $1); + + current_type.AddMember (method); + + async_block = (method.ModFlags & Modifiers.ASYNC) != 0; + + if ($12 != null) + method.SetConstraints ((List) $12); + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + StoreModifierLocation (Modifiers.PARTIAL, GetLocation ($3)); + lbag.AddMember (method, GetModifierLocations (), GetLocation ($7), GetLocation ($10)); + $$ = method; + } + | opt_attributes + opt_modifiers + member_type + modifiers method_declaration_name OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS + { + MemberName name = (MemberName) $5; + report.Error (1585, name.Location, + "Member modifier `{0}' must precede the member type and name", ModifiersExtensions.Name ((Modifiers) $4)); + + var method = Method.Create (current_type, (FullNamedExpression) $3, + 0, name, (ParametersCompiled) $7, (Attributes) $1); + + current_type.AddMember (method); + + current_local_parameters = (ParametersCompiled) $7; + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + $$ = method; + } + | opt_attributes + opt_modifiers + member_type + method_declaration_name error + { + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.Undefined; + + MemberName name = (MemberName) $4; + var method = Method.Create (current_type, (FullNamedExpression) $3, (Modifiers) $2, + name, current_local_parameters, (Attributes) $1); + + current_type.AddMember (method); + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + $$ = method; + } + ; + +method_body_expression_block + : method_body + | expression_block + ; + +method_body + : block + | SEMICOLON { savedLocation = GetLocation ($1); $$ = null; } + ; + +expression_block + : ARROW + { + if (lang_version < LanguageVersion.V_6) { + FeatureIsNotAvailable (GetLocation ($1), "expression bodied members"); + } + + ++lexer.parsing_block; + start_block (GetLocation ($1)); + } + expression SEMICOLON + { + lexer.parsing_block = 0; + current_block.AddStatement (new ContextualReturn ((Expression) $3)); + var b = end_block (GetLocation ($4)); + b.IsCompilerGenerated = true; + $$ = b; + } + ; + +opt_formal_parameter_list + : /* empty */ { $$ = ParametersCompiled.EmptyReadOnlyParameters; } + | formal_parameter_list + ; + +formal_parameter_list + : fixed_parameters + { + var pars_list = (List) $1; + $$ = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation ($$, parameterListCommas); + } + | fixed_parameters COMMA parameter_array + { + var pars_list = (List) $1; + pars_list.Add ((Parameter) $3); + parameterListCommas.Add (GetLocation ($2)); + + $$ = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation ($$, parameterListCommas); + } + | fixed_parameters COMMA arglist_modifier + { + var pars_list = (List) $1; + pars_list.Add (new ArglistParameter (GetLocation ($3))); + parameterListCommas.Add (GetLocation ($2)); + + $$ = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation ($$, parameterListCommas); + } + | parameter_array COMMA error + { + if ($1 != null) + report.Error (231, ((Parameter) $1).Location, "A params parameter must be the last parameter in a formal parameter list"); + + $$ = new ParametersCompiled (new Parameter[] { (Parameter) $1 } ); + lbag.AddLocation ($$, parameterListCommas); + } + | fixed_parameters COMMA parameter_array COMMA error + { + if ($3 != null) + report.Error (231, ((Parameter) $3).Location, "A params parameter must be the last parameter in a formal parameter list"); + + var pars_list = (List) $1; + pars_list.Add (new ArglistParameter (GetLocation ($3))); + parameterListCommas.Add (GetLocation ($2)); + parameterListCommas.Add (GetLocation ($4)); + + $$ = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation ($$, parameterListCommas); + } + | arglist_modifier COMMA error + { + report.Error (257, GetLocation ($1), "An __arglist parameter must be the last parameter in a formal parameter list"); + + $$ = new ParametersCompiled (new Parameter [] { new ArglistParameter (GetLocation ($1)) }, true); + lbag.AddLocation ($$, parameterListCommas); + } + | fixed_parameters COMMA ARGLIST COMMA error + { + report.Error (257, GetLocation ($3), "An __arglist parameter must be the last parameter in a formal parameter list"); + + var pars_list = (List) $1; + pars_list.Add (new ArglistParameter (GetLocation ($3))); + parameterListCommas.Add (GetLocation ($2)); + parameterListCommas.Add (GetLocation ($4)); + + $$ = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation ($$, parameterListCommas); + } + | parameter_array + { + $$ = new ParametersCompiled (new Parameter[] { (Parameter) $1 } ); + } + | arglist_modifier + { + $$ = new ParametersCompiled (new Parameter [] { new ArglistParameter (GetLocation ($1)) }, true); + } + | error + { + Error_SyntaxError (yyToken); + $$ = ParametersCompiled.EmptyReadOnlyParameters; + } + ; + +fixed_parameters + : fixed_parameter + { + parameters_bucket.Clear (); + Parameter p = (Parameter) $1; + parameters_bucket.Add (p); + parameterListCommas.Clear (); + default_parameter_used = p.HasDefaultValue; + $$ = parameters_bucket; + } + | fixed_parameters COMMA fixed_parameter + { + var pars = (List) $1; + Parameter p = (Parameter) $3; + if (p != null) { + if (p.HasExtensionMethodModifier) + report.Error (1100, p.Location, "The parameter modifier `this' can only be used on the first parameter"); + else if (!p.HasDefaultValue && default_parameter_used) + report.Error (1737, p.Location, "Optional parameter cannot precede required parameters"); + + default_parameter_used |= p.HasDefaultValue; + pars.Add (p); + + parameterListCommas.Add (GetLocation ($2)); + } + + $$ = $1; + } + ; + +fixed_parameter + : opt_attributes + opt_parameter_modifier + parameter_type + identifier_inside_body + { + var lt = (LocatedToken) $4; + $$ = new Parameter ((FullNamedExpression) $3, lt.Value, (Parameter.Modifier) $2, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, parameterModifierLocation); + } + | opt_attributes + opt_parameter_modifier + parameter_type + identifier_inside_body OPEN_BRACKET CLOSE_BRACKET + { + var lt = (LocatedToken) $4; + report.Error (1552, lt.Location, "Array type specifier, [], must appear before parameter name"); + $$ = new Parameter ((FullNamedExpression) $3, lt.Value, (Parameter.Modifier) $2, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, parameterModifierLocation); + } + | attribute_sections error + { + Error_SyntaxError (yyToken); + Location l = GetLocation ($2); + $$ = new Parameter (null, null, Parameter.Modifier.NONE, (Attributes) $1, l); + } + | opt_attributes + opt_parameter_modifier + parameter_type + error + { + Error_SyntaxError (yyToken); + Location l = GetLocation ($4); + $$ = new Parameter ((FullNamedExpression) $3, null, (Parameter.Modifier) $2, (Attributes) $1, l); + lbag.AddLocation ($$, parameterModifierLocation); + } + | opt_attributes + opt_parameter_modifier + parameter_type + identifier_inside_body + ASSIGN + { + ++lexer.parsing_block; + } + constant_expression + { + --lexer.parsing_block; + if (lang_version <= LanguageVersion.V_3) { + FeatureIsNotAvailable (GetLocation ($5), "optional parameter"); + } + + Parameter.Modifier mod = (Parameter.Modifier) $2; + if (mod != Parameter.Modifier.NONE) { + switch (mod) { + case Parameter.Modifier.REF: + case Parameter.Modifier.OUT: + report.Error (1741, GetLocation ($2), "Cannot specify a default value for the `{0}' parameter", + Parameter.GetModifierSignature (mod)); + break; + + case Parameter.Modifier.This: + report.Error (1743, GetLocation ($2), "Cannot specify a default value for the `{0}' parameter", + Parameter.GetModifierSignature (mod)); + break; + default: + throw new NotImplementedException (mod.ToString ()); + } + + mod = Parameter.Modifier.NONE; + } + + if ((valid_param_mod & ParameterModifierType.DefaultValue) == 0) + report.Error (1065, GetLocation ($5), "Optional parameter is not valid in this context"); + + var lt = (LocatedToken) $4; + $$ = new Parameter ((FullNamedExpression) $3, lt.Value, mod, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, parameterModifierLocation, GetLocation ($5)); // parameterModifierLocation should be ignored when mod == NONE + + if ($7 != null) + ((Parameter) $$).DefaultValue = new DefaultParameterValueExpression ((Expression) $7); + } + ; + +opt_parameter_modifier + : /* empty */ { $$ = Parameter.Modifier.NONE; } + | parameter_modifiers + ; + +parameter_modifiers + : parameter_modifier + { + $$ = $1; + } + | parameter_modifiers parameter_modifier + { + Parameter.Modifier p2 = (Parameter.Modifier)$2; + Parameter.Modifier mod = (Parameter.Modifier)$1 | p2; + if (((Parameter.Modifier)$1 & p2) == p2) { + Error_DuplicateParameterModifier (lexer.Location, p2); + } else { + switch (mod & ~Parameter.Modifier.This) { + case Parameter.Modifier.REF: + report.Error (1101, lexer.Location, "The parameter modifiers `this' and `ref' cannot be used altogether"); + break; + case Parameter.Modifier.OUT: + report.Error (1102, lexer.Location, "The parameter modifiers `this' and `out' cannot be used altogether"); + break; + default: + report.Error (1108, lexer.Location, "A parameter cannot have specified more than one modifier"); + break; + } + } + $$ = mod; + } + ; + +parameter_modifier + : REF + { + if ((valid_param_mod & ParameterModifierType.Ref) == 0) + Error_ParameterModifierNotValid ("ref", GetLocation ($1)); + parameterModifierLocation = GetLocation ($1); + $$ = Parameter.Modifier.REF; + } + | OUT + { + if ((valid_param_mod & ParameterModifierType.Out) == 0) + Error_ParameterModifierNotValid ("out", GetLocation ($1)); + parameterModifierLocation = GetLocation ($1); + $$ = Parameter.Modifier.OUT; + } + | THIS + { + if ((valid_param_mod & ParameterModifierType.This) == 0) + Error_ParameterModifierNotValid ("this", GetLocation ($1)); + + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "extension methods"); + parameterModifierLocation = GetLocation ($1); + $$ = Parameter.Modifier.This; + } + ; + +parameter_array + : opt_attributes params_modifier type IDENTIFIER + { + var lt = (LocatedToken) $4; + $$ = new ParamsParameter ((FullNamedExpression) $3, lt.Value, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, savedLocation); + } + | opt_attributes params_modifier type IDENTIFIER ASSIGN constant_expression + { + report.Error (1751, GetLocation ($2), "Cannot specify a default value for a parameter array"); + + var lt = (LocatedToken) $4; + $$ = new ParamsParameter ((FullNamedExpression) $3, lt.Value, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, savedLocation); + } + | opt_attributes params_modifier type error + { + Error_SyntaxError (yyToken); + + $$ = new ParamsParameter ((FullNamedExpression) $3, null, (Attributes) $1, Location.Null); + } + ; + +params_modifier + : PARAMS + { + if ((valid_param_mod & ParameterModifierType.Params) == 0) + report.Error (1670, (GetLocation ($1)), "The `params' modifier is not allowed in current context"); + savedLocation = GetLocation ($1); + } + | PARAMS parameter_modifier + { + Parameter.Modifier mod = (Parameter.Modifier)$2; + if ((mod & Parameter.Modifier.This) != 0) { + report.Error (1104, GetLocation ($1), "The parameter modifiers `this' and `params' cannot be used altogether"); + } else { + report.Error (1611, GetLocation ($1), "The params parameter cannot be declared as ref or out"); + } + savedLocation = GetLocation ($1); + } + | PARAMS params_modifier + { + Error_DuplicateParameterModifier (GetLocation ($1), Parameter.Modifier.PARAMS); + } + ; + +arglist_modifier + : ARGLIST + { + if ((valid_param_mod & ParameterModifierType.Arglist) == 0) + report.Error (1669, GetLocation ($1), "__arglist is not valid in this context"); + } + ; + +property_declaration + : opt_attributes + opt_modifiers + member_type + member_declaration_name + { + lexer.parsing_generic_declaration = false; + if (doc_support) + tmpComment = Lexer.consume_doc_comment (); + } + OPEN_BRACE + { + var type = (FullNamedExpression) $3; + current_property = new Property (current_type, type, (Modifiers) $2, + (MemberName) $4, (Attributes) $1); + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (547, GetLocation ($3), "`{0}': property or indexer cannot have void type", current_property.GetSignatureForError ()); + + current_type.AddMember (current_property); + lbag.AddMember (current_property, GetModifierLocations (), GetLocation ($6)); + + lexer.PropertyParsing = true; + } + accessor_declarations + { + lexer.PropertyParsing = false; + + if (doc_support) + current_property.DocComment = ConsumeStoredComment (); + } + CLOSE_BRACE + { + lbag.AppendToMember (current_property, GetLocation ($10)); + lexer.parsing_modifiers = true; + } + opt_property_initializer + { + current_property = null; + } + | opt_attributes + opt_modifiers + member_type + member_declaration_name + { + lexer.parsing_generic_declaration = false; + if (doc_support) + tmpComment = Lexer.consume_doc_comment (); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + expression_block + { + var type = (FullNamedExpression) $3; + var property = new Property (current_type, type, (Modifiers) $2, + (MemberName) $4, (Attributes) $1); + + property.Get = new Property.GetMethod (property, Modifiers.COMPILER_GENERATED, null, property.Location); + property.Get.Block = (ToplevelBlock) $6; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, property.Get.Block.StartLocation, + "`{0}': interface members cannot have a definition", property.GetSignatureForError ()); + } + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (547, GetLocation ($3), "`{0}': property or indexer cannot have void type", property.GetSignatureForError ()); + + current_type.AddMember (property); + + current_local_parameters = null; + } + ; + +opt_property_initializer + : /* empty */ + | ASSIGN + { + ++lexer.parsing_block; + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + start_block (GetLocation ($1)); + } + property_initializer SEMICOLON + { + --lexer.parsing_block; + ((Property)current_property).Initializer = (Expression) $3; + lbag.AppendToMember (current_property, GetLocation ($1), GetLocation ($4)); + end_block (GetLocation ($4)); + current_local_parameters = null; + } + ; + +property_initializer + : expression + | array_initializer + ; + +indexer_declaration + : opt_attributes opt_modifiers + member_type indexer_declaration_name OPEN_BRACKET + { + valid_param_mod = ParameterModifierType.Params | ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_BRACKET + { + valid_param_mod = 0; + var type = (FullNamedExpression) $3; + Indexer indexer = new Indexer (current_type, type, (MemberName) $4, (Modifiers) $2, (ParametersCompiled) $7, (Attributes) $1); + + current_property = indexer; + + current_type.AddIndexer (indexer); + lbag.AddMember (current_property, GetModifierLocations (), GetLocation ($5), GetLocation ($8)); + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (620, GetLocation ($3), "`{0}': indexer return type cannot be `void'", indexer.GetSignatureForError ()); + + if (indexer.ParameterInfo.IsEmpty) { + report.Error (1551, GetLocation ($5), "Indexers must have at least one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.PropertyParsing = true; + current_local_parameters = (ParametersCompiled) $7; + } + indexer_body + { + lexer.PropertyParsing = false; + current_local_parameters = null; + + if (current_property.AccessorFirst != null && current_property.AccessorFirst.Block == null) + ((Indexer) current_property).ParameterInfo.CheckParameters (current_property); + + if (doc_support) + current_property.DocComment = ConsumeStoredComment (); + + current_property = null; + } + ; + +indexer_body + : OPEN_BRACE accessor_declarations CLOSE_BRACE + { + lbag.AppendToMember (current_property, GetLocation ($1), GetLocation ($3)); + } + | expression_block + { + current_property.Get = new Indexer.GetIndexerMethod (current_property, Modifiers.COMPILER_GENERATED, current_local_parameters, null, current_property.Location); + current_property.Get.Block = (ToplevelBlock) $1; + } + ; + +accessor_declarations + : get_accessor_declaration + | get_accessor_declaration accessor_declarations + | set_accessor_declaration + | set_accessor_declaration accessor_declarations + | error + { + if (yyToken == Token.CLOSE_BRACE) { + report.Error (548, lexer.Location, "`{0}': property or indexer must have at least one accessor", current_property.GetSignatureForError ()); + } else { + if (yyToken == Token.SEMICOLON) + report.Error (1597, lexer.Location, "Semicolon after method or accessor block is not valid"); + else + report.Error (1014, GetLocation ($1), "A get or set accessor expected"); + } + } + ; + +get_accessor_declaration + : opt_attributes opt_modifiers GET + { + if ($2 != ModifierNone && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (GetLocation ($2), "access modifiers on properties"); + } + + if (current_property.Get != null) { + report.Error (1007, GetLocation ($3), "Property accessor already defined"); + } + + if (current_property is Indexer) { + current_property.Get = new Indexer.GetIndexerMethod (current_property, (Modifiers) $2, ((Indexer)current_property).ParameterInfo.Clone (), + (Attributes) $1, GetLocation ($3)); + } else { + current_property.Get = new Property.GetMethod (current_property, + (Modifiers) $2, (Attributes) $1, GetLocation ($3)); + } + + current_local_parameters = current_property.Get.ParameterInfo; + lexer.PropertyParsing = false; + } + accessor_body + { + if ($5 != null) { + current_property.Get.Block = (ToplevelBlock) $5; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_property.Get.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_property.Get.GetSignatureForError ()); + } + lbag.AddMember (current_property.Get, GetModifierLocations ()); + } else { + lbag.AddMember (current_property.Get, GetModifierLocations (), savedLocation); + } + + current_local_parameters = null; + lexer.PropertyParsing = true; + + if (doc_support) + if (Lexer.doc_state == XmlCommentState.Error) + Lexer.doc_state = XmlCommentState.NotAllowed; + } + ; + +set_accessor_declaration + : opt_attributes opt_modifiers SET + { + if ($2 != ModifierNone && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (GetLocation ($2), "access modifiers on properties"); + } + + if (current_property.Set != null) { + report.Error (1007, GetLocation ($3), "Property accessor already defined"); + } + + if (current_property is Indexer) { + current_property.Set = new Indexer.SetIndexerMethod (current_property, (Modifiers) $2, + ParametersCompiled.MergeGenerated (compiler, + ((Indexer)current_property).ParameterInfo, true, new Parameter ( + current_property.TypeExpression, "value", Parameter.Modifier.NONE, null, GetLocation ($3)), + null), + (Attributes) $1, GetLocation ($3)); + } else { + current_property.Set = new Property.SetMethod (current_property, (Modifiers) $2, + ParametersCompiled.CreateImplicitParameter (current_property.TypeExpression, GetLocation ($3)), + (Attributes) $1, GetLocation ($3)); + } + + current_local_parameters = current_property.Set.ParameterInfo; + lexer.PropertyParsing = false; + } + accessor_body + { + if ($5 != null) { + current_property.Set.Block = (ToplevelBlock) $5; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_property.Set.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_property.Set.GetSignatureForError ()); + } + lbag.AddMember (current_property.Set, GetModifierLocations ()); + } else { + lbag.AddMember (current_property.Set, GetModifierLocations (), savedLocation); + } + + current_local_parameters = null; + lexer.PropertyParsing = true; + + if (doc_support + && Lexer.doc_state == XmlCommentState.Error) + Lexer.doc_state = XmlCommentState.NotAllowed; + } + ; + +accessor_body + : block + | SEMICOLON + { + savedLocation = GetLocation ($1); + $$ = null; + } + | error + { + Error_SyntaxError (1043, yyToken, "Invalid accessor body"); + $$ = null; + } + ; + +interface_declaration + : opt_attributes + opt_modifiers + opt_partial + INTERFACE + { + } + type_declaration_name + { + lexer.ConstraintsParsing = true; + push_current_container (new Interface (current_container, (MemberName) $6, (Modifiers) $2, (Attributes) $1), $3); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($4)); + } + opt_class_base + opt_type_parameter_constraints_clauses + { + lexer.ConstraintsParsing = false; + + if ($9 != null) + current_container.SetConstraints ((List) $9); + + if (doc_support) { + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.parsing_modifiers = true; + } + OPEN_BRACE opt_interface_member_declarations CLOSE_BRACE + { + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_semicolon + { + if ($15 == null) { + lbag.AppendToMember (current_container, GetLocation ($11), GetLocation ($13)); + } else { + lbag.AppendToMember (current_container, GetLocation ($11), GetLocation ($13), GetLocation ($15)); + } + $$ = pop_current_class (); + } + | opt_attributes opt_modifiers opt_partial INTERFACE error + { + Error_SyntaxError (yyToken); + } + ; + +opt_interface_member_declarations + : /* empty */ + | interface_member_declarations + ; + +interface_member_declarations + : interface_member_declaration + { + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + | interface_member_declarations interface_member_declaration + { + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + ; + +interface_member_declaration + : constant_declaration + { + report.Error (525, GetLocation ($1), "Interfaces cannot contain fields or constants"); + } + | field_declaration + { + report.Error (525, GetLocation ($1), "Interfaces cannot contain fields or constants"); + } + | method_declaration + | property_declaration + | event_declaration + | indexer_declaration + | operator_declaration + { + report.Error (567, GetLocation ($1), "Interfaces cannot contain operators"); + } + | constructor_declaration + { + report.Error (526, GetLocation ($1), "Interfaces cannot contain contructors"); + } + | type_declaration + { + report.Error (524, GetLocation ($1), "Interfaces cannot declare classes, structs, interfaces, delegates, or enumerations"); + } + ; + +operator_declaration + : opt_attributes opt_modifiers operator_declarator + { + } + method_body_expression_block + { + OperatorDeclaration decl = (OperatorDeclaration) $3; + if (decl != null) { + Operator op = new Operator ( + current_type, decl.optype, decl.ret_type, (Modifiers) $2, + current_local_parameters, + (ToplevelBlock) $5, (Attributes) $1, decl.location); + + if (op.Block == null) + op.ParameterInfo.CheckParameters (op); + + if (doc_support) { + op.DocComment = tmpComment; + Lexer.doc_state = XmlCommentState.Allowed; + } + + // Note again, checking is done in semantic analysis + current_type.AddOperator (op); + + lbag.AddMember (op, GetModifierLocations (), lbag.GetLocations (decl)); + if ($5 == null) { // Semicolon + lbag.AddLocation (op, savedLocation); + } + } + + current_local_parameters = null; + } + ; + +operator_type + : type_expression_or_array + | VOID + { + report.Error (590, GetLocation ($1), "User-defined operators cannot return void"); + $$ = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + } + ; + +operator_declarator + : operator_type OPERATOR overloadable_operator OPEN_PARENS + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + + Location loc = GetLocation ($2); + Operator.OpType op = (Operator.OpType) $3; + current_local_parameters = (ParametersCompiled)$6; + + int p_count = current_local_parameters.Count; + if (p_count == 1) { + if (op == Operator.OpType.Addition) + op = Operator.OpType.UnaryPlus; + else if (op == Operator.OpType.Subtraction) + op = Operator.OpType.UnaryNegation; + } + + if (IsUnaryOperator (op)) { + if (p_count == 2) { + report.Error (1020, loc, "Overloadable binary operator expected"); + } else if (p_count != 1) { + report.Error (1535, loc, "Overloaded unary operator `{0}' takes one parameter", + Operator.GetName (op)); + } + } else { + if (p_count == 1) { + report.Error (1019, loc, "Overloadable unary operator expected"); + } else if (p_count != 2) { + report.Error (1534, loc, "Overloaded binary operator `{0}' takes two parameters", + Operator.GetName (op)); + } + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + $$ = new OperatorDeclaration (op, (FullNamedExpression) $1, loc); + lbag.AddLocation ($$, GetLocation ($2), savedOperatorLocation, GetLocation ($4), GetLocation ($7)); + } + | conversion_operator_declarator + ; + +overloadable_operator +// Unary operators: + : BANG { $$ = Operator.OpType.LogicalNot; savedOperatorLocation = GetLocation ($1); } + | TILDE { $$ = Operator.OpType.OnesComplement; savedOperatorLocation = GetLocation ($1); } + | OP_INC { $$ = Operator.OpType.Increment; savedOperatorLocation = GetLocation ($1); } + | OP_DEC { $$ = Operator.OpType.Decrement; savedOperatorLocation = GetLocation ($1); } + | TRUE { $$ = Operator.OpType.True; savedOperatorLocation = GetLocation ($1); } + | FALSE { $$ = Operator.OpType.False; savedOperatorLocation = GetLocation ($1); } +// Unary and binary: + | PLUS { $$ = Operator.OpType.Addition; savedOperatorLocation = GetLocation ($1); } + | MINUS { $$ = Operator.OpType.Subtraction; savedOperatorLocation = GetLocation ($1); } +// Binary: + | STAR { $$ = Operator.OpType.Multiply; savedOperatorLocation = GetLocation ($1); } + | DIV { $$ = Operator.OpType.Division; savedOperatorLocation = GetLocation ($1); } + | PERCENT { $$ = Operator.OpType.Modulus; savedOperatorLocation = GetLocation ($1); } + | BITWISE_AND { $$ = Operator.OpType.BitwiseAnd; savedOperatorLocation = GetLocation ($1); } + | BITWISE_OR { $$ = Operator.OpType.BitwiseOr; savedOperatorLocation = GetLocation ($1); } + | CARRET { $$ = Operator.OpType.ExclusiveOr; savedOperatorLocation = GetLocation ($1); } + | OP_SHIFT_LEFT { $$ = Operator.OpType.LeftShift; savedOperatorLocation = GetLocation ($1); } + | OP_SHIFT_RIGHT { $$ = Operator.OpType.RightShift; savedOperatorLocation = GetLocation ($1); } + | OP_EQ { $$ = Operator.OpType.Equality; savedOperatorLocation = GetLocation ($1); } + | OP_NE { $$ = Operator.OpType.Inequality; savedOperatorLocation = GetLocation ($1); } + | OP_GT { $$ = Operator.OpType.GreaterThan; savedOperatorLocation = GetLocation ($1); } + | OP_LT { $$ = Operator.OpType.LessThan; savedOperatorLocation = GetLocation ($1); } + | OP_GE { $$ = Operator.OpType.GreaterThanOrEqual; savedOperatorLocation = GetLocation ($1); } + | OP_LE { $$ = Operator.OpType.LessThanOrEqual; savedOperatorLocation = GetLocation ($1); } + ; + +conversion_operator_declarator + : IMPLICIT OPERATOR type OPEN_PARENS + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + + Location loc = GetLocation ($2); + current_local_parameters = (ParametersCompiled)$6; + + if (current_local_parameters.Count != 1) { + report.Error (1535, loc, "Overloaded unary operator `implicit' takes one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + $$ = new OperatorDeclaration (Operator.OpType.Implicit, (FullNamedExpression) $3, loc); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2), GetLocation ($4), GetLocation ($7)); + } + | EXPLICIT OPERATOR type OPEN_PARENS + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + + Location loc = GetLocation ($2); + current_local_parameters = (ParametersCompiled)$6; + + if (current_local_parameters.Count != 1) { + report.Error (1535, loc, "Overloaded unary operator `explicit' takes one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + $$ = new OperatorDeclaration (Operator.OpType.Explicit, (FullNamedExpression) $3, loc); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2), GetLocation ($4), GetLocation ($7)); + } + | IMPLICIT error + { + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + $$ = new OperatorDeclaration (Operator.OpType.Implicit, null, GetLocation ($1)); + } + | EXPLICIT error + { + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + $$ = new OperatorDeclaration (Operator.OpType.Explicit, null, GetLocation ($1)); + } + ; + +constructor_declaration + : constructor_declarator + constructor_body + { + Constructor c = (Constructor) $1; + c.Block = (ToplevelBlock) $2; + + if (doc_support) + c.DocComment = ConsumeStoredComment (); + + current_local_parameters = null; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + ; + +constructor_declarator + : opt_attributes + opt_modifiers + IDENTIFIER + { + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + valid_param_mod = ParameterModifierType.All; + } + OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + current_local_parameters = (ParametersCompiled) $6; + + var lt = (LocatedToken) $3; + var mods = (Modifiers) $2; + var c = new Constructor (current_type, lt.Value, mods, (Attributes) $1, current_local_parameters, lt.Location); + + if (lt.Value != current_container.MemberName.Name) { + report.Error (1520, c.Location, "Class, struct, or interface method must have a return type"); + } else if ((mods & Modifiers.STATIC) != 0) { + if ((mods & Modifiers.AccessibilityMask) != 0){ + report.Error (515, c.Location, + "`{0}': static constructor cannot have an access modifier", + c.GetSignatureForError ()); + } + } + + current_type.AddConstructor (c); + lbag.AddMember (c, GetModifierLocations (), GetLocation ($5), GetLocation ($7)); + $$ = c; + + // + // start block here, so possible anonymous methods inside + // constructor initializer can get correct parent block + // + start_block (lexer.Location); + } + opt_constructor_initializer + { + if ($9 != null) { + var c = (Constructor) $8; + c.Initializer = (ConstructorInitializer) $9; + + if (c.IsStatic) { + report.Error (514, c.Location, + "`{0}': static constructor cannot have an explicit `this' or `base' constructor call", + c.GetSignatureForError ()); + } + } + + $$ = $8; + } + ; + +constructor_body + : block_prepared + | SEMICOLON { current_block = null; $$ = null; } + ; + +opt_constructor_initializer + : /* Empty */ + | constructor_initializer + ; + +constructor_initializer + : COLON BASE OPEN_PARENS + { + ++lexer.parsing_block; + } + opt_argument_list CLOSE_PARENS + { + --lexer.parsing_block; + $$ = new ConstructorBaseInitializer ((Arguments) $5, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3), GetLocation ($6)); + } + | COLON THIS OPEN_PARENS + { + ++lexer.parsing_block; + } + opt_argument_list CLOSE_PARENS + { + --lexer.parsing_block; + $$ = new ConstructorThisInitializer ((Arguments) $5, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3), GetLocation ($6)); + } + | COLON error + { + Error_SyntaxError (yyToken); + $$ = new ConstructorThisInitializer (null, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($1)); + } + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +destructor_declaration + : opt_attributes opt_modifiers TILDE + { + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + IDENTIFIER OPEN_PARENS CLOSE_PARENS method_body + { + var lt = (LocatedToken) $5; + if (lt.Value != current_container.MemberName.Name){ + report.Error (574, lt.Location, "Name of destructor must match name of class"); + } else if (current_container.Kind != MemberKind.Class){ + report.Error (575, lt.Location, "Only class types can contain destructor"); + } + + Destructor d = new Destructor (current_type, (Modifiers) $2, + ParametersCompiled.EmptyReadOnlyParameters, (Attributes) $1, lt.Location); + d.Identifier = lt.Value; + if (doc_support) + d.DocComment = ConsumeStoredComment (); + + d.Block = (ToplevelBlock) $8; + current_type.AddMember (d); + lbag.AddMember (d, GetModifierLocations (), GetLocation ($3), GetLocation ($6), GetLocation ($7)); + + current_local_parameters = null; + } + ; + +event_declaration + : opt_attributes + opt_modifiers + EVENT type member_declaration_name + { + current_event_field = new EventField (current_type, (FullNamedExpression) $4, (Modifiers) $2, (MemberName) $5, (Attributes) $1); + current_type.AddMember (current_event_field); + + if (current_event_field.MemberName.ExplicitInterface != null) { + report.Error (71, current_event_field.Location, "`{0}': An explicit interface implementation of an event must use property syntax", + current_event_field.GetSignatureForError ()); + } + + $$ = current_event_field; + } + opt_event_initializer + opt_event_declarators + SEMICOLON + { + if (doc_support) { + current_event_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + if (current_event_field.Initializer != null) { + lbag.AddMember (current_event_field, GetModifierLocations (), GetLocation ($3), savedEventAssignLocation, GetLocation ($9)); + } else { + lbag.AddMember (current_event_field, GetModifierLocations (), GetLocation ($3), GetLocation ($9)); + } + current_event_field = null; + } + | opt_attributes + opt_modifiers + EVENT type member_declaration_name + OPEN_BRACE + { + current_event = new EventProperty (current_type, (FullNamedExpression) $4, (Modifiers) $2, (MemberName) $5, (Attributes) $1); + current_type.AddMember (current_event); + lbag.AddMember (current_event, GetModifierLocations (), GetLocation ($3), GetLocation ($6)); + + lexer.EventParsing = true; + } + event_accessor_declarations + { + if (current_container.Kind == MemberKind.Interface) + report.Error (69, GetLocation ($6), "Event in interface cannot have add or remove accessors"); + + lexer.EventParsing = false; + } + CLOSE_BRACE + { + if (doc_support) { + current_event.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lbag.AppendToMember (current_event, GetLocation ($9)); + current_event = null; + current_local_parameters = null; + } + | opt_attributes + opt_modifiers + EVENT type error + { + Error_SyntaxError (yyToken); + + current_type.AddMember (new EventField (current_type, (FullNamedExpression) $4, (Modifiers) $2, MemberName.Null, (Attributes) $1)); + } + ; + +opt_event_initializer + : /* empty */ + | ASSIGN + { + ++lexer.parsing_block; + } + event_variable_initializer + { + --lexer.parsing_block; + savedEventAssignLocation = GetLocation ($1); + current_event_field.Initializer = (Expression) $3; + } + ; + +opt_event_declarators + : /* empty */ + | event_declarators + ; + +event_declarators + : event_declarator + { + current_event_field.AddDeclarator ((FieldDeclarator) $1); + } + | event_declarators event_declarator + { + current_event_field.AddDeclarator ((FieldDeclarator) $2); + } + ; + +event_declarator + : COMMA IDENTIFIER + { + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null); + lbag.AddLocation ($$, GetLocation ($1)); + } + | COMMA IDENTIFIER ASSIGN + { + ++lexer.parsing_block; + } + event_variable_initializer + { + --lexer.parsing_block; + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) $5); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3)); + } + ; + +event_variable_initializer + : { + if (current_container.Kind == MemberKind.Interface) { + report.Error (68, lexer.Location, "`{0}': event in interface cannot have an initializer", + current_event_field.GetSignatureForError ()); + } + + if ((current_event_field.ModFlags & Modifiers.ABSTRACT) != 0) { + report.Error (74, lexer.Location, "`{0}': abstract event cannot have an initializer", + current_event_field.GetSignatureForError ()); + } + } + variable_initializer + { + $$ = $2; + } + ; + +event_accessor_declarations + : add_accessor_declaration remove_accessor_declaration + | remove_accessor_declaration add_accessor_declaration + | add_accessor_declaration + { + report.Error (65, lexer.Location, "`{0}': event property must have both add and remove accessors", + current_event.GetSignatureForError ()); + } + | remove_accessor_declaration + { + report.Error (65, lexer.Location, "`{0}': event property must have both add and remove accessors", + current_event.GetSignatureForError ()); + } + | error + { + report.Error (1055, GetLocation ($1), "An add or remove accessor expected"); + $$ = null; + } + ; + +add_accessor_declaration + : opt_attributes opt_modifiers ADD + { + if ($2 != ModifierNone) { + report.Error (1609, GetLocation ($2), "Modifiers cannot be placed on event accessor declarations"); + } + + current_event.Add = new EventProperty.AddDelegateMethod (current_event, (Attributes) $1, GetLocation ($3)); + current_local_parameters = current_event.Add.ParameterInfo; + + lbag.AddMember (current_event.Add, GetModifierLocations ()); + lexer.EventParsing = false; + } + event_accessor_block + { + lexer.EventParsing = true; + + current_event.Add.Block = (ToplevelBlock) $5; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_event.Add.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_event.Add.GetSignatureForError ()); + } + + current_local_parameters = null; + } + ; + +remove_accessor_declaration + : opt_attributes opt_modifiers REMOVE + { + if ($2 != ModifierNone) { + report.Error (1609, GetLocation ($2), "Modifiers cannot be placed on event accessor declarations"); + } + + current_event.Remove = new EventProperty.RemoveDelegateMethod (current_event, (Attributes) $1, GetLocation ($3)); + current_local_parameters = current_event.Remove.ParameterInfo; + + lbag.AddMember (current_event.Remove, GetModifierLocations ()); + lexer.EventParsing = false; + } + event_accessor_block + { + lexer.EventParsing = true; + + current_event.Remove.Block = (ToplevelBlock) $5; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_event.Remove.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_event.Remove.GetSignatureForError ()); + } + + current_local_parameters = null; + } + ; + +event_accessor_block + : opt_semicolon + { + report.Error (73, lexer.Location, "An add or remove accessor must have a body"); + $$ = null; + } + | block; + ; + +attributes_without_members + : attribute_sections CLOSE_BRACE + { + current_type.UnattachedAttributes = (Attributes) $1; + report.Error (1519, GetLocation ($1), "An attribute is missing member declaration"); + lexer.putback ('}'); + } + ; + +// For full ast try to recover incomplete ambiguous member +// declaration in form on class X { public int } +incomplete_member + : opt_attributes opt_modifiers member_type CLOSE_BRACE + { + report.Error (1519, lexer.Location, "Unexpected symbol `}' in class, struct, or interface member declaration"); + + lexer.putback ('}'); + + lexer.parsing_generic_declaration = false; + FullNamedExpression type = (FullNamedExpression) $3; + current_field = new Field (current_type, type, (Modifiers) $2, MemberName.Null, (Attributes) $1); + current_type.AddField (current_field); + lbag.AddMember (current_field, GetModifierLocations ()); + $$ = current_field; + } + ; + +enum_declaration + : opt_attributes + opt_modifiers + ENUM + type_declaration_name + opt_enum_base + { + if (doc_support) + enumTypeComment = Lexer.consume_doc_comment (); + } + OPEN_BRACE + { + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + + MemberName name = (MemberName) $4; + if (name.IsGeneric) { + report.Error (1675, name.Location, "Enums cannot have type parameters"); + } + + push_current_container (new Enum (current_container, (FullNamedExpression) $5, (Modifiers) $2, name, (Attributes) $1), null); + if ($5 != null) { + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($3), savedLocation, GetLocation ($7)); + } else { + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($3), GetLocation ($7)); + } + } + opt_enum_member_declarations + { + lexer.parsing_modifiers = true; + + // here will be evaluated after CLOSE_BLACE is consumed. + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + CLOSE_BRACE opt_semicolon + { + lbag.AppendToMember (current_container, GetLocation ($11)); + if ($12 != null) { + lbag.AppendToMember (current_container, GetLocation ($12)); + } + if (doc_support) + current_container.DocComment = enumTypeComment; + + --lexer.parsing_declaration; + +// if (doc_support) +// em.DocComment = ev.DocComment; + + $$ = pop_current_class (); + } + ; + +opt_enum_base + : /* empty */ + | COLON type + { + savedLocation = GetLocation ($1); + $$ = $2; + } + | COLON error + { + Error_TypeExpected (GetLocation ($1)); + $$ = null; + } + ; + +opt_enum_member_declarations + : /* empty */ + | enum_member_declarations + | enum_member_declarations COMMA + { + lbag.AppendToMember (current_container, GetLocation ($2)); + } + ; + +enum_member_declarations + : enum_member_declaration + | enum_member_declarations COMMA enum_member_declaration + { + lbag.AppendToMember (current_container, GetLocation ($2)); + $$ = $3; + } + ; + +enum_member_declaration + : opt_attributes IDENTIFIER + { + var lt = (LocatedToken) $2; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) $1); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) { + em.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + $$ = em; + } + | opt_attributes IDENTIFIER + { + ++lexer.parsing_block; + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + } + ASSIGN constant_expression + { + --lexer.parsing_block; + + var lt = (LocatedToken) $2; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) $1); + em.Initializer = new ConstInitializer (em, (Expression) $5, GetLocation ($4)); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) + em.DocComment = ConsumeStoredComment (); + + $$ = em; + } + | opt_attributes IDENTIFIER error + { + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) $2; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) $1); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) { + em.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + $$ = em; + } + | attributes_without_members + ; + +delegate_declaration + : opt_attributes + opt_modifiers + DELEGATE + member_type type_declaration_name + OPEN_PARENS + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out | ParameterModifierType.Params | ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + + ParametersCompiled p = (ParametersCompiled) $8; + + Delegate del = new Delegate (current_container, (FullNamedExpression) $4, (Modifiers) $2, (MemberName) $5, p, (Attributes) $1); + + p.CheckParameters (del); + + current_container.AddTypeContainer (del); + + current_delegate = del; + lexer.ConstraintsParsing = true; + } + opt_type_parameter_constraints_clauses + { + lexer.ConstraintsParsing = false; + } + SEMICOLON + { + if (doc_support) { + current_delegate.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + if ($11 != null) + current_delegate.SetConstraints ((List) $11); + lbag.AddMember (current_delegate, GetModifierLocations (), GetLocation ($3), GetLocation ($6), GetLocation ($9), GetLocation ($13)); + + $$ = current_delegate; + + current_delegate = null; + } + ; + +opt_nullable + : /* empty */ + | INTERR_NULLABLE + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "nullable types"); + + $$ = ComposedTypeSpecifier.CreateNullable (GetLocation ($1)); + } + ; + +namespace_or_type_expr + : member_name + | qualified_alias_member IDENTIFIER opt_type_argument_list + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + + $$ = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) $3, lt1.Location); + lbag.AddLocation ($$, savedLocation, GetLocation ($2)); + } + | qualified_alias_member IDENTIFIER generic_dimension + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + var qam = new QualifiedAliasMember (lt1.Value, lt2.Value, (int) $3, lt1.Location); + lbag.AddLocation (qam.TypeArguments, Lexer.GenericDimensionLocations); + $$ = qam; + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +member_name + : simple_name_expr + | namespace_or_type_expr DOT IDENTIFIER opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess ((Expression) $1, lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | namespace_or_type_expr DOT IDENTIFIER generic_dimension + { + var lt = (LocatedToken) $3; + var ma = new MemberAccess ((Expression) $1, lt.Value, (int) $4, lt.Location); + lbag.AddLocation (ma.TypeArguments, Lexer.GenericDimensionLocations); + $$ = ma; + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +simple_name_expr + : IDENTIFIER opt_type_argument_list + { + var lt = (LocatedToken) $1; + $$ = new SimpleName (lt.Value, (TypeArguments)$2, lt.Location); + } + | IDENTIFIER generic_dimension + { + var lt = (LocatedToken) $1; + var sn = new SimpleName (lt.Value, (int) $2, lt.Location); + lbag.AddLocation (sn.TypeArguments, Lexer.GenericDimensionLocations); + $$ = sn; + } + ; + +// +// Generics arguments (any type, without attributes) +// +opt_type_argument_list + : /* empty */ + | OP_GENERICS_LT type_arguments OP_GENERICS_GT + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "generics"); + var list = locationListStack.Pop (); + list.Add (GetLocation ($1)); + list.Add (GetLocation ($2)); + lbag.AddLocation ($2, list); + + $$ = $2;; + } + | OP_GENERICS_LT error + { + Error_TypeExpected (lexer.Location); + $$ = new TypeArguments (); + } + ; + +type_arguments + : type + { + TypeArguments type_args = new TypeArguments (); + type_args.Add ((FullNamedExpression) $1); + $$ = type_args; + locationListStack.Push (new List ()); + } + | type_arguments COMMA type + { + TypeArguments type_args = (TypeArguments) $1; + type_args.Add ((FullNamedExpression) $3); + $$ = type_args; + locationListStack.Peek ().Add (GetLocation ($2)); + } + ; + +// +// Generics parameters (identifiers only, with attributes), used in type or method declarations +// +type_declaration_name + : IDENTIFIER + { + lexer.parsing_generic_declaration = true; + } + opt_type_parameter_list + { + lexer.parsing_generic_declaration = false; + var lt = (LocatedToken) $1; + $$ = new MemberName (lt.Value, (TypeParameters)$3, lt.Location); + } + ; + +member_declaration_name + : method_declaration_name + { + MemberName mn = (MemberName)$1; + if (mn.TypeParameters != null) + syntax_error (mn.Location, string.Format ("Member `{0}' cannot declare type arguments", + mn.GetSignatureForError ())); + } + ; + +method_declaration_name + : type_declaration_name + | explicit_interface IDENTIFIER opt_type_parameter_list + { + lexer.parsing_generic_declaration = false; + var lt = (LocatedToken) $2; + $$ = new MemberName (lt.Value, (TypeParameters) $3, (ATypeNameExpression) $1, lt.Location); + } + ; + +indexer_declaration_name + : THIS + { + lexer.parsing_generic_declaration = false; + $$ = new MemberName (TypeDefinition.DefaultIndexerName, GetLocation ($1)); + } + | explicit_interface THIS + { + lexer.parsing_generic_declaration = false; + $$ = new MemberName (TypeDefinition.DefaultIndexerName, null, (ATypeNameExpression) $1, GetLocation ($2)); + } + ; + +explicit_interface + : IDENTIFIER opt_type_argument_list DOT + { + var lt = (LocatedToken) $1; + $$ = new SimpleName (lt.Value, (TypeArguments) $2, lt.Location); + lbag.AddLocation ($$, GetLocation ($3)); + } + | qualified_alias_member IDENTIFIER opt_type_argument_list DOT + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + + $$ = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) $3, lt1.Location); + lbag.AddLocation ($$, savedLocation, GetLocation ($4)); + } + | explicit_interface IDENTIFIER opt_type_argument_list DOT + { + var lt = (LocatedToken) $2; + $$ = new MemberAccess ((ATypeNameExpression) $1, lt.Value, (TypeArguments) $3, lt.Location); + lbag.AddLocation ($$, GetLocation ($4)); + } + ; + +opt_type_parameter_list + : /* empty */ + | OP_GENERICS_LT_DECL type_parameters OP_GENERICS_GT + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "generics"); + + $$ = $2; + var list = locationListStack.Pop (); + list.Add (GetLocation ($1)); + list.Add (GetLocation ($2)); + lbag.AddLocation ($2, list); + } + ; + +type_parameters + : type_parameter + { + var tparams = new TypeParameters (); + tparams.Add ((TypeParameter)$1); + $$ = tparams; + locationListStack.Push (new List ()); + } + | type_parameters COMMA type_parameter + { + var tparams = (TypeParameters) $1; + tparams.Add ((TypeParameter)$3); + $$ = tparams; + locationListStack.Peek ().Add (GetLocation ($2)); + } + ; + +type_parameter + : opt_attributes opt_type_parameter_variance IDENTIFIER + { + var lt = (LocatedToken)$3; + var variance = (VarianceDecl) $2; + $$ = new TypeParameter (new MemberName (lt.Value, lt.Location), (Attributes)$1, variance); + if (variance != null) + lbag.AddLocation ($$, savedLocation); + } + | error + { + if (GetTokenName (yyToken) == "type") + report.Error (81, GetLocation ($1), "Type parameter declaration must be an identifier not a type"); + else + Error_SyntaxError (yyToken); + + $$ = new TypeParameter (MemberName.Null, null, null); + } + ; + +// +// All types where void is allowed +// +type_and_void + : type_expression_or_array + | VOID + { + $$ = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + } + ; + +member_type + : type_and_void + { + lexer.parsing_generic_declaration = true; + } + ; + +// +// A type which does not allow `void' to be used +// +type + : type_expression_or_array + | void_invalid + ; + +simple_type + : type_expression + | void_invalid + ; + +parameter_type + : type_expression_or_array + | VOID + { + report.Error (1536, GetLocation ($1), "Invalid parameter type `void'"); + $$ = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + } + ; + +type_expression_or_array + : type_expression + | type_expression rank_specifiers + { + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + ; + +type_expression + : namespace_or_type_expr opt_nullable + { + if ($2 != null) { + $$ = new ComposedCast ((ATypeNameExpression) $1, (ComposedTypeSpecifier) $2); + } else { + var sn = $1 as SimpleName; + if (sn != null && sn.Name == "var") + $$ = new VarExpr (sn.Location); + else + $$ = $1; + } + } + | namespace_or_type_expr pointer_stars + { + $$ = new ComposedCast ((ATypeNameExpression) $1, (ComposedTypeSpecifier) $2); + } + | builtin_type_expression + ; + +void_invalid + : VOID + { + Expression.Error_VoidInvalidInTheContext (GetLocation ($1), report); + $$ = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + } + ; + +builtin_type_expression + : builtin_types opt_nullable + { + if ($2 != null) + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + | builtin_types pointer_stars + { + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + | VOID pointer_stars + { + $$ = new ComposedCast (new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)), (ComposedTypeSpecifier) $2); + } + ; + +type_list + : base_type_name + { + var types = new List (2); + types.Add ((FullNamedExpression) $1); + $$ = types; + } + | type_list COMMA base_type_name + { + var types = (List) $1; + types.Add ((FullNamedExpression) $3); + lbag.AddLocation (types, GetLocation ($2)); + $$ = types; + } + ; + +base_type_name + : type + { + if ($1 is ComposedCast) { + report.Error (1521, GetLocation ($1), "Invalid base type `{0}'", ((ComposedCast)$1).GetSignatureForError ()); + } + $$ = $1; + } + ; + +/* + * replaces all the productions for isolating the various + * simple types, but we need this to reuse it easily in variable_type + */ +builtin_types + : OBJECT { $$ = new TypeExpression (compiler.BuiltinTypes.Object, GetLocation ($1)); } + | STRING { $$ = new TypeExpression (compiler.BuiltinTypes.String, GetLocation ($1)); } + | BOOL { $$ = new TypeExpression (compiler.BuiltinTypes.Bool, GetLocation ($1)); } + | DECIMAL { $$ = new TypeExpression (compiler.BuiltinTypes.Decimal, GetLocation ($1)); } + | FLOAT { $$ = new TypeExpression (compiler.BuiltinTypes.Float, GetLocation ($1)); } + | DOUBLE { $$ = new TypeExpression (compiler.BuiltinTypes.Double, GetLocation ($1)); } + | integral_type + ; + +integral_type + : SBYTE { $$ = new TypeExpression (compiler.BuiltinTypes.SByte, GetLocation ($1)); } + | BYTE { $$ = new TypeExpression (compiler.BuiltinTypes.Byte, GetLocation ($1)); } + | SHORT { $$ = new TypeExpression (compiler.BuiltinTypes.Short, GetLocation ($1)); } + | USHORT { $$ = new TypeExpression (compiler.BuiltinTypes.UShort, GetLocation ($1)); } + | INT { $$ = new TypeExpression (compiler.BuiltinTypes.Int, GetLocation ($1)); } + | UINT { $$ = new TypeExpression (compiler.BuiltinTypes.UInt, GetLocation ($1)); } + | LONG { $$ = new TypeExpression (compiler.BuiltinTypes.Long, GetLocation ($1)); } + | ULONG { $$ = new TypeExpression (compiler.BuiltinTypes.ULong, GetLocation ($1)); } + | CHAR { $$ = new TypeExpression (compiler.BuiltinTypes.Char, GetLocation ($1)); } + ; + +// +// Expressions, section 7.5 +// + + +primary_expression + : primary_expression_or_type + | literal + | array_creation_expression + | parenthesized_expression + | default_value_expression + | invocation_expression + | element_access + | this_access + | base_access + | post_increment_expression + | post_decrement_expression + | object_or_delegate_creation_expression + | anonymous_type_expression + | typeof_expression + | sizeof_expression + | checked_expression + | unchecked_expression + | pointer_member_access + | anonymous_method_expression + | undocumented_expressions + ; + +primary_expression_or_type + : simple_name_expr + | IDENTIFIER GENERATE_COMPLETION { + var lt = (LocatedToken) $1; + $$ = new CompletionSimpleName (MemberName.MakeName (lt.Value, null), lt.Location); + } + | member_access + ; + +literal + : boolean_literal + | LITERAL + | NULL { $$ = new NullLiteral (GetLocation ($1)); } + ; + +boolean_literal + : TRUE { $$ = new BoolLiteral (compiler.BuiltinTypes, true, GetLocation ($1)); } + | FALSE { $$ = new BoolLiteral (compiler.BuiltinTypes, false, GetLocation ($1)); } + ; + + +// +// Here is the trick, tokenizer may think that parens is a special but +// parser is interested in open parens only, so we merge them. +// Consider: if (a)foo (); +// +open_parens_any + : OPEN_PARENS + | OPEN_PARENS_CAST + ; + +// +// Use this production to accept closing parenthesis or +// performing completion +// +close_parens + : CLOSE_PARENS + | COMPLETE_COMPLETION + ; + + +parenthesized_expression + : OPEN_PARENS expression CLOSE_PARENS + { + $$ = new ParenthesizedExpression ((Expression) $2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3)); + } + | OPEN_PARENS expression COMPLETE_COMPLETION + { + $$ = new ParenthesizedExpression ((Expression) $2, GetLocation ($1)); + } + ; + +member_access + : primary_expression DOT identifier_inside_body opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess ((Expression) $1, lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | primary_expression DOT identifier_inside_body generic_dimension + { + var lt = (LocatedToken) $3; + var ma = new MemberAccess ((Expression) $1, lt.Value, (int) $4, lt.Location); + lbag.AddLocation (ma.TypeArguments, Lexer.GenericDimensionLocations); + $$ = ma; + lbag.AddLocation ($$, GetLocation ($2)); + } + | primary_expression INTERR_OPERATOR DOT identifier_inside_body opt_type_argument_list + { + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation ($2), "null propagating operator"); + + var lt = (LocatedToken) $4; + $$ = new ConditionalMemberAccess ((Expression) $1, lt.Value, (TypeArguments) $5, lt.Location); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3)); + } + | builtin_types DOT identifier_inside_body opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess ((Expression) $1, lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | BASE DOT identifier_inside_body opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess (new BaseThis (GetLocation ($1)), lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | AWAIT DOT identifier_inside_body opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess (new SimpleName ("await", ((LocatedToken) $1).Location), lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | qualified_alias_member identifier_inside_body opt_type_argument_list + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + + $$ = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) $3, lt1.Location); + lbag.AddLocation ($$, savedLocation, GetLocation ($2)); + } + | qualified_alias_member identifier_inside_body generic_dimension + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + var qam = new QualifiedAliasMember (lt1.Value, lt2.Value, (int) $3, lt1.Location); + lbag.AddLocation (qam.TypeArguments, Lexer.GenericDimensionLocations); + $$ = qam; + lbag.AddLocation ($$, GetLocation ($2)); + } + | primary_expression DOT GENERATE_COMPLETION { + $$ = new CompletionMemberAccess ((Expression) $1, null,GetLocation ($3)); + } + | primary_expression DOT IDENTIFIER GENERATE_COMPLETION { + var lt = (LocatedToken) $3; + $$ = new CompletionMemberAccess ((Expression) $1, lt.Value, lt.Location); + } + | builtin_types DOT GENERATE_COMPLETION + { + $$ = new CompletionMemberAccess ((Expression) $1, null, lexer.Location); + } + | builtin_types DOT IDENTIFIER GENERATE_COMPLETION { + var lt = (LocatedToken) $3; + $$ = new CompletionMemberAccess ((Expression) $1, lt.Value, lt.Location); + } + ; + +invocation_expression + : primary_expression open_parens_any opt_argument_list close_parens + { + $$ = new Invocation ((Expression) $1, (Arguments) $3); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | primary_expression open_parens_any argument_list error + { + Error_SyntaxError (yyToken); + + $$ = new Invocation ((Expression) $1, (Arguments) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | primary_expression open_parens_any error + { + Error_SyntaxError (yyToken); + + $$ = new Invocation ((Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +opt_object_or_collection_initializer + : /* empty */ { $$ = null; } + | object_or_collection_initializer + ; + +object_or_collection_initializer + : OPEN_BRACE opt_member_initializer_list close_brace_or_complete_completion + { + if ($2 == null) { + $$ = new CollectionOrObjectInitializers (GetLocation ($1)); + } else { + $$ = new CollectionOrObjectInitializers ((List) $2, GetLocation ($1)); + } + lbag.AddLocation ($$, GetLocation ($3)); + } + | OPEN_BRACE member_initializer_list COMMA CLOSE_BRACE + { + $$ = new CollectionOrObjectInitializers ((List) $2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($4)); + } + ; + +opt_member_initializer_list + : /* empty */ { $$ = null; } + | member_initializer_list + { + $$ = $1; + } + ; + +member_initializer_list + : member_initializer + { + var a = new List (); + a.Add ((Expression) $1); + $$ = a; + } + | member_initializer_list COMMA member_initializer + { + var a = (List)$1; + a.Add ((Expression) $3); + lbag.AddLocation (a, GetLocation ($2)); + $$ = a; + } + | member_initializer_list error { + Error_SyntaxError (yyToken); + $$ = $1; + } + ; + +member_initializer + : IDENTIFIER ASSIGN initializer_value + { + var lt = (LocatedToken) $1; + $$ = new ElementInitializer (lt.Value, (Expression)$3, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | AWAIT ASSIGN initializer_value + { + var lt = (LocatedToken) Error_AwaitAsIdentifier ($1); + $$ = new ElementInitializer (lt.Value, (Expression)$3, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | GENERATE_COMPLETION + { + $$ = new CompletionElementInitializer (null, GetLocation ($1)); + } + | non_assignment_expression opt_COMPLETE_COMPLETION { + CompletionSimpleName csn = $1 as CompletionSimpleName; + if (csn == null) + $$ = new CollectionElementInitializer ((Expression)$1); + else + $$ = new CompletionElementInitializer (csn.Prefix, csn.Location); + } + | OPEN_BRACE expression_list CLOSE_BRACE + { + if ($2 == null) + $$ = new CollectionElementInitializer (GetLocation ($1)); + else { + $$ = new CollectionElementInitializer ((List)$2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2)); + } + lbag.AddLocation ($$, GetLocation ($3)); + } + | OPEN_BRACKET_EXPR expression_list CLOSE_BRACKET ASSIGN initializer_value + { + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation ($1), "dictionary initializer"); + + $$ = new DictionaryElementInitializer ((List)$2, (Expression) $5, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($4)); + } + | OPEN_BRACE CLOSE_BRACE + { + report.Error (1920, GetLocation ($1), "An element initializer cannot be empty"); + $$ = new CollectionElementInitializer (GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +initializer_value + : expression + | object_or_collection_initializer + ; + +opt_argument_list + : /* empty */ { $$ = null; } + | argument_list + ; + +argument_list + : argument_or_named_argument + { + Arguments list = new Arguments (4); + list.Add ((Argument) $1); + $$ = list; + } + | argument_list COMMA argument + { + Arguments list = (Arguments) $1; + if (list [list.Count - 1] is NamedArgument) + Error_NamedArgumentExpected ((NamedArgument) list [list.Count - 1]); + + list.Add ((Argument) $3); + lbag.AddLocation (list, GetLocation ($2)); + $$ = list; + } + | argument_list COMMA named_argument + { + Arguments list = (Arguments) $1; + NamedArgument a = (NamedArgument) $3; + for (int i = 0; i < list.Count; ++i) { + NamedArgument na = list [i] as NamedArgument; + if (na != null && na.Name == a.Name) + report.Error (1740, na.Location, "Named argument `{0}' specified multiple times", + na.Name); + } + + list.Add (a); + lbag.AddLocation (list, GetLocation ($2)); + $$ = list; + } + | argument_list COMMA error + { + if (lexer.putback_char == -1) + lexer.putback (')'); // TODO: Wrong but what can I do + Error_SyntaxError (yyToken); + $$ = $1; + } + | COMMA error + { + report.Error (839, GetLocation ($1), "An argument is missing"); + $$ = null; + } + ; + +argument + : expression + { + $$ = new Argument ((Expression) $1); + } + | non_simple_argument + ; + +argument_or_named_argument + : argument + | named_argument + ; + +non_simple_argument + : REF variable_reference + { + $$ = new Argument ((Expression) $2, Argument.AType.Ref); + lbag.AddLocation ($$, GetLocation ($1)); + } + | OUT variable_reference + { + $$ = new Argument ((Expression) $2, Argument.AType.Out); + lbag.AddLocation ($$, GetLocation ($1)); + } + | ARGLIST OPEN_PARENS argument_list CLOSE_PARENS + { + $$ = new Argument (new Arglist ((Arguments) $3, GetLocation ($1))); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | ARGLIST OPEN_PARENS CLOSE_PARENS + { + $$ = new Argument (new Arglist (GetLocation ($1))); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3)); + } + ; + +variable_reference + : expression + ; + +element_access + : primary_expression OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET + { + $$ = new ElementAccess ((Expression) $1, (Arguments) $3, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | primary_expression INTERR_OPERATOR OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET + { + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation ($2), "null propagating operator"); + + $$ = new ElementAccess ((Expression) $1, (Arguments) $4, GetLocation ($3)) { + ConditionalAccess = true + }; + + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($5)); + } + | primary_expression OPEN_BRACKET_EXPR expression_list_arguments error + { + Error_SyntaxError (yyToken); + $$ = new ElementAccess ((Expression) $1, (Arguments) $3, GetLocation ($2)); + } + | primary_expression OPEN_BRACKET_EXPR error + { + Error_SyntaxError (yyToken); + $$ = new ElementAccess ((Expression) $1, null, GetLocation ($2)); + } + ; + +expression_list + : expression_or_error + { + var list = new List (4); + list.Add ((Expression) $1); + $$ = list; + } + | expression_list COMMA expression_or_error + { + var list = (List) $1; + list.Add ((Expression) $3); + lbag.AddLocation (list, GetLocation ($2)); + $$ = list; + } + ; + +expression_list_arguments + : expression_list_argument + { + Arguments args = new Arguments (4); + args.Add ((Argument) $1); + $$ = args; + } + | expression_list_arguments COMMA expression_list_argument + { + Arguments args = (Arguments) $1; + if (args [args.Count - 1] is NamedArgument && !($3 is NamedArgument)) + Error_NamedArgumentExpected ((NamedArgument) args [args.Count - 1]); + + args.Add ((Argument) $3); + lbag.AddLocation (args, GetLocation ($2)); + $$ = args; + } + ; + +expression_list_argument + : expression + { + $$ = new Argument ((Expression) $1); + } + | named_argument + ; + +this_access + : THIS + { + $$ = new This (GetLocation ($1)); + } + ; + +base_access + : BASE OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET + { + $$ = new ElementAccess (new BaseThis (GetLocation ($1)), (Arguments) $3, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | BASE OPEN_BRACKET error + { + Error_SyntaxError (yyToken); + $$ = new ElementAccess (null, null, GetLocation ($2)); + } + ; + +post_increment_expression + : primary_expression OP_INC + { + $$ = new UnaryMutator (UnaryMutator.Mode.PostIncrement, (Expression) $1, GetLocation ($2)); + } + ; + +post_decrement_expression + : primary_expression OP_DEC + { + $$ = new UnaryMutator (UnaryMutator.Mode.PostDecrement, (Expression) $1, GetLocation ($2)); + } + ; + +object_or_delegate_creation_expression + : NEW new_expr_type open_parens_any opt_argument_list CLOSE_PARENS opt_object_or_collection_initializer + { + if ($6 != null) { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "object initializers"); + + $$ = new NewInitialize ((FullNamedExpression) $2, (Arguments) $4, (CollectionOrObjectInitializers) $6, GetLocation ($1)); + } else { + $$ = new New ((FullNamedExpression) $2, (Arguments) $4, GetLocation ($1)); + } + + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($5)); + } + | NEW new_expr_type object_or_collection_initializer + { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "collection initializers"); + + $$ = new NewInitialize ((FullNamedExpression) $2, null, (CollectionOrObjectInitializers) $3, GetLocation ($1)); + } + ; + +array_creation_expression + : NEW new_expr_type OPEN_BRACKET_EXPR expression_list CLOSE_BRACKET + opt_rank_specifier + opt_array_initializer + { + $$ = new ArrayCreation ((FullNamedExpression) $2, (List) $4, + new ComposedTypeSpecifier (((List) $4).Count, GetLocation ($3)) { + Next = (ComposedTypeSpecifier) $6 + }, (ArrayInitializer) $7, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($5)); + } + | NEW new_expr_type rank_specifiers opt_array_initializer + { + if ($4 == null) + report.Error (1586, GetLocation ($1), "Array creation must have array size or array initializer"); + + $$ = new ArrayCreation ((FullNamedExpression) $2, (ComposedTypeSpecifier) $3, (ArrayInitializer) $4, GetLocation ($1)); + } + | NEW rank_specifier array_initializer + { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "implicitly typed arrays"); + + $$ = new ImplicitlyTypedArrayCreation ((ComposedTypeSpecifier) $2, (ArrayInitializer) $3, GetLocation ($1)); + } + | NEW new_expr_type OPEN_BRACKET CLOSE_BRACKET OPEN_BRACKET_EXPR error CLOSE_BRACKET + { + report.Error (178, GetLocation ($6), "Invalid rank specifier, expecting `,' or `]'"); + $$ = new ArrayCreation ((FullNamedExpression) $2, null, GetLocation ($1)); + } + | NEW new_expr_type error + { + Error_SyntaxError (yyToken); + // It can be any of new expression, create the most common one + $$ = new New ((FullNamedExpression) $2, null, GetLocation ($1)); + } + ; + +new_expr_type + : { + ++lexer.parsing_type; + } + simple_type + { + --lexer.parsing_type; + $$ = $2; + } + ; + +anonymous_type_expression + : NEW OPEN_BRACE anonymous_type_parameters_opt_comma CLOSE_BRACE + { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "anonymous types"); + + $$ = new NewAnonymousType ((List) $3, current_container, GetLocation ($1)); + + // TODO: lbag comma location + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | NEW OPEN_BRACE GENERATE_COMPLETION + { + $$ = new EmptyCompletion (); + } + ; + +anonymous_type_parameters_opt_comma + : anonymous_type_parameters_opt + | anonymous_type_parameters COMMA + ; + +anonymous_type_parameters_opt + : { $$ = null; } + | anonymous_type_parameters + ; + +anonymous_type_parameters + : anonymous_type_parameter + { + var a = new List (4); + a.Add ((AnonymousTypeParameter) $1); + $$ = a; + } + | anonymous_type_parameters COMMA anonymous_type_parameter + { + var a = (List) $1; + a.Add ((AnonymousTypeParameter) $3); + lbag.AddLocation (a, GetLocation ($2)); + + $$ = a; + } + | COMPLETE_COMPLETION + { + $$ = new EmptyCompletion (); + } + | anonymous_type_parameter COMPLETE_COMPLETION + { + $$ = $1; + } + ; + +anonymous_type_parameter + : identifier_inside_body ASSIGN variable_initializer + { + var lt = (LocatedToken)$1; + $$ = new AnonymousTypeParameter ((Expression)$3, lt.Value, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | identifier_inside_body + { + var lt = (LocatedToken)$1; + $$ = new AnonymousTypeParameter (new SimpleName (lt.Value, lt.Location), + lt.Value, lt.Location); + } + | member_access + { + MemberAccess ma = (MemberAccess) $1; + $$ = new AnonymousTypeParameter (ma, ma.Name, ma.Location); + } + | error + { + report.Error (746, lexer.Location, + "Invalid anonymous type member declarator. Anonymous type members must be a member assignment, simple name or member access expression"); + $$ = null; + } + ; + +opt_rank_specifier + : /* empty */ + | rank_specifiers + ; + +rank_specifiers + : rank_specifier + | rank_specifier rank_specifiers + { + ((ComposedTypeSpecifier) $1).Next = (ComposedTypeSpecifier) $2; + $$ = $1; + } + ; + +rank_specifier + : OPEN_BRACKET CLOSE_BRACKET + { + $$ = ComposedTypeSpecifier.CreateArrayDimension (1, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2)); + } + | OPEN_BRACKET dim_separators CLOSE_BRACKET + { + $$ = ComposedTypeSpecifier.CreateArrayDimension ((int)$2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + ; + +dim_separators + : COMMA + { + $$ = 2; + } + | dim_separators COMMA + { + $$ = ((int) $1) + 1; + } + ; + +opt_array_initializer + : /* empty */ + { + $$ = null; + } + | array_initializer + { + $$ = $1; + } + ; + +array_initializer + : OPEN_BRACE CLOSE_BRACE + { + var ai = new ArrayInitializer (0, GetLocation ($1)); + ai.VariableDeclaration = current_variable; + lbag.AddLocation (ai, GetLocation ($2)); + $$ = ai; + } + | OPEN_BRACE variable_initializer_list opt_comma CLOSE_BRACE + { + var ai = new ArrayInitializer ((List) $2, GetLocation ($1)); + ai.VariableDeclaration = current_variable; + if ($3 != null) { + lbag.AddLocation (ai, GetLocation ($3), GetLocation ($4)); + } else { + lbag.AddLocation (ai, GetLocation ($4)); + } + $$ = ai; + } + ; + +variable_initializer_list + : variable_initializer + { + var list = new List (4); + list.Add ((Expression) $1); + $$ = list; + } + | variable_initializer_list COMMA variable_initializer + { + var list = (List) $1; + list.Add ((Expression) $3); + lbag.AddLocation (list, GetLocation ($2)); + $$ = list; + } + ; + +typeof_expression + : TYPEOF open_parens_any typeof_type_expression CLOSE_PARENS + { + $$ = new TypeOf ((FullNamedExpression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + ; + +typeof_type_expression + : type_and_void + | error + { + Error_TypeExpected (lexer.Location); + $$ = null; + } + ; + +generic_dimension + : GENERIC_DIMENSION + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "generics"); + + $$ = $1; + } + ; + +qualified_alias_member + : IDENTIFIER DOUBLE_COLON + { + var lt = (LocatedToken) $1; + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (lt.Location, "namespace alias qualifier"); + savedLocation = GetLocation ($2); + $$ = lt; + } + ; + +sizeof_expression + : SIZEOF open_parens_any type CLOSE_PARENS + { + $$ = new SizeOf ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | SIZEOF open_parens_any type error + { + Error_SyntaxError (yyToken); + + $$ = new SizeOf ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +checked_expression + : CHECKED open_parens_any expression CLOSE_PARENS + { + $$ = new CheckedExpr ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | CHECKED error + { + Error_SyntaxError (yyToken); + + $$ = new CheckedExpr (null, GetLocation ($1)); + } + ; + +unchecked_expression + : UNCHECKED open_parens_any expression CLOSE_PARENS + { + $$ = new UnCheckedExpr ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | UNCHECKED error + { + Error_SyntaxError (yyToken); + + $$ = new UnCheckedExpr (null, GetLocation ($1)); + } + ; + +pointer_member_access + : primary_expression OP_PTR IDENTIFIER opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess (new Indirection ((Expression) $1, GetLocation ($2)), lt.Value, (TypeArguments) $4, lt.Location); + } + ; + +anonymous_method_expression + : DELEGATE opt_anonymous_method_signature + { + start_anonymous (false, (ParametersCompiled) $2, false, GetLocation ($1)); + } + block + { + $$ = end_anonymous ((ParametersBlock) $4); + if ((ParametersCompiled) $2 != ParametersCompiled.Undefined) { + lbag.AddLocation ($$, GetLocation ($1), PopLocation (), PopLocation ()); + } else { + lbag.AddLocation ($$, GetLocation ($1)); + } + } + | ASYNC DELEGATE opt_anonymous_method_signature + { + start_anonymous (false, (ParametersCompiled) $3, true, GetLocation ($1)); + } + block + { + $$ = end_anonymous ((ParametersBlock) $5); + + if ((ParametersCompiled) $3 != ParametersCompiled.Undefined) { + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2), PopLocation (), PopLocation ()); + } else { + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2)); + } + } + ; + +opt_anonymous_method_signature + : + { + $$ = ParametersCompiled.Undefined; + } + | anonymous_method_signature + ; + +anonymous_method_signature + : OPEN_PARENS + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + $$ = $3; + PushLocation (GetLocation ($3)); + PushLocation (GetLocation ($1)); + } + ; + +default_value_expression + : DEFAULT open_parens_any type CLOSE_PARENS + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "default value expression"); + + $$ = new DefaultValueExpression ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + ; + +unary_expression + : primary_expression + | BANG prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.LogicalNot, (Expression) $2, GetLocation ($1)); + } + | TILDE prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.OnesComplement, (Expression) $2, GetLocation ($1)); + } + | OPEN_PARENS_CAST type CLOSE_PARENS prefixed_unary_expression + { + $$ = new Cast ((FullNamedExpression) $2, (Expression) $4, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + | AWAIT prefixed_unary_expression + { + if (!async_block) { + if (current_anonymous_method is LambdaExpression) { + report.Error (4034, GetLocation ($1), + "The `await' operator can only be used when its containing lambda expression is marked with the `async' modifier"); + } else if (current_anonymous_method != null) { + report.Error (4035, GetLocation ($1), + "The `await' operator can only be used when its containing anonymous method is marked with the `async' modifier"); + } else if (interactive_async != null) { + current_block.Explicit.RegisterAsyncAwait (); + interactive_async = true; + } else { + report.Error (4033, GetLocation ($1), + "The `await' operator can only be used when its containing method is marked with the `async' modifier"); + } + } else { + current_block.Explicit.RegisterAsyncAwait (); + } + + $$ = new Await ((Expression) $2, GetLocation ($1)); + } + | BANG error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.LogicalNot, null, GetLocation ($1)); + } + | TILDE error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.OnesComplement, null, GetLocation ($1)); + } + | OPEN_PARENS_CAST type CLOSE_PARENS error + { + Error_SyntaxError (yyToken); + + $$ = new Cast ((FullNamedExpression) $2, null, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + | AWAIT error + { + Error_SyntaxError (yyToken); + + $$ = new Await (null, GetLocation ($1)); + } + ; + + // + // The idea to split this out is from Rhys' grammar + // to solve the problem with casts. + // +prefixed_unary_expression + : unary_expression + | PLUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryPlus, (Expression) $2, GetLocation ($1)); + } + | MINUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryNegation, (Expression) $2, GetLocation ($1)); + } + | OP_INC prefixed_unary_expression + { + $$ = new UnaryMutator (UnaryMutator.Mode.PreIncrement, (Expression) $2, GetLocation ($1)); + } + | OP_DEC prefixed_unary_expression + { + $$ = new UnaryMutator (UnaryMutator.Mode.PreDecrement, (Expression) $2, GetLocation ($1)); + } + | STAR prefixed_unary_expression + { + $$ = new Indirection ((Expression) $2, GetLocation ($1)); + } + | BITWISE_AND prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.AddressOf, (Expression) $2, GetLocation ($1)); + } + | PLUS error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.UnaryPlus, null, GetLocation ($1)); + } + | MINUS error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.UnaryNegation, null, GetLocation ($1)); + } + | OP_INC error + { + Error_SyntaxError (yyToken); + + $$ = new UnaryMutator (UnaryMutator.Mode.PreIncrement, null, GetLocation ($1)); + } + | OP_DEC error + { + Error_SyntaxError (yyToken); + + $$ = new UnaryMutator (UnaryMutator.Mode.PreDecrement, null, GetLocation ($1)); + } + | STAR error + { + Error_SyntaxError (yyToken); + + $$ = new Indirection (null, GetLocation ($1)); + } + | BITWISE_AND error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.AddressOf, null, GetLocation ($1)); + } + ; + +multiplicative_expression + : prefixed_unary_expression + | multiplicative_expression STAR prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Multiply, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression DIV prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Division, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression PERCENT prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Modulus, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression STAR error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Multiply, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression DIV error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Division, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression PERCENT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Modulus, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +additive_expression + : multiplicative_expression + | additive_expression PLUS multiplicative_expression + { + $$ = new Binary (Binary.Operator.Addition, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | additive_expression MINUS multiplicative_expression + { + $$ = new Binary (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | additive_expression PLUS error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Addition, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | additive_expression MINUS error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Subtraction, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | additive_expression AS type + { + $$ = new As ((Expression) $1, (Expression) $3, GetLocation ($2)); + } + | additive_expression IS is_match_expr opt_identifier + { + var is_expr = new Is ((Expression) $1, (Expression) $3, GetLocation ($2)); + if ($4 != null) { + if (lang_version != LanguageVersion.Experimental) + FeatureIsNotAvailable (GetLocation ($4), "type pattern matching"); + + var lt = (LocatedToken) $4; + is_expr.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (is_expr.Variable); + } + + $$ = is_expr; + } + | additive_expression AS error + { + Error_SyntaxError (yyToken); + + $$ = new As ((Expression) $1, null, GetLocation ($2)); + } + | additive_expression IS error + { + Error_SyntaxError (yyToken); + + $$ = new Is ((Expression) $1, null, GetLocation ($2)); + } + | AWAIT IS type + { + var lt = (LocatedToken) $1; + $$ = new Is (new SimpleName (lt.Value, lt.Location), (Expression) $3, GetLocation ($2)); + } + | AWAIT AS type + { + var lt = (LocatedToken) $1; + $$ = new As (new SimpleName (lt.Value, lt.Location), (Expression) $3, GetLocation ($2)); + } + ; + +is_match_expr + : match_type + | match_type rank_specifiers + { + if ($1 is VarExpr) + $1 = new SimpleName ("var", ((VarExpr) $1).Location); + + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + | literal + | PLUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryPlus, (Expression) $2, GetLocation ($1)); + } + | MINUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryNegation, (Expression) $2, GetLocation ($1)); + } + ; + +match_type + : primary_expression_or_type opt_nullable + { + Expression expr = (Expression) $1; + if ($2 == null) { + SimpleName sn = expr as SimpleName; + if (sn != null && sn.Name == "var") + $$ = new VarExpr (sn.Location); + else + $$ = $1; + } else if (expr is ATypeNameExpression) { + $$ = new ComposedCast ((ATypeNameExpression)expr, (ComposedTypeSpecifier) $2); + } else { + Error_ExpectingTypeName (expr); + $$ = null; + } + } + | primary_expression_or_type pointer_stars + { + ATypeNameExpression expr = $1 as ATypeNameExpression; + + if (expr != null) { + $$ = new ComposedCast (expr, (ComposedTypeSpecifier) $2); + } else { + Error_ExpectingTypeName ((Expression)$1); + $$ = expr; + } + } + | builtin_type_expression + | void_invalid + ; + +shift_expression + : additive_expression + | shift_expression OP_SHIFT_LEFT additive_expression + { + $$ = new Binary (Binary.Operator.LeftShift, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | shift_expression OP_SHIFT_RIGHT additive_expression + { + $$ = new Binary (Binary.Operator.RightShift, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | shift_expression OP_SHIFT_LEFT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LeftShift, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | shift_expression OP_SHIFT_RIGHT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.RightShift, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +relational_expression + : shift_expression + | relational_expression OP_LT shift_expression + { + $$ = new Binary (Binary.Operator.LessThan, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_GT shift_expression + { + $$ = new Binary (Binary.Operator.GreaterThan, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_LE shift_expression + { + $$ = new Binary (Binary.Operator.LessThanOrEqual, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_GE shift_expression + { + $$ = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_LT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LessThan, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_GT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.GreaterThan, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_LE error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LessThanOrEqual, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_GE error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +equality_expression + : relational_expression + | equality_expression OP_EQ relational_expression + { + $$ = new Binary (Binary.Operator.Equality, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | equality_expression OP_NE relational_expression + { + $$ = new Binary (Binary.Operator.Inequality, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | equality_expression OP_EQ error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Equality, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | equality_expression OP_NE error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Inequality, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +and_expression + : equality_expression + | and_expression BITWISE_AND equality_expression + { + $$ = new Binary (Binary.Operator.BitwiseAnd, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | and_expression BITWISE_AND error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.BitwiseAnd, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +exclusive_or_expression + : and_expression + | exclusive_or_expression CARRET and_expression + { + $$ = new Binary (Binary.Operator.ExclusiveOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | exclusive_or_expression CARRET error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.ExclusiveOr, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +inclusive_or_expression + : exclusive_or_expression + | inclusive_or_expression BITWISE_OR exclusive_or_expression + { + $$ = new Binary (Binary.Operator.BitwiseOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | inclusive_or_expression BITWISE_OR error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.BitwiseOr, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +conditional_and_expression + : inclusive_or_expression + | conditional_and_expression OP_AND inclusive_or_expression + { + $$ = new Binary (Binary.Operator.LogicalAnd, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | conditional_and_expression OP_AND error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LogicalAnd, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +conditional_or_expression + : conditional_and_expression + | conditional_or_expression OP_OR conditional_and_expression + { + $$ = new Binary (Binary.Operator.LogicalOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | conditional_or_expression OP_OR error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LogicalOr, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +null_coalescing_expression + : conditional_or_expression + | conditional_or_expression OP_COALESCING null_coalescing_expression + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($2), "null coalescing operator"); + + $$ = new Nullable.NullCoalescingOperator ((Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +conditional_expression + : null_coalescing_expression + | null_coalescing_expression INTERR expression COLON expression + { + $$ = new Conditional (new BooleanExpression ((Expression) $1), (Expression) $3, (Expression) $5, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | null_coalescing_expression INTERR expression error + { + Error_SyntaxError (yyToken); + + $$ = new Conditional (new BooleanExpression ((Expression) $1), (Expression) $3, null, GetLocation ($2)); + } + | null_coalescing_expression INTERR expression COLON error + { + Error_SyntaxError (yyToken); + + $$ = new Conditional (new BooleanExpression ((Expression) $1), (Expression) $3, null, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | null_coalescing_expression INTERR expression COLON CLOSE_BRACE + { + Error_SyntaxError (Token.CLOSE_BRACE); + + $$ = new Conditional (new BooleanExpression ((Expression) $1), (Expression) $3, null, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + lexer.putback ('}'); + } + ; + +assignment_expression + : prefixed_unary_expression ASSIGN expression + { + $$ = new SimpleAssign ((Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_MULT_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Multiply, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_DIV_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Division, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_MOD_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Modulus, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_ADD_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Addition, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_SUB_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_SHIFT_LEFT_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.LeftShift, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_SHIFT_RIGHT_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.RightShift, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_AND_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.BitwiseAnd, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_OR_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.BitwiseOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_XOR_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.ExclusiveOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +lambda_parameter_list + : lambda_parameter + { + var pars = new List (4); + pars.Add ((Parameter) $1); + parameterListCommas.Clear (); + $$ = pars; + } + | lambda_parameter_list COMMA lambda_parameter + { + var pars = (List) $1; + Parameter p = (Parameter)$3; + if (pars[0].GetType () != p.GetType ()) { + report.Error (748, p.Location, "All lambda parameters must be typed either explicitly or implicitly"); + } + + pars.Add (p); + parameterListCommas.Add (GetLocation ($2)); + + $$ = pars; + } + ; + +lambda_parameter + : parameter_modifier parameter_type identifier_inside_body + { + var lt = (LocatedToken) $3; + + $$ = new Parameter ((FullNamedExpression) $2, lt.Value, (Parameter.Modifier) $1, null, lt.Location); + } + | parameter_type identifier_inside_body + { + var lt = (LocatedToken) $2; + + $$ = new Parameter ((FullNamedExpression) $1, lt.Value, Parameter.Modifier.NONE, null, lt.Location); + } + | IDENTIFIER + { + var lt = (LocatedToken) $1; + $$ = new ImplicitLambdaParameter (lt.Value, lt.Location); + } + | AWAIT + { + var lt = (LocatedToken) Error_AwaitAsIdentifier ($1); + $$ = new ImplicitLambdaParameter (lt.Value, lt.Location); + } + ; + +opt_lambda_parameter_list + : /* empty */ { $$ = ParametersCompiled.EmptyReadOnlyParameters; } + | lambda_parameter_list { + var pars_list = (List) $1; + $$ = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation ($$, parameterListCommas); + } + ; + +lambda_expression_body + : { + start_block (Location.Null); + } + expression // All expressions must handle error or current block won't be restored and breaking ast completely + { + Block b = end_block (Location.Null); + b.IsCompilerGenerated = true; + b.AddStatement (new ContextualReturn ((Expression) $2)); + $$ = b; + } + | block + | error + { + // Handles only cases like foo = x.FirstOrDefault (l => ); + // where we must restore current_variable + Block b = end_block (Location.Null); + b.IsCompilerGenerated = true; + + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +expression_or_error + : expression + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +lambda_expression + : IDENTIFIER ARROW + { + var lt = (LocatedToken) $1; + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), false, lt.Location); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $4); + lbag.AddLocation ($$, GetLocation ($2)); + } + | AWAIT ARROW + { + var lt = (LocatedToken) Error_AwaitAsIdentifier ($1); + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), false, lt.Location); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $4); + lbag.AddLocation ($$, GetLocation ($2)); + } + | ASYNC identifier_inside_body ARROW + { + var lt = (LocatedToken) $2; + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), true, lt.Location); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $5); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3)); + } + | OPEN_PARENS_LAMBDA + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_lambda_parameter_list CLOSE_PARENS ARROW + { + valid_param_mod = 0; + start_anonymous (true, (ParametersCompiled) $3, false, GetLocation ($1)); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $7); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($4), GetLocation ($5)); + } + | ASYNC OPEN_PARENS_LAMBDA + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_lambda_parameter_list CLOSE_PARENS ARROW + { + valid_param_mod = 0; + start_anonymous (true, (ParametersCompiled) $4, true, GetLocation ($1)); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $8); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2), GetLocation ($5), GetLocation ($6)); + } + ; + +expression + : assignment_expression + | non_assignment_expression + ; + +non_assignment_expression + : conditional_expression + | lambda_expression + | query_expression + | ARGLIST + { + $$ = new ArglistAccess (GetLocation ($1)); + } + ; + +undocumented_expressions + : REFVALUE OPEN_PARENS non_assignment_expression COMMA type CLOSE_PARENS + { + $$ = new RefValueExpr ((Expression) $3, (FullNamedExpression) $5, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4), GetLocation ($6)); + } + | REFTYPE open_parens_any expression CLOSE_PARENS + { + $$ = new RefTypeExpr ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | MAKEREF open_parens_any expression CLOSE_PARENS + { + $$ = new MakeRefExpr ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + ; + +constant_expression + : expression + ; + +boolean_expression + : expression + { + $$ = new BooleanExpression ((Expression) $1); + } + ; + +opt_primary_parameters + : /* empty */ + { + $$ = null; + } + | primary_parameters + ; + +primary_parameters + : OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS + { + $$ = $2; + + // Cannot use opt_formal_parameter_list because it can be shared instance for empty parameters + lbag.AppendToMember (current_container, GetLocation ($1), GetLocation ($3)); + + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation ($1), "primary constructor"); + } + ; + +opt_primary_parameters_with_class_base + : /* empty */ + { + $$ = null; + } + | class_base + { + $$ = null; + } + | primary_parameters + { + $$ = $1; + } + | primary_parameters class_base + { + $$ = $1; + } + | primary_parameters class_base OPEN_PARENS + { + ++lexer.parsing_block; + current_type.PrimaryConstructorBaseArgumentsStart = GetLocation ($3); + } + opt_argument_list CLOSE_PARENS + { + lbag.AppendToMember (current_container, GetLocation ($6)); + current_type.PrimaryConstructorBaseArguments = (Arguments) $5; + --lexer.parsing_block; + + $$ = $1; + } + ; + +// +// 10 classes +// +class_declaration + : opt_attributes + opt_modifiers + opt_partial + CLASS + { + } + type_declaration_name + { + lexer.ConstraintsParsing = true; + + Class c = new Class (current_container, (MemberName) $6, (Modifiers) $2, (Attributes) $1); + if (((c.ModFlags & Modifiers.STATIC) != 0) && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (c.Location, "static classes"); + } + + push_current_container (c, $3); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($4)); + valid_param_mod = ParameterModifierType.PrimaryConstructor; + } + opt_primary_parameters_with_class_base + opt_type_parameter_constraints_clauses + { + valid_param_mod = 0; + lexer.ConstraintsParsing = false; + + if ($8 != null) + current_type.PrimaryConstructorParameters = (ParametersCompiled) $8; + + if ($9 != null) + current_container.SetConstraints ((List) $9); + + if (doc_support) { + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.parsing_modifiers = true; + } + OPEN_BRACE opt_class_member_declarations CLOSE_BRACE + { + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_semicolon + { + if ($15 == null) { + lbag.AppendToMember (current_container, GetLocation ($11), GetLocation ($13)); + } else { + lbag.AppendToMember (current_container, GetLocation ($11), GetLocation ($13), GetLocation ($15)); + } + $$ = pop_current_class (); + } + ; + +opt_partial + : /* empty */ + { $$ = null; } + | PARTIAL + { $$ = $1; StoreModifierLocation (Modifiers.PARTIAL, GetLocation ($1)); } // location + ; + +opt_modifiers + : /* empty */ + { + mod_locations = null; + $$ = ModifierNone; + lexer.parsing_modifiers = false; + } + | modifiers + { + lexer.parsing_modifiers = false; + } + ; + +modifiers + : modifier + | modifiers modifier + { + var m1 = (Modifiers) $1; + var m2 = (Modifiers) $2; + + if ((m1 & m2) != 0) { + report.Error (1004, lexer.Location - ModifiersExtensions.Name (m2).Length, + "Duplicate `{0}' modifier", ModifiersExtensions.Name (m2)); + } else if ((m2 & Modifiers.AccessibilityMask) != 0 && (m1 & Modifiers.AccessibilityMask) != 0 && + ((m2 | m1 & Modifiers.AccessibilityMask) != (Modifiers.PROTECTED | Modifiers.INTERNAL))) { + report.Error (107, lexer.Location - ModifiersExtensions.Name (m2).Length, + "More than one protection modifier specified"); + } + + $$ = m1 | m2; + } + ; + +modifier + : NEW + { + $$ = Modifiers.NEW; + StoreModifierLocation ($$, GetLocation ($1)); + + if (current_container.Kind == MemberKind.Namespace) + report.Error (1530, GetLocation ($1), "Keyword `new' is not allowed on namespace elements"); + } + | PUBLIC + { + $$ = Modifiers.PUBLIC; + StoreModifierLocation ($$, GetLocation ($1)); + } + | PROTECTED + { + $$ = Modifiers.PROTECTED; + StoreModifierLocation ($$, GetLocation ($1)); + } + | INTERNAL + { + $$ = Modifiers.INTERNAL; + StoreModifierLocation ($$, GetLocation ($1)); + } + | PRIVATE + { + $$ = Modifiers.PRIVATE; + StoreModifierLocation ($$, GetLocation ($1)); + } + | ABSTRACT + { + $$ = Modifiers.ABSTRACT; + StoreModifierLocation ($$, GetLocation ($1)); + } + | SEALED + { + $$ = Modifiers.SEALED; + StoreModifierLocation ($$, GetLocation ($1)); + } + | STATIC + { + $$ = Modifiers.STATIC; + StoreModifierLocation ($$, GetLocation ($1)); + } + | READONLY + { + $$ = Modifiers.READONLY; + StoreModifierLocation ($$, GetLocation ($1)); + } + | VIRTUAL + { + $$ = Modifiers.VIRTUAL; + StoreModifierLocation ($$, GetLocation ($1)); + } + | OVERRIDE + { + $$ = Modifiers.OVERRIDE; + StoreModifierLocation ($$, GetLocation ($1)); + } + | EXTERN + { + $$ = Modifiers.EXTERN; + StoreModifierLocation ($$, GetLocation ($1)); + } + | VOLATILE + { + $$ = Modifiers.VOLATILE; + StoreModifierLocation ($$, GetLocation ($1)); + } + | UNSAFE + { + $$ = Modifiers.UNSAFE; + StoreModifierLocation ($$, GetLocation ($1)); + if (!settings.Unsafe) + Error_UnsafeCodeNotAllowed (GetLocation ($1)); + } + | ASYNC + { + $$ = Modifiers.ASYNC; + StoreModifierLocation ($$, GetLocation ($1)); + } + ; + +opt_class_base + : /* empty */ + | class_base + ; + +class_base + : COLON type_list + { + current_type.SetBaseTypes ((List) $2); + lbag.AppendToMember (current_type, GetLocation ($1)); + } + | COLON type_list error + { + Error_SyntaxError (yyToken); + + current_type.SetBaseTypes ((List) $2); + } + ; + +opt_type_parameter_constraints_clauses + : /* empty */ + | type_parameter_constraints_clauses + { + $$ = $1; + } + ; + +type_parameter_constraints_clauses + : type_parameter_constraints_clause + { + var constraints = new List (1); + constraints.Add ((Constraints) $1); + $$ = constraints; + } + | type_parameter_constraints_clauses type_parameter_constraints_clause + { + var constraints = (List) $1; + Constraints new_constraint = (Constraints)$2; + + foreach (Constraints c in constraints) { + if (new_constraint.TypeParameter.Value == c.TypeParameter.Value) { + report.Error (409, new_constraint.Location, + "A constraint clause has already been specified for type parameter `{0}'", + new_constraint.TypeParameter.Value); + } + } + + constraints.Add (new_constraint); + $$ = constraints; + } + ; + +type_parameter_constraints_clause + : WHERE IDENTIFIER COLON type_parameter_constraints + { + var lt = (LocatedToken) $2; + $$ = new Constraints (new SimpleMemberName (lt.Value, lt.Location), (List) $4, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + | WHERE IDENTIFIER error + { + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) $2; + $$ = new Constraints (new SimpleMemberName (lt.Value, lt.Location), null, GetLocation ($1)); + } + ; + +type_parameter_constraints + : type_parameter_constraint + { + var constraints = new List (1); + constraints.Add ((FullNamedExpression) $1); + $$ = constraints; + } + | type_parameter_constraints COMMA type_parameter_constraint + { + var constraints = (List) $1; + var prev = constraints [constraints.Count - 1] as SpecialContraintExpr; + if (prev != null && (prev.Constraint & SpecialConstraint.Constructor) != 0) { + report.Error (401, GetLocation ($2), "The `new()' constraint must be the last constraint specified"); + } + + prev = $3 as SpecialContraintExpr; + if (prev != null) { + if ((prev.Constraint & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) { + report.Error (449, prev.Location, "The `class' or `struct' constraint must be the first constraint specified"); + } else { + prev = constraints [0] as SpecialContraintExpr; + if (prev != null && (prev.Constraint & SpecialConstraint.Struct) != 0) { + report.Error (451, GetLocation ($3), "The `new()' constraint cannot be used with the `struct' constraint"); + } + } + } + + constraints.Add ((FullNamedExpression) $3); + lbag.AddLocation (constraints, GetLocation ($2)); + $$ = constraints; + } + ; + +type_parameter_constraint + : type + { + if ($1 is ComposedCast) + report.Error (706, GetLocation ($1), "Invalid constraint type `{0}'", ((ComposedCast)$1).GetSignatureForError ()); + + $$ = $1; + } + | NEW OPEN_PARENS CLOSE_PARENS + { + $$ = new SpecialContraintExpr (SpecialConstraint.Constructor, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3)); + } + | CLASS + { + $$ = new SpecialContraintExpr (SpecialConstraint.Class, GetLocation ($1)); + } + | STRUCT + { + $$ = new SpecialContraintExpr (SpecialConstraint.Struct, GetLocation ($1)); + } + ; + +opt_type_parameter_variance + : /* empty */ + { + $$ = null; + } + | type_parameter_variance + { + if (lang_version <= LanguageVersion.V_3) + FeatureIsNotAvailable (lexer.Location, "generic type variance"); + + $$ = $1; + } + ; + +type_parameter_variance + : OUT + { + $$ = new VarianceDecl (Variance.Covariant, GetLocation ($1)); + savedLocation = GetLocation ($1); + } + | IN + { + $$ = new VarianceDecl (Variance.Contravariant, GetLocation ($1)); + savedLocation = GetLocation ($1); + } + ; + +// +// Statements (8.2) +// + +// +// A block is "contained" on the following places: +// method_body +// property_declaration as part of the accessor body (get/set) +// operator_declaration +// constructor_declaration +// destructor_declaration +// event_declaration as part of add_accessor_declaration or remove_accessor_declaration +// +block + : OPEN_BRACE + { + ++lexer.parsing_block; + start_block (GetLocation ($1)); + } + opt_statement_list block_end + { + $$ = $4; + } + ; + +block_end + : CLOSE_BRACE + { + --lexer.parsing_block; + $$ = end_block (GetLocation ($1)); + } + | COMPLETE_COMPLETION + { + --lexer.parsing_block; + $$ = end_block (lexer.Location); + } + ; + + +block_prepared + : OPEN_BRACE + { + ++lexer.parsing_block; + current_block.StartLocation = GetLocation ($1); + } + opt_statement_list CLOSE_BRACE + { + --lexer.parsing_block; + $$ = end_block (GetLocation ($4)); + } | CLOSE_BRACE + { + report.Error (1525, GetLocation ($1), "Unexpected symbol '}', expected '{'"); + lexer.putback ('}'); + $$ = end_block (GetLocation ($1)); + } + ; + +block_prepared_strict + : OPEN_BRACE + { + ++lexer.parsing_block; + current_block.StartLocation = GetLocation ($1); + } + opt_statement_list CLOSE_BRACE + { + --lexer.parsing_block; + $$ = end_block (GetLocation ($4)); + } + ; + +opt_statement_list + : /* empty */ + | statement_list + ; + +statement_list + : statement + | statement_list statement + ; + +statement + : block_variable_declaration + { + current_block.AddStatement ((Statement) $1); + } + | valid_declaration_statement + { + current_block.AddStatement ((Statement) $1); + } + | labeled_statement +// WORKAROUND:Remove that rule, if it is >really< fixed. + | IDENTIFIER error + { + Error_SyntaxError (yyToken); + var lt =(LocatedToken) $1; + var sn = new SimpleName (lt.Value, lt.Location); + current_block.AddStatement(new StatementErrorExpression (sn)); + $$ = null; + } +//////// + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +// +// The interactive_statement and its derivatives are only +// used to provide a special version of `expression_statement' +// that has a side effect of assigning the expression to +// $retval +// +interactive_statement_list + : interactive_statement + | interactive_statement_list interactive_statement + ; + +interactive_statement + : block_variable_declaration + { + current_block.AddStatement ((Statement) $1); + } + | interactive_valid_declaration_statement + { + current_block.AddStatement ((Statement) $1); + } + | labeled_statement + ; + +valid_declaration_statement + : block + | empty_statement + | expression_statement + | selection_statement + | iteration_statement + | jump_statement + | try_statement + | checked_statement + | unchecked_statement + | lock_statement + | using_statement + | unsafe_statement + | fixed_statement + ; + +interactive_valid_declaration_statement + : block + | empty_statement + | interactive_expression_statement + | selection_statement + | iteration_statement + | jump_statement + | try_statement + | checked_statement + | unchecked_statement + | lock_statement + | using_statement + | unsafe_statement + | fixed_statement + ; + +embedded_statement + : valid_declaration_statement + | block_variable_declaration + { + report.Error (1023, GetLocation ($1), "An embedded statement may not be a declaration or labeled statement"); + $$ = null; + } + | labeled_statement + { + report.Error (1023, GetLocation ($1), "An embedded statement may not be a declaration or labeled statement"); + $$ = null; + } + | error + { + Error_SyntaxError (yyToken); + $$ = new EmptyStatement (GetLocation ($1)); + } + ; + +empty_statement + : SEMICOLON + { + // Uses lexer.Location because semicolon location is not kept in quick mode + $$ = new EmptyStatement (lexer.Location); + } + ; + +labeled_statement + : identifier_inside_body COLON + { + var lt = (LocatedToken) $1; + LabeledStatement labeled = new LabeledStatement (lt.Value, current_block, lt.Location); + lbag.AddLocation (labeled, GetLocation ($2)); + current_block.AddLabel (labeled); + current_block.AddStatement (labeled); + } + statement + ; + +variable_type + : variable_type_simple + | variable_type_simple rank_specifiers + { + if ($1 is VarExpr) + $1 = new SimpleName ("var", ((VarExpr) $1).Location); + + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + ; + +/* + * The following is from Rhys' grammar: + * > Types in local variable declarations must be recognized as + * > expressions to prevent reduce/reduce errors in the grammar. + * > The expressions are converted into types during semantic analysis. + */ +variable_type_simple + : primary_expression_or_type opt_nullable + { + // Ok, the above "primary_expression" is there to get rid of + // both reduce/reduce and shift/reduces in the grammar, it should + // really just be "type_name". If you use type_name, a reduce/reduce + // creeps up. If you use namespace_or_type_name (which is all we need + // really) two shift/reduces appear. + // + + // So the super-trick is that primary_expression + // can only be either a SimpleName or a MemberAccess. + // The MemberAccess case arises when you have a fully qualified type-name like : + // Foo.Bar.Blah i; + // SimpleName is when you have + // Blah i; + + Expression expr = (Expression) $1; + if ($2 == null) { + SimpleName sn = expr as SimpleName; + if (sn != null && sn.Name == "var") + $$ = new VarExpr (sn.Location); + else + $$ = $1; + } else if (expr is ATypeNameExpression) { + $$ = new ComposedCast ((ATypeNameExpression)expr, (ComposedTypeSpecifier) $2); + } else { + Error_ExpectingTypeName (expr); + $$ = null; + } + } + | primary_expression_or_type pointer_stars + { + ATypeNameExpression expr = $1 as ATypeNameExpression; + + if (expr != null) { + $$ = new ComposedCast (expr, (ComposedTypeSpecifier) $2); + } else { + Error_ExpectingTypeName ((Expression)$1); + $$ = expr; + } + } + | builtin_type_expression + | void_invalid + ; + +pointer_stars + : pointer_star + | pointer_star pointer_stars + { + ((ComposedTypeSpecifier) $1).Next = (ComposedTypeSpecifier) $2; + $$ = $1; + } + ; + +pointer_star + : STAR + { + $$ = ComposedTypeSpecifier.CreatePointer (GetLocation ($1)); + } + ; + +identifier_inside_body + : IDENTIFIER + | AWAIT + { + $$ = Error_AwaitAsIdentifier ($1); + } + ; + +block_variable_declaration + : variable_type identifier_inside_body + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) $1, li); + } + opt_local_variable_initializer opt_variable_declarators semicolon_or_handle_error_close_brace + { + $$ = current_variable; + current_variable = null; + if ($4 != null) + lbag.AddLocation ($$, PopLocation (), GetLocation ($6)); + else + lbag.AddLocation ($$, GetLocation ($6)); + } + | CONST variable_type identifier_inside_body + { + var lt = (LocatedToken) $3; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.Constant, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockConstant ((FullNamedExpression) $2, li); + } + const_variable_initializer opt_const_declarators SEMICOLON + { + if (current_variable.Initializer != null) { + lbag.AddLocation (current_variable, GetLocation ($1), savedLocation, GetLocation ($7)); + } else { + lbag.AddLocation (current_variable, GetLocation ($1), GetLocation ($7)); + } + $$ = current_variable;; + current_variable = null; + } + ; + +semicolon_or_handle_error_close_brace + : SEMICOLON + | CLOSE_BRACE { + // Redundant, but wont regress + report.Error (1525, lexer.Location, "Unexpected symbol }"); + lexer.putback ('}'); + $$ = $1; + } + ; + +opt_local_variable_initializer + : /* empty */ + | ASSIGN block_variable_initializer + { + current_variable.Initializer = (Expression) $2; + PushLocation (GetLocation ($1)); + $$ = current_variable; + } + | error + { + if (yyToken == Token.OPEN_BRACKET_EXPR) { + report.Error (650, lexer.Location, + "Syntax error, bad array declarator. To declare a managed array the rank specifier precedes the variable's identifier. To declare a fixed size buffer field, use the fixed keyword before the field type"); + } else { + Error_SyntaxError (yyToken); + } + } + ; + +opt_variable_declarators + : /* empty */ + | variable_declarators + ; + +opt_using_or_fixed_variable_declarators + : /* empty */ + | variable_declarators + { + foreach (var d in current_variable.Declarators) { + if (d.Initializer == null) + Error_MissingInitializer (d.Variable.Location); + } + } + ; + +variable_declarators + : variable_declarator + | variable_declarators variable_declarator + ; + +variable_declarator + : COMMA identifier_inside_body + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_variable.Variable, lt.Value, lt.Location); + var d = new BlockVariableDeclarator (li, null); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation ($1)); + } + | COMMA identifier_inside_body ASSIGN block_variable_initializer + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_variable.Variable, lt.Value, lt.Location); + var d = new BlockVariableDeclarator (li, (Expression) $4); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation ($1), GetLocation ($3)); + } + ; + +const_variable_initializer + : /* empty */ + { + report.Error (145, lexer.Location, "A const field requires a value to be provided"); + } + | ASSIGN constant_initializer_expr + { + savedLocation = GetLocation ($1); + current_variable.Initializer = (Expression) $2; + } + ; + +opt_const_declarators + : /* empty */ + | const_declarators + ; + +const_declarators + : const_declarator + | const_declarators const_declarator + ; + +const_declarator + : COMMA identifier_inside_body ASSIGN constant_initializer_expr + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.Constant, lt.Location); + var d = new BlockVariableDeclarator (li, (Expression) $4); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation ($1), GetLocation ($3)); + } + ; + +block_variable_initializer + : variable_initializer + | STACKALLOC simple_type OPEN_BRACKET_EXPR expression CLOSE_BRACKET + { + $$ = new StackAlloc ((Expression) $2, (Expression) $4, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($5)); + } + | STACKALLOC simple_type + { + report.Error (1575, GetLocation ($1), "A stackalloc expression requires [] after type"); + $$ = new StackAlloc ((Expression) $2, null, GetLocation ($1)); + } + ; + +expression_statement + : statement_expression SEMICOLON + { + $$ = $1; + lbag.AddStatement ($$, GetLocation ($2)); + } + | statement_expression COMPLETE_COMPLETION { $$ = $1; } + | statement_expression CLOSE_BRACE + { + $$ = $1; + report.Error (1002, GetLocation ($2), "; expected"); + lexer.putback ('}'); + } + ; + +interactive_expression_statement + : interactive_statement_expression SEMICOLON { $$ = $1; } + | interactive_statement_expression COMPLETE_COMPLETION { $$ = $1; } + ; + + // + // We have to do the wrapping here and not in the case above, + // because statement_expression is used for example in for_statement + // +statement_expression + : expression + { + ExpressionStatement s = $1 as ExpressionStatement; + if (s == null) { + var expr = $1 as Expression; + $$ = new StatementErrorExpression (expr); + } else { + $$ = new StatementExpression (s); + } + } + ; + +interactive_statement_expression + : expression + { + Expression expr = (Expression) $1; + $$ = new StatementExpression (new OptionalAssign (expr, lexer.Location)); + } + | error + { + Error_SyntaxError (yyToken); + $$ = new EmptyStatement (GetLocation ($1)); + } + ; + +selection_statement + : if_statement + | switch_statement + ; + +if_statement + : IF open_parens_any boolean_expression CLOSE_PARENS + embedded_statement + { + if ($5 is EmptyStatement) + Warning_EmptyStatement (GetLocation ($5)); + + $$ = new If ((BooleanExpression) $3, (Statement) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | IF open_parens_any boolean_expression CLOSE_PARENS + embedded_statement ELSE embedded_statement + { + $$ = new If ((BooleanExpression) $3, (Statement) $5, (Statement) $7, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4), GetLocation ($6)); + + if ($5 is EmptyStatement) + Warning_EmptyStatement (GetLocation ($5)); + if ($7 is EmptyStatement) + Warning_EmptyStatement (GetLocation ($7)); + } + | IF open_parens_any boolean_expression error + { + Error_SyntaxError (yyToken); + + $$ = new If ((BooleanExpression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +switch_statement + : SWITCH open_parens_any expression CLOSE_PARENS OPEN_BRACE + { + start_block (GetLocation ($5)); + } + opt_switch_sections CLOSE_BRACE + { + $$ = new Switch ((Expression) $3, (ExplicitBlock) current_block.Explicit, GetLocation ($1)); + end_block (GetLocation ($8)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4), GetLocation ($5), GetLocation ($8)); + } + | SWITCH open_parens_any expression error + { + Error_SyntaxError (yyToken); + + $$ = new Switch ((Expression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +opt_switch_sections + : /* empty */ + { + report.Warning (1522, 1, current_block.StartLocation, "Empty switch block"); + } + | switch_sections + ; + +switch_sections + : switch_section + | switch_sections switch_section + | error + { + Error_SyntaxError (yyToken); + } + ; + +switch_section + : switch_labels statement_list + ; + +switch_labels + : switch_label + { + var label = (SwitchLabel) $1; + label.SectionStart = true; + current_block.AddStatement (label); + } + | switch_labels switch_label + { + current_block.AddStatement ((Statement) $2); + } + ; + +switch_label + : CASE constant_expression COLON + { + $$ = new SwitchLabel ((Expression) $2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + | CASE constant_expression error + { + Error_SyntaxError (yyToken); + $$ = new SwitchLabel ((Expression) $2, GetLocation ($1)); + } + | DEFAULT_COLON + { + $$ = new SwitchLabel (null, GetLocation ($1)); + } + ; + +iteration_statement + : while_statement + | do_statement + | for_statement + | foreach_statement + ; + +while_statement + : WHILE open_parens_any boolean_expression CLOSE_PARENS embedded_statement + { + if ($5 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($5)); + + $$ = new While ((BooleanExpression) $3, (Statement) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | WHILE open_parens_any boolean_expression error + { + Error_SyntaxError (yyToken); + + $$ = new While ((BooleanExpression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +do_statement + : DO embedded_statement WHILE open_parens_any boolean_expression CLOSE_PARENS SEMICOLON + { + $$ = new Do ((Statement) $2, (BooleanExpression) $5, GetLocation ($1), GetLocation ($3)); + lbag.AddStatement ($$, GetLocation ($3), GetLocation ($4), GetLocation ($6), GetLocation ($7)); + } + | DO embedded_statement error + { + Error_SyntaxError (yyToken); + $$ = new Do ((Statement) $2, null, GetLocation ($1), Location.Null); + } + | DO embedded_statement WHILE open_parens_any boolean_expression error + { + Error_SyntaxError (yyToken); + + $$ = new Do ((Statement) $2, (BooleanExpression) $5, GetLocation ($1), GetLocation ($3)); + lbag.AddStatement ($$, GetLocation ($3), GetLocation ($4)); + } + ; + +for_statement + : FOR open_parens_any + { + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + For f = new For (GetLocation ($1)); + current_block.AddStatement (f); + lbag.AddStatement (f, current_block.StartLocation); + $$ = f; + } + for_statement_cont + { + $$ = $4; + } + ; + +// Has to use be extra rule to recover started block +for_statement_cont + : opt_for_initializer SEMICOLON + { + For f = (For) $0; + f.Initializer = (Statement) $1; + lbag.AddLocation (f, GetLocation ($2)); + $$ = f; + } + for_statement_condition + { + $$ = $4; + } + | opt_for_initializer CLOSE_PARENS { + report.Error (1525, GetLocation ($2), "Unexpected symbol ')', expected ';'"); + For f = (For) $0; + f.Initializer = (Statement) $1; + lbag.AddLocation (f, GetLocation ($2)); + $$ = end_block (GetLocation ($2)); + } + ; + +for_statement_condition + : opt_for_condition SEMICOLON + { + For f = (For) $0; + f.Condition = (BooleanExpression) $1; + lbag.AddLocation (f, GetLocation ($2)); + $$ = f; + } + for_statement_end + { + $$ = $4; + } + + | boolean_expression CLOSE_PARENS { + report.Error (1525, GetLocation ($2), "Unexpected symbol ')', expected ';'"); + For f = (For) $0; + f.Condition = (BooleanExpression) $1; + lbag.AddLocation (f, GetLocation ($2)); + $$ = end_block (GetLocation ($2)); + } + ; + +for_statement_end + : opt_for_iterator CLOSE_PARENS + embedded_statement + { + For f = (For) $0; + f.Iterator = (Statement) $1; + + if ($3 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($3)); + + f.Statement = (Statement) $3; + lbag.AddLocation (f, GetLocation ($2)); + + $$ = end_block (GetLocation ($2)); + } + | error + { + Error_SyntaxError (yyToken); + $$ = end_block (current_block.StartLocation); + } + ; + +opt_for_initializer + : /* empty */ { $$ = new EmptyStatement (lexer.Location); } + | for_initializer + ; + +for_initializer + : variable_type identifier_inside_body + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) $1, li); + } + opt_local_variable_initializer opt_variable_declarators + { + $$ = current_variable; + if ($4 != null) + lbag.AddLocation (current_variable, PopLocation ()); + + current_variable = null; + } + | statement_expression_list + ; + +opt_for_condition + : /* empty */ { $$ = null; } + | boolean_expression + ; + +opt_for_iterator + : /* empty */ { $$ = new EmptyStatement (lexer.Location); } + | for_iterator + ; + +for_iterator + : statement_expression_list + ; + +statement_expression_list + : statement_expression + | statement_expression_list COMMA statement_expression + { + var sl = $1 as StatementList; + if (sl == null) { + sl = new StatementList ((Statement) $1, (Statement) $3); + lbag.AddStatement (sl, GetLocation ($2)); + } else { + sl.Add ((Statement) $3); + lbag.AddLocation (sl, GetLocation ($2)); + + } + + $$ = sl; + } + ; + +foreach_statement + : FOREACH open_parens_any type error + { + report.Error (230, GetLocation ($1), "Type and identifier are both required in a foreach statement"); + + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + + Foreach f = new Foreach ((Expression) $3, null, null, null, null, GetLocation ($1)); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation ($2)); + $$ = end_block (GetLocation ($4)); + } + | FOREACH open_parens_any type identifier_inside_body error + { + Error_SyntaxError (yyToken); + + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + + Foreach f = new Foreach ((Expression) $3, li, null, null, null, GetLocation ($1)); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation ($2)); + $$ = end_block (GetLocation ($5)); + } + | FOREACH open_parens_any type identifier_inside_body IN expression CLOSE_PARENS + { + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + $$ = li; + } + embedded_statement + { + if ($9 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($9)); + + Foreach f = new Foreach ((Expression) $3, (LocalVariable) $8, (Expression) $6, (Statement) $9, current_block, GetLocation ($1)); + lbag.AddStatement (f, GetLocation ($2), GetLocation ($5), GetLocation ($7)); + end_block (GetLocation ($7)); + + $$ = f; + } + | FOREACH open_parens_any type identifier_inside_body error + { + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + var lt = $4 as LocatedToken; + var li = lt != null ? new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location) : null; + + Foreach f = new Foreach ((Expression) $3, li, null, null, null, GetLocation ($1)); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation ($2)); + $$ = end_block (GetLocation ($5)); + } + | FOREACH open_parens_any type error + { + Foreach f = new Foreach ((Expression) $3, null, null, null, null, GetLocation ($1)); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation ($2)); + $$ = f; + } + ; + +jump_statement + : break_statement + | continue_statement + | goto_statement + | return_statement + | throw_statement + | yield_statement + ; + +break_statement + : BREAK SEMICOLON + { + $$ = new Break (GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +continue_statement + : CONTINUE SEMICOLON + { + $$ = new Continue (GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + | CONTINUE error + { + Error_SyntaxError (yyToken); + $$ = new Continue (GetLocation ($1)); + } + ; + +goto_statement + : GOTO identifier_inside_body SEMICOLON + { + var lt = (LocatedToken) $2; + $$ = new Goto (lt.Value, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($3)); + } + | GOTO CASE constant_expression SEMICOLON + { + $$ = new GotoCase ((Expression) $3, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | GOTO DEFAULT SEMICOLON + { + $$ = new GotoDefault (GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($3)); + } + ; + +return_statement + : RETURN opt_expression SEMICOLON + { + $$ = new Return ((Expression) $2, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($3)); + } + | RETURN expression error + { + Error_SyntaxError (yyToken); + $$ = new Return ((Expression) $2, GetLocation ($1)); + } + | RETURN error + { + Error_SyntaxError (yyToken); + $$ = new Return (null, GetLocation ($1)); + } + ; + +throw_statement + : THROW opt_expression SEMICOLON + { + $$ = new Throw ((Expression) $2, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($3)); + } + | THROW expression error + { + Error_SyntaxError (yyToken); + $$ = new Throw ((Expression) $2, GetLocation ($1)); + } + | THROW error + { + Error_SyntaxError (yyToken); + $$ = new Throw (null, GetLocation ($1)); + } + ; + +yield_statement + : identifier_inside_body RETURN opt_expression SEMICOLON + { + var lt = (LocatedToken) $1; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if ($3 == null) { + report.Error (1627, GetLocation ($4), "Expression expected after yield return"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.Explicit.RegisterIteratorYield (); + $$ = new Yield ((Expression) $3, lt.Location); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | identifier_inside_body RETURN expression error + { + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) $1; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if ($3 == null) { + report.Error (1627, GetLocation ($4), "Expression expected after yield return"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.Explicit.RegisterIteratorYield (); + $$ = new Yield ((Expression) $3, lt.Location); + lbag.AddStatement ($$, GetLocation ($2)); + } + | identifier_inside_body BREAK SEMICOLON + { + var lt = (LocatedToken) $1; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.ParametersBlock.TopBlock.IsIterator = true; + $$ = new YieldBreak (lt.Location); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($3)); + } + ; + +opt_expression + : /* empty */ + | expression + ; + +try_statement + : TRY block catch_clauses + { + $$ = new TryCatch ((Block) $2, (List) $3, GetLocation ($1), false); + } + | TRY block FINALLY block + { + $$ = new TryFinally ((Statement) $2, (ExplicitBlock) $4, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($3)); + } + | TRY block catch_clauses FINALLY block + { + $$ = new TryFinally (new TryCatch ((Block) $2, (List) $3, GetLocation ($1), true), (ExplicitBlock) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($4)); + } + | TRY block error + { + Error_SyntaxError (1524, yyToken); + $$ = new TryCatch ((Block) $2, null, GetLocation ($1), false); + } + ; + +catch_clauses + : catch_clause + { + var l = new List (2); + + l.Add ((Catch) $1); + $$ = l; + } + | catch_clauses catch_clause + { + var l = (List) $1; + + Catch c = (Catch) $2; + var prev_catch = l [l.Count - 1]; + if (prev_catch.IsGeneral && prev_catch.Filter == null) { + report.Error (1017, c.loc, "Try statement already has an empty catch block"); + } + + l.Add (c); + $$ = l; + } + ; + +opt_identifier + : /* empty */ + | identifier_inside_body + ; + +catch_clause + : CATCH opt_catch_filter block + { + var c = new Catch ((ExplicitBlock) $3, GetLocation ($1)); + c.Filter = (CatchFilterExpression) $2; + $$ = c; + } + | CATCH open_parens_any type opt_identifier CLOSE_PARENS + { + start_block (GetLocation ($2)); + var c = new Catch ((ExplicitBlock) current_block, GetLocation ($1)); + c.TypeExpression = (FullNamedExpression) $3; + + if ($4 != null) { + var lt = (LocatedToken) $4; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (c.Variable); + } + + lbag.AddLocation (c, GetLocation ($2), GetLocation ($5)); + $$ = c; + } + opt_catch_filter block_prepared + { + ((Catch) $6).Filter = (CatchFilterExpression) $7; + $$ = $6; + } + | CATCH open_parens_any error + { + if (yyToken == Token.CLOSE_PARENS) { + report.Error (1015, lexer.Location, + "A type that derives from `System.Exception', `object', or `string' expected"); + } else { + Error_SyntaxError (yyToken); + } + + $$ = new Catch (null, GetLocation ($1)); + } + | CATCH open_parens_any type opt_identifier CLOSE_PARENS error + { + Error_SyntaxError (yyToken); + + // Required otherwise missing block could not be detected because + // start_block is run early + var c = new Catch (null, GetLocation ($1)); + c.TypeExpression = (FullNamedExpression) $3; + + if ($4 != null) { + var lt = (LocatedToken) $4; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + } + + if ($4 != null) { + var lt = (LocatedToken) $4; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + } + + lbag.AddLocation (c, GetLocation ($2), GetLocation ($5)); + + $$ = c; + } + ; + +opt_catch_filter + : /* empty */ + | IF open_parens_any expression CLOSE_PARENS + { + if (lang_version <= LanguageVersion.V_5) + FeatureIsNotAvailable (GetLocation ($1), "exception filter"); + + $$ = new CatchFilterExpression ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + ; + +checked_statement + : CHECKED block + { + $$ = new Checked ((Block) $2, GetLocation ($1)); + } + ; + +unchecked_statement + : UNCHECKED block + { + $$ = new Unchecked ((Block) $2, GetLocation ($1)); + } + ; + +unsafe_statement + : UNSAFE + { + if (!settings.Unsafe) + Error_UnsafeCodeNotAllowed (GetLocation ($1)); + } block { + $$ = new Unsafe ((Block) $3, GetLocation ($1)); + } + ; + +lock_statement + : LOCK open_parens_any expression CLOSE_PARENS embedded_statement + { + if ($5 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($5)); + + $$ = new Lock ((Expression) $3, (Statement) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | LOCK open_parens_any expression error + { + Error_SyntaxError (yyToken); + + $$ = new Lock ((Expression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +fixed_statement + : FIXED open_parens_any variable_type identifier_inside_body + { + start_block (GetLocation ($2)); + + current_block.IsCompilerGenerated = true; + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.FixedVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + current_variable = new Fixed.VariableDeclaration ((FullNamedExpression) $3, li); + } + using_or_fixed_variable_initializer opt_using_or_fixed_variable_declarators CLOSE_PARENS + { + $$ = current_variable; + current_variable = null; + } + embedded_statement + { + if ($10 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($10)); + + Fixed f = new Fixed ((Fixed.VariableDeclaration) $9, (Statement) $10, GetLocation ($1)); + current_block.AddStatement (f); + lbag.AddStatement (f, GetLocation ($2), GetLocation ($8)); + $$ = end_block (GetLocation ($8)); + } + ; + +using_statement + : USING open_parens_any variable_type identifier_inside_body + { + start_block (GetLocation ($2)); + + current_block.IsCompilerGenerated = true; + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.UsingVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + current_variable = new Using.VariableDeclaration ((FullNamedExpression) $3, li); + } + using_initialization CLOSE_PARENS + { + $$ = current_variable; + current_variable = null; + } + embedded_statement + { + if ($9 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($9)); + + Using u = new Using ((Using.VariableDeclaration) $8, (Statement) $9, GetLocation ($1)); + lbag.AddStatement (u, GetLocation ($2), GetLocation ($7)); + current_block.AddStatement (u); + $$ = end_block (GetLocation ($7)); + } + | USING open_parens_any expression CLOSE_PARENS embedded_statement + { + if ($5 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($5)); + + $$ = new Using ((Expression) $3, (Statement) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | USING open_parens_any expression error + { + Error_SyntaxError (yyToken); + + $$ = new Using ((Expression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +using_initialization + : using_or_fixed_variable_initializer opt_using_or_fixed_variable_declarators + | error + { + // It has to be here for the parent to safely restore artificial block + Error_SyntaxError (yyToken); + } + ; + +using_or_fixed_variable_initializer + : /* empty */ + { + Error_MissingInitializer (lexer.Location); + } + | ASSIGN variable_initializer + { + current_variable.Initializer = (Expression) $2; + lbag.AddLocation (current_variable, GetLocation ($1)); + $$ = current_variable; + } + ; + + +// LINQ + +query_expression + : first_from_clause query_body + { + lexer.query_parsing = false; + + Linq.AQueryClause from = $1 as Linq.AQueryClause; + + from.Tail.Next = (Linq.AQueryClause)$2; + $$ = from; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + | nested_from_clause query_body + { + Linq.AQueryClause from = $1 as Linq.AQueryClause; + + from.Tail.Next = (Linq.AQueryClause)$2; + $$ = from; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + + // Bubble up COMPLETE_COMPLETION productions + | first_from_clause COMPLETE_COMPLETION { + lexer.query_parsing = false; + $$ = $1; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + | nested_from_clause COMPLETE_COMPLETION { + $$ = $1; + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + ; + +first_from_clause + : FROM_FIRST identifier_inside_body IN expression + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) $2; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)$4, rv, GetLocation ($1)); + lbag.AddLocation (clause, GetLocation ($3)); + $$ = new Linq.QueryExpression (clause); + } + | FROM_FIRST type identifier_inside_body IN expression + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) $3; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)$5, rv, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + lbag.AddLocation (clause, GetLocation ($4)); + $$ = new Linq.QueryExpression (clause); + } + ; + +nested_from_clause + : FROM identifier_inside_body IN expression + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) $2; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)$4, rv, GetLocation ($1)); + lbag.AddLocation (clause, GetLocation ($3)); + $$ = new Linq.QueryExpression (clause); + } + | FROM type identifier_inside_body IN expression + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) $3; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)$5, rv, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + lbag.AddLocation (clause, GetLocation ($4)); + $$ = new Linq.QueryExpression (clause); + } + ; + +from_clause + : FROM identifier_inside_body IN + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + var lt = (LocatedToken) $2; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + $$ = new Linq.SelectMany ((Linq.QueryBlock)current_block, sn, (Expression)$5, GetLocation ($1)); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + lbag.AddLocation ($$, GetLocation ($3)); + } + | FROM type identifier_inside_body IN + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + var lt = (LocatedToken) $3; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + + $$ = new Linq.SelectMany ((Linq.QueryBlock)current_block, sn, (Expression)$6, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lbag.AddLocation ($$, GetLocation ($4)); + } + ; + +query_body + : query_body_clauses select_or_group_clause opt_query_continuation + { + Linq.AQueryClause head = (Linq.AQueryClause)$2; + + if ($3 != null) + head.Next = (Linq.AQueryClause)$3; + + if ($1 != null) { + Linq.AQueryClause clause = (Linq.AQueryClause)$1; + clause.Tail.Next = head; + head = clause; + } + + $$ = head; + } + | select_or_group_clause opt_query_continuation + { + Linq.AQueryClause head = (Linq.AQueryClause)$2; + + if ($1 != null) { + Linq.AQueryClause clause = (Linq.AQueryClause)$1; + clause.Tail.Next = head; + head = clause; + } + + $$ = head; + } + | query_body_clauses COMPLETE_COMPLETION + | query_body_clauses error + { + report.Error (742, GetLocation ($2), "Unexpected symbol `{0}'. A query body must end with select or group clause", GetSymbolName (yyToken)); + $$ = $1; + } + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +select_or_group_clause + : SELECT + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + $$ = new Linq.Select ((Linq.QueryBlock)current_block, (Expression)$3, GetLocation ($1)); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + | GROUP + { + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock)current_block); + } + expression_or_error + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + by_expression + { + var obj = (object[]) $5; + + $$ = new Linq.GroupBy ((Linq.QueryBlock)current_block, (Expression)$3, linq_clause_blocks.Pop (), (Expression)obj[0], GetLocation ($1)); + lbag.AddLocation ($$, (Location) obj[1]); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + ; + +by_expression + : BY expression_or_error + { + $$ = new object[] { $2, GetLocation ($1) }; + } + | error + { + Error_SyntaxError (yyToken); + $$ = new object[2] { null, Location.Null }; + } + ; + +query_body_clauses + : query_body_clause + | query_body_clauses query_body_clause + { + ((Linq.AQueryClause)$1).Tail.Next = (Linq.AQueryClause)$2; + $$ = $1; + } + ; + +query_body_clause + : from_clause + | let_clause + | where_clause + | join_clause + | orderby_clause + ; + +let_clause + : LET identifier_inside_body ASSIGN + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + var lt = (LocatedToken) $2; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + $$ = new Linq.Let ((Linq.QueryBlock) current_block, sn, (Expression)$5, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + } + ; + +where_clause + : WHERE + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + $$ = new Linq.Where ((Linq.QueryBlock)current_block, (Expression)$3, GetLocation ($1)); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + ; + +join_clause + : JOIN identifier_inside_body IN + { + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + expression_or_error ON + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + expression_or_error EQUALS + { + current_block.AddStatement (new ContextualReturn ((Expression) $8)); + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error opt_join_into + { + current_block.AddStatement (new ContextualReturn ((Expression) $11)); + current_block.SetEndLocation (lexer.Location); + + var outer_selector = linq_clause_blocks.Pop (); + var block = linq_clause_blocks.Pop (); + + var lt = (LocatedToken) $2; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + Linq.RangeVariable into; + + if ($12 == null) { + into = sn; + $$ = new Linq.Join (block, sn, (Expression)$5, outer_selector, (Linq.QueryBlock) current_block, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($6), GetLocation ($9)); + } else { + // + // Set equals right side parent to beginning of linq query, it is not accessible therefore cannot cause name collisions + // + var parent = block.Parent; + while (parent is Linq.QueryBlock) { + parent = parent.Parent; + } + current_block.Parent = parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lt = (LocatedToken) $12; + into = new Linq.RangeVariable (lt.Value, lt.Location); + + $$ = new Linq.GroupJoin (block, sn, (Expression)$5, outer_selector, (Linq.QueryBlock) current_block, into, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($6), GetLocation ($9), opt_intoStack.Pop ()); + } + + current_block = block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (into); + } + | JOIN type identifier_inside_body IN + { + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + expression_or_error ON + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + expression_or_error EQUALS + { + current_block.AddStatement (new ContextualReturn ((Expression) $9)); + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error opt_join_into + { + current_block.AddStatement (new ContextualReturn ((Expression) $12)); + current_block.SetEndLocation (lexer.Location); + + var outer_selector = linq_clause_blocks.Pop (); + var block = linq_clause_blocks.Pop (); + + var lt = (LocatedToken) $3; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + Linq.RangeVariable into; + + if ($13 == null) { + into = sn; + $$ = new Linq.Join (block, sn, (Expression)$6, outer_selector, (Linq.QueryBlock) current_block, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($6), GetLocation ($9)); + } else { + // + // Set equals right side parent to beginning of linq query, it is not accessible therefore cannot cause name collisions + // + var parent = block.Parent; + while (parent is Linq.QueryBlock) { + parent = parent.Parent; + } + current_block.Parent = parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lt = (LocatedToken) $13; + into = new Linq.RangeVariable (lt.Value, lt.Location); // TODO: + + $$ = new Linq.GroupJoin (block, sn, (Expression)$6, outer_selector, (Linq.QueryBlock) current_block, into, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($6), GetLocation ($9), opt_intoStack.Pop ()); + } + + current_block = block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (into); + } + ; + +opt_join_into + : /* empty */ + | INTO identifier_inside_body + { + opt_intoStack.Push (GetLocation ($1)); + $$ = $2; + } + ; + +orderby_clause + : ORDERBY + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + lbag.AddLocation (current_block, GetLocation ($1)); + } + orderings + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + $$ = $3; + } + ; + +orderings + : order_by + | order_by COMMA + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + orderings_then_by + { + ((Linq.AQueryClause)$1).Next = (Linq.AQueryClause)$4; + $$ = $1; + } + ; + +orderings_then_by + : then_by + | orderings_then_by COMMA + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock ((Linq.QueryBlock) current_block, lexer.Location); + } + then_by + { + ((Linq.AQueryClause)$1).Tail.Next = (Linq.AQueryClause)$4; + $$ = $1; + } + ; + +order_by + : expression + { + $$ = new Linq.OrderByAscending ((Linq.QueryBlock) current_block, (Expression)$1); + } + | expression ASCENDING + { + $$ = new Linq.OrderByAscending ((Linq.QueryBlock) current_block, (Expression)$1); + lbag.AddLocation ($$, GetLocation ($2)); + } + | expression DESCENDING + { + $$ = new Linq.OrderByDescending ((Linq.QueryBlock) current_block, (Expression)$1); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +then_by + : expression + { + $$ = new Linq.ThenByAscending ((Linq.QueryBlock) current_block, (Expression)$1); + } + | expression ASCENDING + { + $$ = new Linq.ThenByAscending ((Linq.QueryBlock) current_block, (Expression)$1); + lbag.AddLocation ($$, GetLocation ($2)); + } + | expression DESCENDING + { + $$ = new Linq.ThenByDescending ((Linq.QueryBlock) current_block, (Expression)$1); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + + +opt_query_continuation + : /* empty */ + | INTO identifier_inside_body + { + // query continuation block is not linked with query block but with block + // before. This means each query can use same range variable names for + // different identifiers. + + current_block.SetEndLocation (GetLocation ($1)); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + query_body + { + var current_block = linq_clause_blocks.Pop (); + var lt = (LocatedToken) $2; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + $$ = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, null, rv, GetLocation ($1)) { + next = (Linq.AQueryClause)$4 + }; + } + ; + +// +// Support for using the compiler as an interactive parser +// +// The INTERACTIVE_PARSER token is first sent to parse our +// productions; If the result is a Statement, the parsing +// is repeated, this time with INTERACTIVE_PARSE_WITH_BLOCK +// to setup the blocks in advance. +// +// This setup is here so that in the future we can add +// support for other constructs (type parsing, namespaces, etc) +// that do not require a block to be setup in advance +// + +interactive_parsing + : EVAL_STATEMENT_PARSER EOF + | EVAL_USING_DECLARATIONS_UNIT_PARSER using_directives opt_COMPLETE_COMPLETION + | EVAL_STATEMENT_PARSER + { + current_container = current_type = new Class (current_container, new MemberName (""), Modifiers.PUBLIC, null); + + // (ref object retval) + Parameter [] mpar = new Parameter [1]; + mpar [0] = new Parameter (new TypeExpression (compiler.BuiltinTypes.Object, Location.Null), "$retval", Parameter.Modifier.REF, null, Location.Null); + + ParametersCompiled pars = new ParametersCompiled (mpar); + var mods = Modifiers.PUBLIC | Modifiers.STATIC; + if (settings.Unsafe) + mods |= Modifiers.UNSAFE; + + current_local_parameters = pars; + var method = new InteractiveMethod ( + current_type, + new TypeExpression (compiler.BuiltinTypes.Void, Location.Null), + mods, + pars); + + current_type.AddMember (method); + oob_stack.Push (method); + + interactive_async = false; + + ++lexer.parsing_block; + start_block (lexer.Location); + } + interactive_statement_list opt_COMPLETE_COMPLETION + { + --lexer.parsing_block; + var method = (InteractiveMethod) oob_stack.Pop (); + method.Block = (ToplevelBlock) end_block(lexer.Location); + + if (interactive_async == true) { + method.ChangeToAsync (); + } + + InteractiveResult = (Class) pop_current_class (); + current_local_parameters = null; + } + | EVAL_COMPILATION_UNIT_PARSER interactive_compilation_unit + ; + +interactive_compilation_unit + : opt_extern_alias_directives opt_using_directives + | opt_extern_alias_directives opt_using_directives namespace_or_type_declarations + ; + +opt_COMPLETE_COMPLETION + : /* nothing */ + | COMPLETE_COMPLETION + ; + +close_brace_or_complete_completion + : CLOSE_BRACE + | COMPLETE_COMPLETION + ; + +// +// XML documentation code references micro parser +// +documentation_parsing + : DOC_SEE doc_cref + { + module.DocumentationBuilder.ParsedName = (MemberName) $2; + } + ; + +doc_cref + : doc_type_declaration_name opt_doc_method_sig + { + module.DocumentationBuilder.ParsedParameters = (List)$2; + } + | builtin_types opt_doc_method_sig + { + module.DocumentationBuilder.ParsedBuiltinType = (TypeExpression)$1; + module.DocumentationBuilder.ParsedParameters = (List)$2; + $$ = null; + } + | VOID opt_doc_method_sig + { + module.DocumentationBuilder.ParsedBuiltinType = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + module.DocumentationBuilder.ParsedParameters = (List)$2; + $$ = null; + } + | builtin_types DOT IDENTIFIER opt_doc_method_sig + { + module.DocumentationBuilder.ParsedBuiltinType = (TypeExpression)$1; + module.DocumentationBuilder.ParsedParameters = (List)$4; + var lt = (LocatedToken) $3; + $$ = new MemberName (lt.Value); + } + | doc_type_declaration_name DOT THIS + { + $$ = new MemberName ((MemberName) $1, MemberCache.IndexerNameAlias, Location.Null); + } + | doc_type_declaration_name DOT THIS OPEN_BRACKET + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_doc_parameters CLOSE_BRACKET + { + module.DocumentationBuilder.ParsedParameters = (List)$6; + $$ = new MemberName ((MemberName) $1, MemberCache.IndexerNameAlias, Location.Null); + } + | EXPLICIT OPERATOR type opt_doc_method_sig + { + var p = (List)$4 ?? new List (1); + p.Add (new DocumentationParameter ((FullNamedExpression) $3)); + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = Operator.OpType.Explicit; + $$ = null; + } + | IMPLICIT OPERATOR type opt_doc_method_sig + { + var p = (List)$4 ?? new List (1); + p.Add (new DocumentationParameter ((FullNamedExpression) $3)); + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = Operator.OpType.Implicit; + $$ = null; + } + | OPERATOR overloadable_operator opt_doc_method_sig + { + var p = (List)$3; + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = (Operator.OpType) $2; + $$ = null; + } + ; + +doc_type_declaration_name + : type_declaration_name + | doc_type_declaration_name DOT type_declaration_name + { + $$ = new MemberName (((MemberName) $1), (MemberName) $3); + } + ; + +opt_doc_method_sig + : /* empty */ + | OPEN_PARENS + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_doc_parameters CLOSE_PARENS + { + $$ = $3; + } + ; + +opt_doc_parameters + : /* empty */ + { + $$ = new List (0); + } + | doc_parameters + ; + +doc_parameters + : doc_parameter + { + var parameters = new List (); + parameters.Add ((DocumentationParameter) $1); + $$ = parameters; + } + | doc_parameters COMMA doc_parameter + { + var parameters = $1 as List; + parameters.Add ((DocumentationParameter) $3); + $$ = parameters; + } + ; + +doc_parameter + : opt_parameter_modifier parameter_type + { + if ($1 != null) + $$ = new DocumentationParameter ((Parameter.Modifier) $1, (FullNamedExpression) $2); + else + $$ = new DocumentationParameter ((FullNamedExpression) $2); + } + ; + +%% + +// +// A class used to hold info about an operator declarator +// +class OperatorDeclaration { + public readonly Operator.OpType optype; + public readonly FullNamedExpression ret_type; + public readonly Location location; + + public OperatorDeclaration (Operator.OpType op, FullNamedExpression ret_type, Location location) + { + optype = op; + this.ret_type = ret_type; + this.location = location; + } +} + +void Error_ExpectingTypeName (Expression expr) +{ + if (expr is Invocation){ + report.Error (1002, expr.Location, "Expecting `;'"); + } else { + expr.Error_InvalidExpressionStatement (report); + } +} + +void Error_ParameterModifierNotValid (string modifier, Location loc) +{ + report.Error (631, loc, "The parameter modifier `{0}' is not valid in this context", + modifier); +} + +void Error_DuplicateParameterModifier (Location loc, Parameter.Modifier mod) +{ + report.Error (1107, loc, "Duplicate parameter modifier `{0}'", + Parameter.GetModifierSignature (mod)); +} + +void Error_TypeExpected (Location loc) +{ + report.Error (1031, loc, "Type expected"); +} + +void Error_UnsafeCodeNotAllowed (Location loc) +{ + report.Error (227, loc, "Unsafe code requires the `unsafe' command line option to be specified"); +} + +void Warning_EmptyStatement (Location loc) +{ + report.Warning (642, 3, loc, "Possible mistaken empty statement"); +} + +void Error_NamedArgumentExpected (NamedArgument a) +{ + report.Error (1738, a.Location, "Named arguments must appear after the positional arguments"); +} + +void Error_MissingInitializer (Location loc) +{ + report.Error (210, loc, "You must provide an initializer in a fixed or using statement declaration"); +} + +object Error_AwaitAsIdentifier (object token) +{ + if (async_block) { + report.Error (4003, GetLocation (token), "`await' cannot be used as an identifier within an async method or lambda expression"); + return new LocatedToken ("await", GetLocation (token)); + } + + return token; +} + +void push_current_container (TypeDefinition tc, object partial_token) +{ + if (module.Evaluator != null){ + tc.Definition.Modifiers = tc.ModFlags = (tc.ModFlags & ~Modifiers.AccessibilityMask) | Modifiers.PUBLIC; + if (undo == null) + undo = new Undo (); + + undo.AddTypeContainer (current_container, tc); + } + + if (partial_token != null) + current_container.AddPartial (tc); + else + current_container.AddTypeContainer (tc); + + ++lexer.parsing_declaration; + current_container = tc; + current_type = tc; +} + +TypeContainer pop_current_class () +{ + var retval = current_container; + + current_container = current_container.Parent; + current_type = current_type.Parent as TypeDefinition; + + return retval; +} + +[System.Diagnostics.Conditional ("FULL_AST")] +void StoreModifierLocation (object token, Location loc) +{ + if (lbag == null) + return; + + if (mod_locations == null) + mod_locations = new List> (); + + mod_locations.Add (Tuple.Create ((Modifiers) token, loc)); +} + +List> GetModifierLocations () +{ + var result = mod_locations; + mod_locations = null; + return result; +} + +[System.Diagnostics.Conditional ("FULL_AST")] +void PushLocation (Location loc) +{ + if (location_stack == null) + location_stack = new Stack (); + + location_stack.Push (loc); +} + +Location PopLocation () +{ + if (location_stack == null) + return Location.Null; + + return location_stack.Pop (); +} + +string CheckAttributeTarget (int token, string a, Location l) +{ + switch (a) { + case "assembly" : case "module" : case "field" : case "method" : case "param" : case "property" : case "type" : + return a; + } + + if (!Tokenizer.IsValidIdentifier (a)) { + Error_SyntaxError (token); + } else { + report.Warning (658, 1, l, + "`{0}' is invalid attribute target. All attributes in this attribute section will be ignored", a); + } + + return string.Empty; +} + +static bool IsUnaryOperator (Operator.OpType op) +{ + switch (op) { + + case Operator.OpType.LogicalNot: + case Operator.OpType.OnesComplement: + case Operator.OpType.Increment: + case Operator.OpType.Decrement: + case Operator.OpType.True: + case Operator.OpType.False: + case Operator.OpType.UnaryPlus: + case Operator.OpType.UnaryNegation: + return true; + } + return false; +} + +void syntax_error (Location l, string msg) +{ + report.Error (1003, l, "Syntax error, " + msg); +} + +Tokenizer lexer; + +public Tokenizer Lexer { + get { + return lexer; + } +} + +public CSharpParser (SeekableStreamReader reader, CompilationSourceFile file, ParserSession session) + : this (reader, file, file.Compiler.Report, session) +{ +} + +public CSharpParser (SeekableStreamReader reader, CompilationSourceFile file, Report report, ParserSession session) +{ + this.file = file; + current_container = current_namespace = file; + + this.module = file.Module; + this.compiler = file.Compiler; + this.settings = compiler.Settings; + this.report = report; + + lang_version = settings.Version; + yacc_verbose_flag = settings.VerboseParserFlag; + doc_support = settings.DocumentationFile != null; + lexer = new Tokenizer (reader, file, session, report); + oob_stack = new Stack (); + lbag = session.LocationsBag; + use_global_stacks = session.UseJayGlobalArrays; + parameters_bucket = session.ParametersStack; +} + +public void parse () +{ + eof_token = Token.EOF; + + try { + if (yacc_verbose_flag > 1) + yyparse (lexer, new yydebug.yyDebugSimple ()); + else + yyparse (lexer); + + Tokenizer tokenizer = lexer as Tokenizer; + tokenizer.cleanup (); + } catch (Exception e){ + if (e is yyParser.yyUnexpectedEof) { + Error_SyntaxError (yyToken); + UnexpectedEOF = true; + return; + } + + if (e is yyParser.yyException) { + if (report.Errors == 0) + report.Error (-25, lexer.Location, "Parsing error"); + } else { + // Used by compiler-tester to test internal errors + if (yacc_verbose_flag > 0 || e is FatalException) + throw; + + report.Error (589, lexer.Location, "Internal compiler error during parsing" + e); + } + } +} + +void CheckToken (int error, int yyToken, string msg, Location loc) +{ + if (yyToken >= Token.FIRST_KEYWORD && yyToken <= Token.LAST_KEYWORD) + report.Error (error, loc, "{0}: `{1}' is a keyword", msg, GetTokenName (yyToken)); + else + report.Error (error, loc, msg); +} + +string ConsumeStoredComment () +{ + string s = tmpComment; + tmpComment = null; + Lexer.doc_state = XmlCommentState.Allowed; + return s; +} + +void FeatureIsNotAvailable (Location loc, string feature) +{ + report.FeatureIsNotAvailable (compiler, loc, feature); +} + +Location GetLocation (object obj) +{ + var lt = obj as LocatedToken; + if (lt != null) + return lt.Location; + + var mn = obj as MemberName; + if (mn != null) + return mn.Location; + + var expr = obj as Expression; + if (expr != null) + return expr.Location; + + return lexer.Location; +} + +void start_block (Location loc) +{ + if (current_block == null) { + current_block = new ToplevelBlock (compiler, current_local_parameters, loc); + parsing_anonymous_method = false; + } else if (parsing_anonymous_method) { + current_block = new ParametersBlock (current_block, current_local_parameters, loc); + parsing_anonymous_method = false; + } else { + current_block = new ExplicitBlock (current_block, loc, Location.Null); + } +} + +Block +end_block (Location loc) +{ + Block retval = current_block.Explicit; + retval.SetEndLocation (loc); + current_block = retval.Parent; + return retval; +} + +void start_anonymous (bool isLambda, ParametersCompiled parameters, bool isAsync, Location loc) +{ + oob_stack.Push (current_anonymous_method); + oob_stack.Push (current_local_parameters); + oob_stack.Push (current_variable); + oob_stack.Push (async_block); + + current_local_parameters = parameters; + if (isLambda) { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (loc, "lambda expressions"); + + current_anonymous_method = new LambdaExpression (loc); + } else { + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (loc, "anonymous methods"); + + current_anonymous_method = new AnonymousMethodExpression (loc); + } + current_anonymous_method.IsAsync = isAsync; + + async_block = isAsync; + // Force the next block to be created as a ToplevelBlock + parsing_anonymous_method = true; +} + +/* + * Completes the anonymous method processing, if lambda_expr is null, this + * means that we have a Statement instead of an Expression embedded + */ +AnonymousMethodExpression end_anonymous (ParametersBlock anon_block) +{ + AnonymousMethodExpression retval; + + if (async_block) + anon_block.IsAsync = true; + + current_anonymous_method.Block = anon_block; + retval = current_anonymous_method; + + async_block = (bool) oob_stack.Pop (); + current_variable = (BlockVariable) oob_stack.Pop (); + current_local_parameters = (ParametersCompiled) oob_stack.Pop (); + current_anonymous_method = (AnonymousMethodExpression) oob_stack.Pop (); + + return retval; +} + +void Error_SyntaxError (int token) +{ + Error_SyntaxError (0, token); +} + +void Error_SyntaxError (int error_code, int token) +{ + Error_SyntaxError (error_code, token, "Unexpected symbol"); +} + +void Error_SyntaxError (int error_code, int token, string msg) +{ + Lexer.CompleteOnEOF = false; + + // An error message has been reported by tokenizer + if (token == Token.ERROR) + return; + + // Avoid duplicit error message after unterminated string literals + if (token == Token.LITERAL && lexer.Location.Column == 0) + return; + + string symbol = GetSymbolName (token); + string expecting = GetExpecting (); + var loc = lexer.Location - symbol.Length; + + if (error_code == 0) { + if (expecting == "`identifier'") { + if (token > Token.FIRST_KEYWORD && token < Token.LAST_KEYWORD) { + report.Error (1041, loc, "Identifier expected, `{0}' is a keyword", symbol); + return; + } + + error_code = 1001; + expecting = "identifier"; + } else if (expecting == "`)'") { + error_code = 1026; + } else { + error_code = 1525; + } + } + + if (string.IsNullOrEmpty (expecting)) + report.Error (error_code, loc, "{1} `{0}'", symbol, msg); + else + report.Error (error_code, loc, "{2} `{0}', expecting {1}", symbol, expecting, msg); +} + +string GetExpecting () +{ + int [] tokens = yyExpectingTokens (yyExpectingState); + var names = new List (tokens.Length); + bool has_type = false; + bool has_identifier = false; + for (int i = 0; i < tokens.Length; i++){ + int token = tokens [i]; + has_identifier |= token == Token.IDENTIFIER; + + string name = GetTokenName (token); + if (name == "") + continue; + + has_type |= name == "type"; + if (names.Contains (name)) + continue; + + names.Add (name); + } + + // + // Too many tokens to enumerate + // + if (names.Count > 8) + return null; + + if (has_type && has_identifier) + names.Remove ("identifier"); + + if (names.Count == 1) + return "`" + GetTokenName (tokens [0]) + "'"; + + StringBuilder sb = new StringBuilder (); + names.Sort (); + int count = names.Count; + for (int i = 0; i < count; i++){ + bool last = i + 1 == count; + if (last) + sb.Append ("or "); + sb.Append ('`'); + sb.Append (names [i]); + sb.Append (last ? "'" : count < 3 ? "' " : "', "); + } + return sb.ToString (); +} + + +string GetSymbolName (int token) +{ + switch (token){ + case Token.LITERAL: + return ((Constant)lexer.Value).GetValue ().ToString (); + case Token.IDENTIFIER: + return ((LocatedToken)lexer.Value).Value; + + case Token.BOOL: + return "bool"; + case Token.BYTE: + return "byte"; + case Token.CHAR: + return "char"; + case Token.VOID: + return "void"; + case Token.DECIMAL: + return "decimal"; + case Token.DOUBLE: + return "double"; + case Token.FLOAT: + return "float"; + case Token.INT: + return "int"; + case Token.LONG: + return "long"; + case Token.SBYTE: + return "sbyte"; + case Token.SHORT: + return "short"; + case Token.STRING: + return "string"; + case Token.UINT: + return "uint"; + case Token.ULONG: + return "ulong"; + case Token.USHORT: + return "ushort"; + case Token.OBJECT: + return "object"; + + case Token.PLUS: + return "+"; + case Token.UMINUS: + case Token.MINUS: + return "-"; + case Token.BANG: + return "!"; + case Token.BITWISE_AND: + return "&"; + case Token.BITWISE_OR: + return "|"; + case Token.STAR: + return "*"; + case Token.PERCENT: + return "%"; + case Token.DIV: + return "/"; + case Token.CARRET: + return "^"; + case Token.OP_INC: + return "++"; + case Token.OP_DEC: + return "--"; + case Token.OP_SHIFT_LEFT: + return "<<"; + case Token.OP_SHIFT_RIGHT: + return ">>"; + case Token.OP_LT: + return "<"; + case Token.OP_GT: + return ">"; + case Token.OP_LE: + return "<="; + case Token.OP_GE: + return ">="; + case Token.OP_EQ: + return "=="; + case Token.OP_NE: + return "!="; + case Token.OP_AND: + return "&&"; + case Token.OP_OR: + return "||"; + case Token.OP_PTR: + return "->"; + case Token.OP_COALESCING: + return "??"; + case Token.OP_MULT_ASSIGN: + return "*="; + case Token.OP_DIV_ASSIGN: + return "/="; + case Token.OP_MOD_ASSIGN: + return "%="; + case Token.OP_ADD_ASSIGN: + return "+="; + case Token.OP_SUB_ASSIGN: + return "-="; + case Token.OP_SHIFT_LEFT_ASSIGN: + return "<<="; + case Token.OP_SHIFT_RIGHT_ASSIGN: + return ">>="; + case Token.OP_AND_ASSIGN: + return "&="; + case Token.OP_XOR_ASSIGN: + return "^="; + case Token.OP_OR_ASSIGN: + return "|="; + } + + return GetTokenName (token); +} + +static string GetTokenName (int token) +{ + switch (token){ + case Token.ABSTRACT: + return "abstract"; + case Token.AS: + return "as"; + case Token.ADD: + return "add"; + case Token.ASYNC: + return "async"; + case Token.BASE: + return "base"; + case Token.BREAK: + return "break"; + case Token.CASE: + return "case"; + case Token.CATCH: + return "catch"; + case Token.CHECKED: + return "checked"; + case Token.CLASS: + return "class"; + case Token.CONST: + return "const"; + case Token.CONTINUE: + return "continue"; + case Token.DEFAULT: + return "default"; + case Token.DELEGATE: + return "delegate"; + case Token.DO: + return "do"; + case Token.ELSE: + return "else"; + case Token.ENUM: + return "enum"; + case Token.EVENT: + return "event"; + case Token.EXPLICIT: + return "explicit"; + case Token.EXTERN: + case Token.EXTERN_ALIAS: + return "extern"; + case Token.FALSE: + return "false"; + case Token.FINALLY: + return "finally"; + case Token.FIXED: + return "fixed"; + case Token.FOR: + return "for"; + case Token.FOREACH: + return "foreach"; + case Token.GOTO: + return "goto"; + case Token.IF: + return "if"; + case Token.IMPLICIT: + return "implicit"; + case Token.IN: + return "in"; + case Token.INTERFACE: + return "interface"; + case Token.INTERNAL: + return "internal"; + case Token.IS: + return "is"; + case Token.LOCK: + return "lock"; + case Token.NAMESPACE: + return "namespace"; + case Token.NEW: + return "new"; + case Token.NULL: + return "null"; + case Token.OPERATOR: + return "operator"; + case Token.OUT: + return "out"; + case Token.OVERRIDE: + return "override"; + case Token.PARAMS: + return "params"; + case Token.PRIVATE: + return "private"; + case Token.PROTECTED: + return "protected"; + case Token.PUBLIC: + return "public"; + case Token.READONLY: + return "readonly"; + case Token.REF: + return "ref"; + case Token.RETURN: + return "return"; + case Token.REMOVE: + return "remove"; + case Token.SEALED: + return "sealed"; + case Token.SIZEOF: + return "sizeof"; + case Token.STACKALLOC: + return "stackalloc"; + case Token.STATIC: + return "static"; + case Token.STRUCT: + return "struct"; + case Token.SWITCH: + return "switch"; + case Token.THIS: + return "this"; + case Token.THROW: + return "throw"; + case Token.TRUE: + return "true"; + case Token.TRY: + return "try"; + case Token.TYPEOF: + return "typeof"; + case Token.UNCHECKED: + return "unchecked"; + case Token.UNSAFE: + return "unsafe"; + case Token.USING: + return "using"; + case Token.VIRTUAL: + return "virtual"; + case Token.VOLATILE: + return "volatile"; + case Token.WHERE: + return "where"; + case Token.WHILE: + return "while"; + case Token.ARGLIST: + return "__arglist"; + case Token.REFVALUE: + return "__refvalue"; + case Token.REFTYPE: + return "__reftype"; + case Token.MAKEREF: + return "__makeref"; + case Token.PARTIAL: + return "partial"; + case Token.ARROW: + return "=>"; + case Token.FROM: + case Token.FROM_FIRST: + return "from"; + case Token.JOIN: + return "join"; + case Token.ON: + return "on"; + case Token.EQUALS: + return "equals"; + case Token.SELECT: + return "select"; + case Token.GROUP: + return "group"; + case Token.BY: + return "by"; + case Token.LET: + return "let"; + case Token.ORDERBY: + return "orderby"; + case Token.ASCENDING: + return "ascending"; + case Token.DESCENDING: + return "descending"; + case Token.INTO: + return "into"; + case Token.GET: + return "get"; + case Token.SET: + return "set"; + case Token.OPEN_BRACE: + return "{"; + case Token.CLOSE_BRACE: + return "}"; + case Token.OPEN_BRACKET: + case Token.OPEN_BRACKET_EXPR: + return "["; + case Token.CLOSE_BRACKET: + return "]"; + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + case Token.OPEN_PARENS: + return "("; + case Token.CLOSE_PARENS: + return ")"; + case Token.DOT: + return "."; + case Token.COMMA: + return ","; + case Token.DEFAULT_COLON: + return "default:"; + case Token.COLON: + return ":"; + case Token.SEMICOLON: + return ";"; + case Token.TILDE: + return "~"; + + case Token.PLUS: + case Token.UMINUS: + case Token.MINUS: + case Token.BANG: + case Token.OP_LT: + case Token.OP_GT: + case Token.BITWISE_AND: + case Token.BITWISE_OR: + case Token.STAR: + case Token.PERCENT: + case Token.DIV: + case Token.CARRET: + case Token.OP_INC: + case Token.OP_DEC: + case Token.OP_SHIFT_LEFT: + case Token.OP_SHIFT_RIGHT: + case Token.OP_LE: + case Token.OP_GE: + case Token.OP_EQ: + case Token.OP_NE: + case Token.OP_AND: + case Token.OP_OR: + case Token.OP_PTR: + case Token.OP_COALESCING: + case Token.OP_MULT_ASSIGN: + case Token.OP_DIV_ASSIGN: + case Token.OP_MOD_ASSIGN: + case Token.OP_ADD_ASSIGN: + case Token.OP_SUB_ASSIGN: + case Token.OP_SHIFT_LEFT_ASSIGN: + case Token.OP_SHIFT_RIGHT_ASSIGN: + case Token.OP_AND_ASSIGN: + case Token.OP_XOR_ASSIGN: + case Token.OP_OR_ASSIGN: + return ""; + + case Token.BOOL: + case Token.BYTE: + case Token.CHAR: + case Token.VOID: + case Token.DECIMAL: + case Token.DOUBLE: + case Token.FLOAT: + case Token.INT: + case Token.LONG: + case Token.SBYTE: + case Token.SHORT: + case Token.STRING: + case Token.UINT: + case Token.ULONG: + case Token.USHORT: + case Token.OBJECT: + return "type"; + + case Token.ASSIGN: + return "="; + case Token.OP_GENERICS_LT: + case Token.GENERIC_DIMENSION: + return "<"; + case Token.OP_GENERICS_GT: + return ">"; + case Token.INTERR: + case Token.INTERR_NULLABLE: + return "?"; + case Token.DOUBLE_COLON: + return "::"; + case Token.LITERAL: + return "value"; + case Token.IDENTIFIER: + case Token.AWAIT: + return "identifier"; + + case Token.EOF: + return "end-of-file"; + + // All of these are internal. + case Token.NONE: + case Token.ERROR: + case Token.FIRST_KEYWORD: + case Token.EVAL_COMPILATION_UNIT_PARSER: + case Token.EVAL_USING_DECLARATIONS_UNIT_PARSER: + case Token.EVAL_STATEMENT_PARSER: + case Token.LAST_KEYWORD: + case Token.GENERATE_COMPLETION: + case Token.COMPLETE_COMPLETION: + return ""; + + // A bit more robust. + default: + return yyNames [token]; + } +} + +/* end end end */ +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs new file mode 100644 index 000000000..a423eb50f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs @@ -0,0 +1,4033 @@ +// +// cs-tokenizer.cs: The Tokenizer for the C# compiler +// This also implements the preprocessor +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// +using System; +using System.Text; +using System.Collections.Generic; +using System.Globalization; +using System.Diagnostics; +using System.Collections; + +namespace Mono.CSharp +{ + // + // This class has to be used by parser only, it reuses token + // details once a file is parsed + // + public class LocatedToken + { + public int row, column; + public string value; + public SourceFile file; + + public LocatedToken () + { + } + + public LocatedToken (string value, Location loc) + { + this.value = value; + file = loc.SourceFile; + row = loc.Row; + column = loc.Column; + } + + public override string ToString () + { + return string.Format ("Token '{0}' at {1},{2}", Value, row, column); + } + + public Location Location + { + get { return new Location (file, row, column); } + } + + public string Value + { + get { return value; } + } + } + + /// + /// Tokenizer for C# source code. + /// + public class Tokenizer : yyParser.yyInput + { + class KeywordEntry + { + public readonly T Token; + public KeywordEntry Next; + public readonly char[] Value; + + public KeywordEntry (string value,T token) + { + this.Value = value.ToCharArray (); + this.Token = token; + } + } + + sealed class IdentifiersComparer : IEqualityComparer + { + readonly int length; + + public IdentifiersComparer (int length) + { + this.length = length; + } + + public bool Equals (char[] x, char[] y) + { + for (int i = 0; i < length; ++i) + if (x [i] != y [i]) + return false; + + return true; + } + + public int GetHashCode (char[] obj) + { + int h = 0; + for (int i = 0; i < length; ++i) + h = (h << 5) - h + obj [i]; + + return h; + } + } + + public class LocatedTokenBuffer + { + readonly LocatedToken[] buffer; + public int pos; + + public LocatedTokenBuffer () + { + buffer = new LocatedToken[0]; + } + + public LocatedTokenBuffer (LocatedToken[] buffer) + { + this.buffer = buffer ?? new LocatedToken[0]; + } + + public LocatedToken Create (SourceFile file, int row, int column) + { + return Create (null, file, row, column); + } + + public LocatedToken Create (string value, SourceFile file, int row, int column) + { + // + // TODO: I am not very happy about the logic but it's the best + // what I could come up with for now. + // Ideally we should be using just tiny buffer (256 elements) which + // is enough to hold all details for currect stack and recycle elements + // poped from the stack but there is a trick needed to recycle + // them properly. + // + LocatedToken entry; + if (pos >= buffer.Length) { + entry = new LocatedToken (); + } else { + entry = buffer[pos]; + if (entry == null) { + entry = new LocatedToken (); + buffer[pos] = entry; + } + + ++pos; + } + entry.value = value; + entry.file = file; + entry.row = row; + entry.column = column; + return entry; + } + + // + // Used for token not required by expression evaluator + // + [Conditional ("FULL_AST")] + public void CreateOptional (SourceFile file, int row, int col, ref object token) + { + token = Create (file, row, col); + } + } + + public enum PreprocessorDirective + { + Invalid = 0, + + Region = 1, + Endregion = 2, + If = 3 | RequiresArgument, + Endif = 4, + Elif = 5 | RequiresArgument, + Else = 6, + Define = 7 | RequiresArgument, + Undef = 8 | RequiresArgument, + Error = 9, + Warning = 10, + Pragma = 11 | CustomArgumentsParsing, + Line = 12 | CustomArgumentsParsing, + + CustomArgumentsParsing = 1 << 10, + RequiresArgument = 1 << 11 + } + + readonly SeekableStreamReader reader; + readonly CompilationSourceFile source_file; + public CompilationSourceFile SourceFile { get { return source_file; } } + readonly CompilerContext context; + readonly Report Report; + + + SourceFile current_source; + Location hidden_block_start; + int ref_line = 1; + int line = 1; + int col = 0; + int previous_col; + int current_token; + readonly int tab_size; + bool handle_get_set = false; + bool handle_remove_add = false; + bool handle_where; + bool lambda_arguments_parsing; + List escaped_identifiers; + int parsing_generic_less_than; + readonly bool doc_processing; + readonly LocatedTokenBuffer ltb; + + // + // Used mainly for parser optimizations. Some expressions for instance + // can appear only in block (including initializer, base initializer) + // scope only + // + public int parsing_block; + internal bool query_parsing; + + // + // When parsing type only, useful for ambiguous nullable types + // + public int parsing_type; + + // + // Set when parsing generic declaration (type or method header) + // + public bool parsing_generic_declaration; + public bool parsing_generic_declaration_doc; + + // + // The value indicates that we have not reach any declaration or + // namespace yet + // + public int parsing_declaration; + public bool parsing_attribute_section; + + public bool parsing_modifiers; + + // + // The special characters to inject on streams to run the unit parser + // in the special expression mode. Using private characters from + // Plane Sixteen (U+100000 to U+10FFFD) + // + // This character is only tested just before the tokenizer is about to report + // an error; So on the regular operation mode, this addition will have no + // impact on the tokenizer's performance. + // + + public const int EvalStatementParserCharacter = 0x100000; + public const int EvalCompilationUnitParserCharacter = 0x100001; + public const int EvalUsingDeclarationsParserCharacter = 0x100002; + public const int DocumentationXref = 0x100003; + + const int UnicodeLS = 0x2028; + const int UnicodePS = 0x2029; + + // + // XML documentation buffer. The save point is used to divide + // comments on types and comments on members. + // + StringBuilder xml_comment_buffer; + + // + // See comment on XmlCommentState enumeration. + // + XmlCommentState xml_doc_state = XmlCommentState.Allowed; + + // + // Whether tokens have been seen on this line + // + bool tokens_seen = false; + + // + // Set to true once the GENERATE_COMPLETION token has bee + // returned. This helps produce one GENERATE_COMPLETION, + // as many COMPLETE_COMPLETION as necessary to complete the + // AST tree and one final EOF. + // + bool generated; + + // + // Whether a token has been seen on the file + // This is needed because `define' is not allowed to be used + // after a token has been seen. + // + bool any_token_seen; + + // + // Class variables + // + static readonly KeywordEntry[][] keywords; + static readonly KeywordEntry[][] keywords_preprocessor; + static readonly HashSet keyword_strings; + static readonly NumberStyles styles; + static readonly NumberFormatInfo csharp_format_info; + + // Pragma arguments + static readonly char[] pragma_warning = "warning".ToCharArray (); + static readonly char[] pragma_warning_disable = "disable".ToCharArray (); + static readonly char[] pragma_warning_restore = "restore".ToCharArray (); + static readonly char[] pragma_checksum = "checksum".ToCharArray (); + static readonly char[] line_hidden = "hidden".ToCharArray (); + static readonly char[] line_default = "default".ToCharArray (); + + static readonly char[] simple_whitespaces = new char[] { ' ', '\t' }; + bool startsLine = true; + internal SpecialsBag sbag; + + public bool PropertyParsing { + get { return handle_get_set; } + set { handle_get_set = value; } + } + + public bool EventParsing { + get { return handle_remove_add; } + set { handle_remove_add = value; } + } + + public bool ConstraintsParsing { + get { return handle_where; } + set { handle_where = value; } + } + + public XmlCommentState doc_state { + get { return xml_doc_state; } + set { + if (value == XmlCommentState.Allowed) { + check_incorrect_doc_comment (); + reset_doc_comment (); + } + xml_doc_state = value; + } + } + + // + // This is used to trigger completion generation on the parser + public bool CompleteOnEOF; + + void AddEscapedIdentifier (Location loc) + { + if (escaped_identifiers == null) + escaped_identifiers = new List (); + + escaped_identifiers.Add (loc); + } + + public bool IsEscapedIdentifier (ATypeNameExpression name) + { + return escaped_identifiers != null && escaped_identifiers.Contains (name.Location); + } + + // + // Values for the associated token returned + // + internal int putback_char; // Used by repl only + object val; + + // + // Pre-processor + // + const int TAKING = 1; + const int ELSE_SEEN = 4; + const int PARENT_TAKING = 8; + const int REGION = 16; + + // + // pre-processor if stack state: + // + Stack ifstack; + + public const int MaxIdentifierLength = 512; + public const int MaxNumberLength = 512; + + readonly char[] id_builder; + readonly Dictionary[] identifiers; + readonly char[] number_builder; + int number_pos; + + char[] value_builder = new char[64]; + + public int Line { + get { + return ref_line; + } + set { + ref_line = value; + } + } + + public int Column { + get { + return col; + } + set { + col = value; + } + } + + // + // This is used when the tokenizer needs to save + // the current position as it needs to do some parsing + // on its own to deamiguate a token in behalf of the + // parser. + // + Stack position_stack = new Stack (2); + + class Position + { + public int position; + public int line; + public int ref_line; + public int col; + public Location hidden; + public int putback_char; + public int previous_col; + public Stack ifstack; + public int parsing_generic_less_than; + public int current_token; + public object val; + + public Position (Tokenizer t) + { + position = t.reader.Position; + line = t.line; + ref_line = t.ref_line; + col = t.col; + hidden = t.hidden_block_start; + putback_char = t.putback_char; + previous_col = t.previous_col; + if (t.ifstack != null && t.ifstack.Count != 0) { + // There is no simple way to clone Stack all + // methods reverse the order + var clone = t.ifstack.ToArray (); + Array.Reverse (clone); + ifstack = new Stack (clone); + } + parsing_generic_less_than = t.parsing_generic_less_than; + current_token = t.current_token; + val = t.val; + } + } + + public Tokenizer (SeekableStreamReader input, CompilationSourceFile file, ParserSession session, Report report) + { + this.source_file = file; + this.context = file.Compiler; + this.current_source = file.SourceFile; + this.identifiers = session.Identifiers; + this.id_builder = session.IDBuilder; + this.number_builder = session.NumberBuilder; + this.ltb = new LocatedTokenBuffer (session.LocatedTokens); + this.Report = report; + + reader = input; + + putback_char = -1; + + xml_comment_buffer = new StringBuilder (); + doc_processing = context.Settings.DocumentationFile != null; + + tab_size = context.Settings.TabSize; + } + + public void PushPosition () + { + position_stack.Push (new Position (this)); + } + + public void PopPosition () + { + Position p = position_stack.Pop (); + + reader.Position = p.position; + ref_line = p.ref_line; + line = p.line; + col = p.col; + hidden_block_start = p.hidden; + putback_char = p.putback_char; + previous_col = p.previous_col; + ifstack = p.ifstack; + parsing_generic_less_than = p.parsing_generic_less_than; + current_token = p.current_token; + val = p.val; + } + + // Do not reset the position, ignore it. + public void DiscardPosition () + { + position_stack.Pop (); + } + + static void AddKeyword (string kw, int token) + { + keyword_strings.Add (kw); + + AddKeyword (keywords, kw, token); +} + + static void AddPreprocessorKeyword (string kw, PreprocessorDirective directive) + { + AddKeyword (keywords_preprocessor, kw, directive); + } + + static void AddKeyword (KeywordEntry[][] keywords, string kw, T token) + { + int length = kw.Length; + if (keywords[length] == null) { + keywords[length] = new KeywordEntry['z' - '_' + 1]; + } + + int char_index = kw[0] - '_'; + var kwe = keywords[length][char_index]; + if (kwe == null) { + keywords[length][char_index] = new KeywordEntry (kw, token); + return; + } + + while (kwe.Next != null) { + kwe = kwe.Next; + } + + kwe.Next = new KeywordEntry (kw, token); + } + + // + // Class initializer + // + static Tokenizer () + { + keyword_strings = new HashSet (); + + // 11 is the length of the longest keyword for now + keywords = new KeywordEntry[11][]; + + AddKeyword ("__arglist", Token.ARGLIST); + AddKeyword ("__makeref", Token.MAKEREF); + AddKeyword ("__reftype", Token.REFTYPE); + AddKeyword ("__refvalue", Token.REFVALUE); + AddKeyword ("abstract", Token.ABSTRACT); + AddKeyword ("as", Token.AS); + AddKeyword ("add", Token.ADD); + AddKeyword ("base", Token.BASE); + AddKeyword ("bool", Token.BOOL); + AddKeyword ("break", Token.BREAK); + AddKeyword ("byte", Token.BYTE); + AddKeyword ("case", Token.CASE); + AddKeyword ("catch", Token.CATCH); + AddKeyword ("char", Token.CHAR); + AddKeyword ("checked", Token.CHECKED); + AddKeyword ("class", Token.CLASS); + AddKeyword ("const", Token.CONST); + AddKeyword ("continue", Token.CONTINUE); + AddKeyword ("decimal", Token.DECIMAL); + AddKeyword ("default", Token.DEFAULT); + AddKeyword ("delegate", Token.DELEGATE); + AddKeyword ("do", Token.DO); + AddKeyword ("double", Token.DOUBLE); + AddKeyword ("else", Token.ELSE); + AddKeyword ("enum", Token.ENUM); + AddKeyword ("event", Token.EVENT); + AddKeyword ("explicit", Token.EXPLICIT); + AddKeyword ("extern", Token.EXTERN); + AddKeyword ("false", Token.FALSE); + AddKeyword ("finally", Token.FINALLY); + AddKeyword ("fixed", Token.FIXED); + AddKeyword ("float", Token.FLOAT); + AddKeyword ("for", Token.FOR); + AddKeyword ("foreach", Token.FOREACH); + AddKeyword ("goto", Token.GOTO); + AddKeyword ("get", Token.GET); + AddKeyword ("if", Token.IF); + AddKeyword ("implicit", Token.IMPLICIT); + AddKeyword ("in", Token.IN); + AddKeyword ("int", Token.INT); + AddKeyword ("interface", Token.INTERFACE); + AddKeyword ("internal", Token.INTERNAL); + AddKeyword ("is", Token.IS); + AddKeyword ("lock", Token.LOCK); + AddKeyword ("long", Token.LONG); + AddKeyword ("namespace", Token.NAMESPACE); + AddKeyword ("new", Token.NEW); + AddKeyword ("null", Token.NULL); + AddKeyword ("object", Token.OBJECT); + AddKeyword ("operator", Token.OPERATOR); + AddKeyword ("out", Token.OUT); + AddKeyword ("override", Token.OVERRIDE); + AddKeyword ("params", Token.PARAMS); + AddKeyword ("private", Token.PRIVATE); + AddKeyword ("protected", Token.PROTECTED); + AddKeyword ("public", Token.PUBLIC); + AddKeyword ("readonly", Token.READONLY); + AddKeyword ("ref", Token.REF); + AddKeyword ("remove", Token.REMOVE); + AddKeyword ("return", Token.RETURN); + AddKeyword ("sbyte", Token.SBYTE); + AddKeyword ("sealed", Token.SEALED); + AddKeyword ("set", Token.SET); + AddKeyword ("short", Token.SHORT); + AddKeyword ("sizeof", Token.SIZEOF); + AddKeyword ("stackalloc", Token.STACKALLOC); + AddKeyword ("static", Token.STATIC); + AddKeyword ("string", Token.STRING); + AddKeyword ("struct", Token.STRUCT); + AddKeyword ("switch", Token.SWITCH); + AddKeyword ("this", Token.THIS); + AddKeyword ("throw", Token.THROW); + AddKeyword ("true", Token.TRUE); + AddKeyword ("try", Token.TRY); + AddKeyword ("typeof", Token.TYPEOF); + AddKeyword ("uint", Token.UINT); + AddKeyword ("ulong", Token.ULONG); + AddKeyword ("unchecked", Token.UNCHECKED); + AddKeyword ("unsafe", Token.UNSAFE); + AddKeyword ("ushort", Token.USHORT); + AddKeyword ("using", Token.USING); + AddKeyword ("virtual", Token.VIRTUAL); + AddKeyword ("void", Token.VOID); + AddKeyword ("volatile", Token.VOLATILE); + AddKeyword ("while", Token.WHILE); + AddKeyword ("partial", Token.PARTIAL); + AddKeyword ("where", Token.WHERE); + + // LINQ keywords + AddKeyword ("from", Token.FROM); + AddKeyword ("join", Token.JOIN); + AddKeyword ("on", Token.ON); + AddKeyword ("equals", Token.EQUALS); + AddKeyword ("select", Token.SELECT); + AddKeyword ("group", Token.GROUP); + AddKeyword ("by", Token.BY); + AddKeyword ("let", Token.LET); + AddKeyword ("orderby", Token.ORDERBY); + AddKeyword ("ascending", Token.ASCENDING); + AddKeyword ("descending", Token.DESCENDING); + AddKeyword ("into", Token.INTO); + + // Contextual async keywords + AddKeyword ("async", Token.ASYNC); + AddKeyword ("await", Token.AWAIT); + + keywords_preprocessor = new KeywordEntry[10][]; + + AddPreprocessorKeyword ("region", PreprocessorDirective.Region); + AddPreprocessorKeyword ("endregion", PreprocessorDirective.Endregion); + AddPreprocessorKeyword ("if", PreprocessorDirective.If); + AddPreprocessorKeyword ("endif", PreprocessorDirective.Endif); + AddPreprocessorKeyword ("elif", PreprocessorDirective.Elif); + AddPreprocessorKeyword ("else", PreprocessorDirective.Else); + AddPreprocessorKeyword ("define", PreprocessorDirective.Define); + AddPreprocessorKeyword ("undef", PreprocessorDirective.Undef); + AddPreprocessorKeyword ("error", PreprocessorDirective.Error); + AddPreprocessorKeyword ("warning", PreprocessorDirective.Warning); + AddPreprocessorKeyword ("pragma", PreprocessorDirective.Pragma); + AddPreprocessorKeyword ("line", PreprocessorDirective.Line); + + csharp_format_info = NumberFormatInfo.InvariantInfo; + styles = NumberStyles.Float; + } + + int GetKeyword (char[] id, int id_len) + { + // + // Keywords are stored in an array of arrays grouped by their + // length and then by the first character + // + if (id_len >= keywords.Length || keywords [id_len] == null) + return -1; + + int first_index = id [0] - '_'; + if (first_index > 'z' - '_') + return -1; + + var kwe = keywords [id_len] [first_index]; + if (kwe == null) + return -1; + + int res; + do { + res = kwe.Token; + for (int i = 1; i < id_len; ++i) { + if (id [i] != kwe.Value [i]) { + res = 0; + kwe = kwe.Next; + break; + } + } + } while (res == 0 && kwe != null); + + if (res == 0) + return -1; + + int next_token; + switch (res) { + case Token.GET: + case Token.SET: + if (!handle_get_set) + res = -1; + break; + case Token.REMOVE: + case Token.ADD: + if (!handle_remove_add) + res = -1; + break; + case Token.EXTERN: + if (parsing_declaration == 0) + res = Token.EXTERN_ALIAS; + break; + case Token.DEFAULT: + if (peek_token () == Token.COLON) { + token (); + res = Token.DEFAULT_COLON; + } + break; + case Token.WHERE: + if (!(handle_where && current_token != Token.COLON) && !query_parsing) + res = -1; + break; + case Token.FROM: + // + // A query expression is any expression that starts with `from identifier' + // followed by any token except ; , = + // + if (!query_parsing) { + if (lambda_arguments_parsing || parsing_block == 0) { + res = -1; + break; + } + + PushPosition (); + // HACK: to disable generics micro-parser, because PushPosition does not + // store identifiers array + parsing_generic_less_than = 1; + switch (xtoken ()) { + case Token.IDENTIFIER: + case Token.INT: + case Token.BOOL: + case Token.BYTE: + case Token.CHAR: + case Token.DECIMAL: + case Token.DOUBLE: + case Token.FLOAT: + case Token.LONG: + case Token.OBJECT: + case Token.STRING: + case Token.UINT: + case Token.ULONG: + next_token = xtoken (); + if (next_token == Token.SEMICOLON || next_token == Token.COMMA || next_token == Token.EQUALS || next_token == Token.ASSIGN) + goto default; + + res = Token.FROM_FIRST; + query_parsing = true; + if (context.Settings.Version <= LanguageVersion.ISO_2) + Report.FeatureIsNotAvailable (context, Location, "query expressions"); + break; + case Token.VOID: + Expression.Error_VoidInvalidInTheContext (Location, Report); + break; + default: + PopPosition (); + // HACK: A token is not a keyword so we need to restore identifiers buffer + // which has been overwritten before we grabbed the identifier + id_builder [0] = 'f'; id_builder [1] = 'r'; id_builder [2] = 'o'; id_builder [3] = 'm'; + return -1; + } + PopPosition (); + } + break; + case Token.JOIN: + case Token.ON: + case Token.EQUALS: + case Token.SELECT: + case Token.GROUP: + case Token.BY: + case Token.LET: + case Token.ORDERBY: + case Token.ASCENDING: + case Token.DESCENDING: + case Token.INTO: + if (!query_parsing) + res = -1; + break; + + case Token.USING: + case Token.NAMESPACE: + // TODO: some explanation needed + check_incorrect_doc_comment (); + parsing_modifiers = false; + break; + + case Token.PARTIAL: + if (parsing_block > 0) { + res = -1; + break; + } + + // Save current position and parse next token. + PushPosition (); + + next_token = token (); + bool ok = (next_token == Token.CLASS) || + (next_token == Token.STRUCT) || + (next_token == Token.INTERFACE) || + (next_token == Token.VOID); + + PopPosition (); + + if (ok) { + if (next_token == Token.VOID) { + if (context.Settings.Version <= LanguageVersion.ISO_2) + Report.FeatureIsNotAvailable (context, Location, "partial methods"); + } else if (context.Settings.Version == LanguageVersion.ISO_1) + Report.FeatureIsNotAvailable (context, Location, "partial types"); + + return res; + } + + if (next_token < Token.LAST_KEYWORD) { + Report.Error (267, Location, + "The `partial' modifier can be used only immediately before `class', `struct', `interface', or `void' keyword"); + return token (); + } + + // HACK: A token is not a keyword so we need to restore identifiers buffer + // which has been overwritten before we grabbed the identifier + id_builder[0] = 'p'; + id_builder[1] = 'a'; + id_builder[2] = 'r'; + id_builder[3] = 't'; + id_builder[4] = 'i'; + id_builder[5] = 'a'; + id_builder[6] = 'l'; + res = -1; + break; + + case Token.ASYNC: + if (parsing_modifiers) { + // + // Skip attributes section or constructor called async + // + if (parsing_attribute_section || peek_token () == Token.OPEN_PARENS) { + res = -1; + } else { + // async is keyword + } + } else if (parsing_block > 0) { + switch (peek_token ()) { + case Token.DELEGATE: + case Token.OPEN_PARENS_LAMBDA: + // async is keyword + break; + case Token.IDENTIFIER: + PushPosition (); + xtoken (); + if (xtoken () != Token.ARROW) { + PopPosition (); + goto default; + } + + PopPosition (); + break; + default: + // peek_token could overwrite id_buffer + id_builder [0] = 'a'; id_builder [1] = 's'; id_builder [2] = 'y'; id_builder [3] = 'n'; id_builder [4] = 'c'; + res = -1; + break; + } + } else { + res = -1; + } + + if (res == Token.ASYNC && context.Settings.Version <= LanguageVersion.V_4) { + Report.FeatureIsNotAvailable (context, Location, "asynchronous functions"); + } + + break; + + case Token.AWAIT: + if (parsing_block == 0) + res = -1; + + break; + } + + return res; + } + + static PreprocessorDirective GetPreprocessorDirective (char[] id, int id_len) + { + // + // Keywords are stored in an array of arrays grouped by their + // length and then by the first character + // + if (id_len >= keywords_preprocessor.Length || keywords_preprocessor[id_len] == null) + return PreprocessorDirective.Invalid; + + int first_index = id[0] - '_'; + if (first_index > 'z' - '_') + return PreprocessorDirective.Invalid; + + var kwe = keywords_preprocessor[id_len][first_index]; + if (kwe == null) + return PreprocessorDirective.Invalid; + + PreprocessorDirective res = PreprocessorDirective.Invalid; + do { + res = kwe.Token; + for (int i = 1; i < id_len; ++i) { + if (id[i] != kwe.Value[i]) { + res = 0; + kwe = kwe.Next; + break; + } + } + } while (res == PreprocessorDirective.Invalid && kwe != null); + + return res; + } + + public Location Location { + get { + return new Location (current_source, ref_line, col); + } + } + + static bool is_identifier_start_character (int c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter ((char)c); + } + + static bool is_identifier_part_character (char c) + { + if (c >= 'a' && c <= 'z') + return true; + + if (c >= 'A' && c <= 'Z') + return true; + + if (c == '_' || (c >= '0' && c <= '9')) + return true; + + if (c < 0x80) + return false; + + return is_identifier_part_character_slow_part (c); + } + + static bool is_identifier_part_character_slow_part (char c) + { + if (Char.IsLetter (c)) + return true; + + switch (Char.GetUnicodeCategory (c)) { + case UnicodeCategory.ConnectorPunctuation: + + // combining-character: A Unicode character of classes Mn or Mc + case UnicodeCategory.NonSpacingMark: + case UnicodeCategory.SpacingCombiningMark: + + // decimal-digit-character: A Unicode character of the class Nd + case UnicodeCategory.DecimalDigitNumber: + return true; + } + + return false; + } + + public static bool IsKeyword (string s) + { + return keyword_strings.Contains (s); + } + + // + // Open parens micro parser. Detects both lambda and cast ambiguity. + // + int TokenizeOpenParens () + { + int ptoken; + current_token = -1; + + int bracket_level = 0; + bool is_type = false; + bool can_be_type = false; + + while (true) { + ptoken = current_token; + token (); + + switch (current_token) { + case Token.CLOSE_PARENS: + token (); + + // + // Expression inside parens is lambda, (int i) => + // + if (current_token == Token.ARROW) + return Token.OPEN_PARENS_LAMBDA; + + // + // Expression inside parens is single type, (int[]) + // + if (is_type) { + if (current_token == Token.SEMICOLON) + return Token.OPEN_PARENS; + + return Token.OPEN_PARENS_CAST; + } + + // + // Expression is possible cast, look at next token, (T)null + // + if (can_be_type) { + switch (current_token) { + case Token.OPEN_PARENS: + case Token.BANG: + case Token.TILDE: + case Token.IDENTIFIER: + case Token.LITERAL: + case Token.BASE: + case Token.CHECKED: + case Token.DELEGATE: + case Token.FALSE: + case Token.FIXED: + case Token.NEW: + case Token.NULL: + case Token.SIZEOF: + case Token.THIS: + case Token.THROW: + case Token.TRUE: + case Token.TYPEOF: + case Token.UNCHECKED: + case Token.UNSAFE: + case Token.DEFAULT: + case Token.AWAIT: + + // + // These can be part of a member access + // + case Token.INT: + case Token.UINT: + case Token.SHORT: + case Token.USHORT: + case Token.LONG: + case Token.ULONG: + case Token.DOUBLE: + case Token.FLOAT: + case Token.CHAR: + case Token.BYTE: + case Token.DECIMAL: + case Token.BOOL: + return Token.OPEN_PARENS_CAST; + } + } + return Token.OPEN_PARENS; + + case Token.DOT: + case Token.DOUBLE_COLON: + if (ptoken != Token.IDENTIFIER && ptoken != Token.OP_GENERICS_GT) + goto default; + + continue; + + case Token.IDENTIFIER: + case Token.AWAIT: + switch (ptoken) { + case Token.DOT: + if (bracket_level == 0) { + is_type = false; + can_be_type = true; + } + + continue; + case Token.OP_GENERICS_LT: + case Token.COMMA: + case Token.DOUBLE_COLON: + case -1: + if (bracket_level == 0) + can_be_type = true; + continue; + default: + can_be_type = is_type = false; + continue; + } + + case Token.OBJECT: + case Token.STRING: + case Token.BOOL: + case Token.DECIMAL: + case Token.FLOAT: + case Token.DOUBLE: + case Token.SBYTE: + case Token.BYTE: + case Token.SHORT: + case Token.USHORT: + case Token.INT: + case Token.UINT: + case Token.LONG: + case Token.ULONG: + case Token.CHAR: + case Token.VOID: + if (bracket_level == 0) + is_type = true; + continue; + + case Token.COMMA: + if (bracket_level == 0) { + bracket_level = 100; + can_be_type = is_type = false; + } + continue; + + case Token.OP_GENERICS_LT: + case Token.OPEN_BRACKET: + if (bracket_level++ == 0) + is_type = true; + continue; + + case Token.OP_GENERICS_GT: + case Token.CLOSE_BRACKET: + --bracket_level; + continue; + + case Token.INTERR_NULLABLE: + case Token.STAR: + if (bracket_level == 0) + is_type = true; + continue; + + case Token.REF: + case Token.OUT: + can_be_type = is_type = false; + continue; + + default: + return Token.OPEN_PARENS; + } + } + } + + public static bool IsValidIdentifier (string s) + { + if (s == null || s.Length == 0) + return false; + + if (!is_identifier_start_character (s [0])) + return false; + + for (int i = 1; i < s.Length; i ++) + if (! is_identifier_part_character (s [i])) + return false; + + return true; + } + + Stack> genericDimensionLocations = new Stack> (); + + public List GenericDimensionLocations { + get { + if (genericDimensionLocations.Count == 0) + return null; + return genericDimensionLocations.Pop (); + } + } + + bool parse_less_than (ref int genericDimension) + { + genericDimensionLocations.Push (new List ()); + genericDimensionLocations.Peek ().Add (Location); + start: + int the_token = token (); + if (the_token == Token.OPEN_BRACKET) { + while (true) { + the_token = token (); + if (the_token == Token.EOF) + return true; + + if (the_token == Token.CLOSE_BRACKET) + break; + } + the_token = token (); + } else if (the_token == Token.IN || the_token == Token.OUT) { + the_token = token (); + } + switch (the_token) { + case Token.IDENTIFIER: + case Token.OBJECT: + case Token.STRING: + case Token.BOOL: + case Token.DECIMAL: + case Token.FLOAT: + case Token.DOUBLE: + case Token.SBYTE: + case Token.BYTE: + case Token.SHORT: + case Token.USHORT: + case Token.INT: + case Token.UINT: + case Token.LONG: + case Token.ULONG: + case Token.CHAR: + case Token.VOID: + break; + case Token.OP_GENERICS_GT: + genericDimension = 1; + genericDimensionLocations.Peek ().Add (Location); + return true; + case Token.IN: + case Token.OUT: + genericDimensionLocations.Pop (); + return true; + case Token.COMMA: + do { + ++genericDimension; + if (genericDimensionLocations.Count > 0) + genericDimensionLocations.Peek ().Add (Location); + the_token = token (); + } while (the_token == Token.COMMA); + + if (the_token == Token.OP_GENERICS_GT) { + ++genericDimension; + if (genericDimensionLocations.Count > 0) + genericDimensionLocations.Peek ().Add (Location); + return true; + } + + genericDimensionLocations.Pop (); + return false; + default: + genericDimensionLocations.Pop (); + return false; + } + again: + the_token = token (); + if (the_token == Token.OP_GENERICS_GT) { + genericDimensionLocations.Peek ().Add (Location); + return true; + } + else if (the_token == Token.COMMA || the_token == Token.DOT || the_token == Token.DOUBLE_COLON) + goto start; + else if (the_token == Token.INTERR_NULLABLE || the_token == Token.STAR) + goto again; + else if (the_token == Token.OP_GENERICS_LT) { + if (!parse_less_than (ref genericDimension)) { + genericDimensionLocations.Pop (); + return false; + } + goto again; + } else if (the_token == Token.OPEN_BRACKET) { + rank_specifiers: + the_token = token (); + if (the_token == Token.CLOSE_BRACKET) + goto again; + else if (the_token == Token.COMMA) + goto rank_specifiers; + genericDimensionLocations.Pop (); + return false; + } + + genericDimensionLocations.Pop (); + return false; + } + + + public int peek_token () + { + int the_token; + + PushPosition (); + sbag.Suppress = true; + the_token = token (); + sbag.Suppress = false; + PopPosition (); + + return the_token; + } + + // + // Tonizes `?' using custom disambiguous rules to return one + // of following tokens: INTERR_NULLABLE, OP_COALESCING, INTERR + // + // Tricky expression looks like: + // + // Foo ? a = x ? b : c; + // + int TokenizePossibleNullableType () + { + if (parsing_block == 0 || parsing_type > 0) + return Token.INTERR_NULLABLE; + + int d = peek_char (); + if (d == '?') { + get_char (); + return Token.OP_COALESCING; + } + + if (d == '.') { + return Token.INTERR_OPERATOR; + } + + if (d != ' ') { + if (d == ',' || d == ';' || d == '>') + return Token.INTERR_NULLABLE; + if (d == '*' || (d >= '0' && d <= '9')) + return Token.INTERR; + } + + PushPosition (); + current_token = Token.NONE; + int next_token; + int parens = 0; + int generics = 0; + + var nt = xtoken (); + switch (nt) { + case Token.DOT: + case Token.OPEN_BRACKET_EXPR: + next_token = Token.INTERR_OPERATOR; + break; + case Token.LITERAL: + case Token.TRUE: + case Token.FALSE: + case Token.NULL: + case Token.THIS: + case Token.NEW: + next_token = Token.INTERR; + break; + + case Token.SEMICOLON: + case Token.COMMA: + case Token.CLOSE_PARENS: + case Token.OPEN_BRACKET: + case Token.OP_GENERICS_GT: + case Token.INTERR: + case Token.OP_COALESCING: + case Token.COLON: + next_token = Token.INTERR_NULLABLE; + break; + + case Token.OPEN_PARENS: + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + next_token = -1; + ++parens; + break; + + case Token.OP_GENERICS_LT: + case Token.OP_GENERICS_LT_DECL: + case Token.GENERIC_DIMENSION: + next_token = -1; + ++generics; + break; + + default: + next_token = -1; + break; + } + + if (next_token == -1) { + switch (xtoken ()) { + case Token.COMMA: + case Token.SEMICOLON: + case Token.OPEN_BRACE: + case Token.IN: + next_token = Token.INTERR_NULLABLE; + break; + + case Token.COLON: + next_token = Token.INTERR; + break; + + case Token.OPEN_PARENS: + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + ++parens; + goto default; + + case Token.CLOSE_PARENS: + --parens; + goto default; + + case Token.OP_GENERICS_LT: + case Token.OP_GENERICS_LT_DECL: + case Token.GENERIC_DIMENSION: + ++generics; + goto default; + + default: + int ntoken; + int interrs = 1; + int colons = 0; + int braces = 0; + int brackets = 0; + // + // All shorcuts failed, do it hard way + // + while ((ntoken = xtoken ()) != Token.EOF) { + switch (ntoken) { + case Token.OPEN_BRACE: + ++braces; + continue; + case Token.OPEN_PARENS: + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + ++parens; + continue; + case Token.CLOSE_BRACE: + --braces; + continue; + case Token.OP_GENERICS_LT: + case Token.OP_GENERICS_LT_DECL: + case Token.GENERIC_DIMENSION: + ++generics; + continue; + case Token.OPEN_BRACKET: + case Token.OPEN_BRACKET_EXPR: + ++brackets; + continue; + case Token.CLOSE_BRACKET: + --brackets; + continue; + case Token.CLOSE_PARENS: + if (parens > 0) { + --parens; + continue; + } + + PopPosition (); + return Token.INTERR_NULLABLE; + + case Token.OP_GENERICS_GT: + if (generics > 0) { + --generics; + continue; + } + + PopPosition (); + return Token.INTERR_NULLABLE; + } + + if (braces != 0) + continue; + + if (ntoken == Token.SEMICOLON) + break; + + if (parens != 0) + continue; + + if (ntoken == Token.COMMA) { + if (generics != 0 || brackets != 0) + continue; + + PopPosition (); + return Token.INTERR_NULLABLE; + } + + if (ntoken == Token.COLON) { + if (++colons == interrs) + break; + continue; + } + + if (ntoken == Token.INTERR) { + ++interrs; + continue; + } + } + + next_token = colons != interrs && braces == 0 ? Token.INTERR_NULLABLE : Token.INTERR; + break; + } + } + + PopPosition (); + return next_token; + } + + bool decimal_digits (int c) + { + int d; + bool seen_digits = false; + + if (c != -1){ + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = (char) c; + } + + // + // We use peek_char2, because decimal_digits needs to do a + // 2-character look-ahead (5.ToString for example). + // + while ((d = peek_char2 ()) != -1){ + if (d >= '0' && d <= '9'){ + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = (char) d; + get_char (); + seen_digits = true; + } else + break; + } + + return seen_digits; + } + + static bool is_hex (int e) + { + return (e >= '0' && e <= '9') || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f'); + } + + static TypeCode real_type_suffix (int c) + { + switch (c){ + case 'F': case 'f': + return TypeCode.Single; + case 'D': case 'd': + return TypeCode.Double; + case 'M': case 'm': + return TypeCode.Decimal; + default: + return TypeCode.Empty; + } + } + + ILiteralConstant integer_type_suffix (ulong ul, int c, Location loc) + { + bool is_unsigned = false; + bool is_long = false; + + if (c != -1){ + bool scanning = true; + do { + switch (c){ + case 'U': case 'u': + if (is_unsigned) + scanning = false; + is_unsigned = true; + get_char (); + break; + + case 'l': + if (!is_unsigned){ + // + // if we have not seen anything in between + // report this error + // + Report.Warning (78, 4, Location, "The `l' suffix is easily confused with the digit `1' (use `L' for clarity)"); + } + + goto case 'L'; + + case 'L': + if (is_long) + scanning = false; + is_long = true; + get_char (); + break; + + default: + scanning = false; + break; + } + c = peek_char (); + } while (scanning); + } + + if (is_long && is_unsigned){ + return new ULongLiteral (context.BuiltinTypes, ul, loc); + } + + if (is_unsigned){ + // uint if possible, or ulong else. + + if ((ul & 0xffffffff00000000) == 0) + return new UIntLiteral (context.BuiltinTypes, (uint) ul, loc); + else + return new ULongLiteral (context.BuiltinTypes, ul, loc); + } else if (is_long){ + // long if possible, ulong otherwise + if ((ul & 0x8000000000000000) != 0) + return new ULongLiteral (context.BuiltinTypes, ul, loc); + else + return new LongLiteral (context.BuiltinTypes, (long) ul, loc); + } else { + // int, uint, long or ulong in that order + if ((ul & 0xffffffff00000000) == 0){ + uint ui = (uint) ul; + + if ((ui & 0x80000000) != 0) + return new UIntLiteral (context.BuiltinTypes, ui, loc); + else + return new IntLiteral (context.BuiltinTypes, (int) ui, loc); + } else { + if ((ul & 0x8000000000000000) != 0) + return new ULongLiteral (context.BuiltinTypes, ul, loc); + else + return new LongLiteral (context.BuiltinTypes, (long) ul, loc); + } + } + } + + // + // given `c' as the next char in the input decide whether + // we need to convert to a special type, and then choose + // the best representation for the integer + // + ILiteralConstant adjust_int (int c, Location loc) + { + try { + if (number_pos > 9){ + ulong ul = (uint) (number_builder [0] - '0'); + + for (int i = 1; i < number_pos; i++){ + ul = checked ((ul * 10) + ((uint)(number_builder [i] - '0'))); + } + + return integer_type_suffix (ul, c, loc); + } else { + uint ui = (uint) (number_builder [0] - '0'); + + for (int i = 1; i < number_pos; i++){ + ui = checked ((ui * 10) + ((uint)(number_builder [i] - '0'))); + } + + return integer_type_suffix (ui, c, loc); + } + } catch (OverflowException) { + Error_NumericConstantTooLong (); + return new IntLiteral (context.BuiltinTypes, 0, loc); + } + catch (FormatException) { + Report.Error (1013, Location, "Invalid number"); + return new IntLiteral (context.BuiltinTypes, 0, loc); + } + } + + ILiteralConstant adjust_real (TypeCode t, Location loc) + { + string s = new string (number_builder, 0, number_pos); + const string error_details = "Floating-point constant is outside the range of type `{0}'"; + + switch (t){ + case TypeCode.Decimal: + try { + return new DecimalLiteral (context.BuiltinTypes, decimal.Parse (s, styles, csharp_format_info), loc); + } catch (OverflowException) { + Report.Error (594, Location, error_details, "decimal"); + return new DecimalLiteral (context.BuiltinTypes, 0, loc); + } + case TypeCode.Single: + try { + return new FloatLiteral (context.BuiltinTypes, float.Parse (s, styles, csharp_format_info), loc); + } catch (OverflowException) { + Report.Error (594, Location, error_details, "float"); + return new FloatLiteral (context.BuiltinTypes, 0, loc); + } + default: + try { + return new DoubleLiteral (context.BuiltinTypes, double.Parse (s, styles, csharp_format_info), loc); + } catch (OverflowException) { + Report.Error (594, loc, error_details, "double"); + return new DoubleLiteral (context.BuiltinTypes, 0, loc); + } + } + } + + ILiteralConstant handle_hex (Location loc) + { + int d; + ulong ul; + + get_char (); + while ((d = peek_char ()) != -1){ + if (is_hex (d)){ + number_builder [number_pos++] = (char) d; + get_char (); + } else + break; + } + + string s = new String (number_builder, 0, number_pos); + + try { + if (number_pos <= 8) + ul = System.UInt32.Parse (s, NumberStyles.HexNumber); + else + ul = System.UInt64.Parse (s, NumberStyles.HexNumber); + + return integer_type_suffix (ul, peek_char (), loc); + } catch (OverflowException){ + Error_NumericConstantTooLong (); + return new IntLiteral (context.BuiltinTypes, 0, loc); + } + catch (FormatException) { + Report.Error (1013, Location, "Invalid number"); + return new IntLiteral (context.BuiltinTypes, 0, loc); + } + } + + // + // Invoked if we know we have .digits or digits + // + int is_number (int c, bool dotLead) + { + ILiteralConstant res; + +#if FULL_AST + int read_start = reader.Position - 1; + if (dotLead) { + // + // Caller did peek_char + // + --read_start; + } +#endif + number_pos = 0; + var loc = Location; + bool hasLeadingDot = c == '.'; + + if (!dotLead){ + if (c == '0'){ + int peek = peek_char (); + + if (peek == 'x' || peek == 'X') { + val = res = handle_hex (loc); +#if FULL_AST + res.ParsedValue = reader.ReadChars (read_start, reader.Position - 1); +#endif + + return Token.LITERAL; + } + } + decimal_digits (c); + c = peek_char (); + } + + // + // We need to handle the case of + // "1.1" vs "1.string" (LITERAL_FLOAT vs NUMBER DOT IDENTIFIER) + // + bool is_real = false; + if (c == '.'){ + if (!dotLead) + get_char (); + + if (decimal_digits ('.')){ + is_real = true; + c = peek_char (); + } else { + putback ('.'); + number_pos--; + val = res = adjust_int (-1, loc); +#if FULL_AST + res.ParsedValue = reader.ReadChars (read_start, reader.Position - 1); +#endif + return Token.LITERAL; + } + } + + if (c == 'e' || c == 'E'){ + is_real = true; + get_char (); + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = (char) c; + c = get_char (); + + if (c == '+'){ + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = '+'; + c = -1; + } else if (c == '-') { + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = '-'; + c = -1; + } else { + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = '+'; + } + + decimal_digits (c); + c = peek_char (); + } + + var type = real_type_suffix (c); + if (type == TypeCode.Empty && !is_real) { + res = adjust_int (c, loc); + } else { + is_real = true; + + if (type != TypeCode.Empty) { + get_char (); + } + + res = adjust_real (type, loc); + } + + val = res; +#if FULL_AST + var chars = reader.ReadChars (read_start, reader.Position - (type == TypeCode.Empty && c > 0 ? 1 : 0)); + if (chars[chars.Length - 1] == '\r') + Array.Resize (ref chars, chars.Length - 1); + res.ParsedValue = chars; +#endif + + return Token.LITERAL; + } + + // + // Accepts exactly count (4 or 8) hex, no more no less + // + int getHex (int count, out int surrogate, out bool error) + { + int i; + int total = 0; + int c; + int top = count != -1 ? count : 4; + + get_char (); + error = false; + surrogate = 0; + for (i = 0; i < top; i++){ + c = get_char (); + + if (c >= '0' && c <= '9') + c = (int) c - (int) '0'; + else if (c >= 'A' && c <= 'F') + c = (int) c - (int) 'A' + 10; + else if (c >= 'a' && c <= 'f') + c = (int) c - (int) 'a' + 10; + else { + error = true; + return 0; + } + + total = (total * 16) + c; + if (count == -1){ + int p = peek_char (); + if (p == -1) + break; + if (!is_hex ((char)p)) + break; + } + } + + if (top == 8) { + if (total > 0x0010FFFF) { + error = true; + return 0; + } + + if (total >= 0x00010000) { + surrogate = ((total - 0x00010000) % 0x0400 + 0xDC00); + total = ((total - 0x00010000) / 0x0400 + 0xD800); + } + } + + return total; + } + + int escape (int c, out int surrogate) + { + bool error; + int d; + int v; + + d = peek_char (); + if (c != '\\') { + surrogate = 0; + return c; + } + + switch (d){ + case 'a': + v = '\a'; break; + case 'b': + v = '\b'; break; + case 'n': + v = '\n'; break; + case 't': + v = '\t'; break; + case 'v': + v = '\v'; break; + case 'r': + v = '\r'; break; + case '\\': + v = '\\'; break; + case 'f': + v = '\f'; break; + case '0': + v = 0; break; + case '"': + v = '"'; break; + case '\'': + v = '\''; break; + case 'x': + v = getHex (-1, out surrogate, out error); + if (error) + goto default; + return v; + case 'u': + case 'U': + return EscapeUnicode (d, out surrogate); + default: + surrogate = 0; + Report.Error (1009, Location, "Unrecognized escape sequence `\\{0}'", ((char)d).ToString ()); + return d; + } + + get_char (); + surrogate = 0; + return v; + } + + int EscapeUnicode (int ch, out int surrogate) + { + bool error; + if (ch == 'U') { + ch = getHex (8, out surrogate, out error); + } else { + ch = getHex (4, out surrogate, out error); + } + + if (error) + Report.Error (1009, Location, "Unrecognized escape sequence"); + + return ch; + } + + int get_char () + { + int x; + if (putback_char != -1) { + x = putback_char; + putback_char = -1; + } else { + x = reader.Read (); + } + + if (x <= 13) { + if (x == '\r') { + if (peek_char () == '\n') { + putback_char = -1; + advance_line (SpecialsBag.NewLine.Windows); + } else { + advance_line (SpecialsBag.NewLine.Unix); + } + + x = '\n'; + } else if (x == '\n') { + advance_line (SpecialsBag.NewLine.Unix); + } else { + col++; + } + } else if (x >= UnicodeLS && x <= UnicodePS) { + advance_line (SpecialsBag.NewLine.Unix); + } else { + col++; + } + + return x; + } + + bool recordNewLine = true; + void advance_line (SpecialsBag.NewLine newLine) + { + if (recordNewLine) + sbag.AddNewLine (line, col, newLine); + line++; + ref_line++; + previous_col = col; + col = 0; + startsLine = true; + } + + int peek_char () + { + if (putback_char == -1) + putback_char = reader.Read (); + return putback_char; + } + + int peek_char2 () + { + if (putback_char != -1) + return putback_char; + return reader.Peek (); + } + + public void putback (int c) + { + if (putback_char != -1) { + throw new InternalErrorException (string.Format ("Secondary putback [{0}] putting back [{1}] is not allowed", (char)putback_char, (char) c), Location); + } + + if (c == '\n' || col == 0 || (c >= UnicodeLS && c <= UnicodePS)) { + // It won't happen though. + line--; + ref_line--; + col = previous_col; + } + else + col--; + putback_char = c; + } + + public bool advance () + { + return peek_char () != -1 || CompleteOnEOF; + } + + public Object Value { + get { + return val; + } + } + + public Object value () + { + return val; + } + + public int token () + { + current_token = xtoken (); + return current_token; + } + + int TokenizePreprocessorIdentifier (out int c) + { + int startCol, endLine, endCol; + return TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol); + } + + int TokenizePreprocessorIdentifier (out int c, out int startCol, out int endLine, out int endCol) + { + // skip over white space + do { + endLine = line; + endCol = col; + c = get_char (); + } while (c == ' ' || c == '\t'); + startCol = col; + int pos = 0; + while (c != -1 && c >= 'a' && c <= 'z') { + id_builder[pos++] = (char) c; + endCol = col + 1; + c = get_char (); + if (c == '\\') { + int peek = peek_char (); + if (peek == 'U' || peek == 'u') { + int surrogate; + c = EscapeUnicode (c, out surrogate); + if (surrogate != 0) { + if (is_identifier_part_character ((char) c)) { + id_builder[pos++] = (char) c; + } + c = surrogate; + } + } + } + } + + return pos; + } + + PreprocessorDirective get_cmd_arg (out string arg) + { + int c; + int startLine = line, startCol = col; + + tokens_seen = false; + arg = ""; + + int startCol2, endLine, endCol; + var cmd = GetPreprocessorDirective (id_builder, TokenizePreprocessorIdentifier (out c, out startCol2, out endLine, out endCol)); + + if ((cmd & PreprocessorDirective.CustomArgumentsParsing) != 0) { + if (position_stack.Count == 0) + sbag.AddPreProcessorDirective (startLine, startCol, line, col, cmd, null); + return cmd; + } + + // skip over white space + while (c == ' ' || c == '\t') { + c = get_char (); + } + int has_identifier_argument = (int)(cmd & PreprocessorDirective.RequiresArgument); + int pos = 0; + while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS) { + if (c == '\\' && has_identifier_argument >= 0) { + if (has_identifier_argument != 0) { + has_identifier_argument = 1; + + int peek = peek_char (); + if (peek == 'U' || peek == 'u') { + int surrogate; + c = EscapeUnicode (c, out surrogate); + if (surrogate != 0) { + if (is_identifier_part_character ((char)c)) { + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder [pos++] = (char)c; + } + c = surrogate; + } + } + } else { + has_identifier_argument = -1; + } + } else if (c == '/' && peek_char () == '/') { + // + // Eat single-line comments + // + get_char (); + ReadToEndOfLine (); + break; + } + + endLine = line; + endCol = col + 1; + + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder[pos++] = (char) c; + c = get_char (); + } + + if (pos != 0) { + if (pos > MaxIdentifierLength) + arg = new string (value_builder, 0, pos); + else + arg = InternIdentifier (value_builder, pos); + + // Eat any trailing whitespaces + arg = arg.Trim (simple_whitespaces); + } + if (position_stack.Count == 0) + sbag.AddPreProcessorDirective (startLine, startCol, endLine, endCol, cmd, arg); + + return cmd; + } + + // + // Handles the #line directive + // + bool PreProcessLine () + { + Location loc = Location; + #if FULL_AST + var lineDirective = sbag.GetCurrentLineProcessorDirective(); + #endif + + int c; + + int length = TokenizePreprocessorIdentifier (out c); + if (length == line_default.Length) { + if (!IsTokenIdentifierEqual (line_default)) + return false; + + current_source = source_file.SourceFile; + if (!hidden_block_start.IsNull) { + current_source.RegisterHiddenScope (hidden_block_start, loc); + hidden_block_start = Location.Null; + } + + //ref_line = line; + return true; + } + + if (length == line_hidden.Length) { + if (!IsTokenIdentifierEqual (line_hidden)) + return false; + + if (hidden_block_start.IsNull) + hidden_block_start = loc; + + return true; + } + + if (length != 0 || c < '0' || c > '9') { + // + // Eat any remaining characters to continue parsing on next line + // + ReadToEndOfLine (); + return false; + } + + int new_line = TokenizeNumber (c); + if (new_line < 1) { + // + // Eat any remaining characters to continue parsing on next line + // + ReadToEndOfLine (); + return new_line != 0; + } + #if FULL_AST + lineDirective.LineNumber = new_line; + #endif + + c = get_char (); + if (c == ' ') { + // skip over white space + do { + c = get_char (); + } while (c == ' ' || c == '\t'); + } else if (c == '"') { + c = 0; + } + + if (c != '\n' && c != '/' && c != '"' && c != UnicodeLS && c != UnicodePS) { + // + // Eat any remaining characters to continue parsing on next line + // + ReadToEndOfLine (); + + Report.Error (1578, loc, "Filename, single-line comment or end-of-line expected"); + return true; + } + + string new_file_name = null; + if (c == '"') { + new_file_name = TokenizeFileName (ref c); + #if FULL_AST + lineDirective.FileName = new_file_name; + #endif + + // skip over white space + while (c == ' ' || c == '\t') { + c = get_char (); + } + } + + if (c == '\n' || c == UnicodeLS || c == UnicodePS) { + + } else if (c == '/') { + ReadSingleLineComment (); + } else { + // + // Eat any remaining characters to continue parsing on next line + // + ReadToEndOfLine (); + + Error_EndLineExpected (); + return true; + } + + if (new_file_name != null) { + current_source = context.LookupFile (source_file, new_file_name); + source_file.AddIncludeFile (current_source); + } + + if (!hidden_block_start.IsNull) { + current_source.RegisterHiddenScope (hidden_block_start, loc); + hidden_block_start = Location.Null; + } + + //ref_line = new_line; + return true; + } + + // + // Handles #define and #undef + // + void PreProcessDefinition (bool is_define, string ident, bool caller_is_taking) + { + if (ident.Length == 0 || ident == "true" || ident == "false"){ + Report.Error (1001, Location, "Missing identifier to pre-processor directive"); + return; + } + + if (ident.IndexOfAny (simple_whitespaces) != -1){ + Error_EndLineExpected (); + return; + } + + if (!is_identifier_start_character (ident [0])) + Report.Error (1001, Location, "Identifier expected: {0}", ident); + + foreach (char c in ident.Substring (1)){ + if (!is_identifier_part_character (c)){ + Report.Error (1001, Location, "Identifier expected: {0}", ident); + return; + } + } + + if (!caller_is_taking) + return; + + if (is_define) { + // + // #define ident + // + if (context.Settings.IsConditionalSymbolDefined (ident)) + return; + + source_file.AddDefine (ident); + } else { + // + // #undef ident + // + source_file.AddUndefine (ident); + } + } + + byte read_hex (out bool error) + { + int total; + int c = get_char (); + + if ((c >= '0') && (c <= '9')) + total = (int) c - (int) '0'; + else if ((c >= 'A') && (c <= 'F')) + total = (int) c - (int) 'A' + 10; + else if ((c >= 'a') && (c <= 'f')) + total = (int) c - (int) 'a' + 10; + else { + error = true; + return 0; + } + + total *= 16; + c = get_char (); + + if ((c >= '0') && (c <= '9')) + total += (int) c - (int) '0'; + else if ((c >= 'A') && (c <= 'F')) + total += (int) c - (int) 'A' + 10; + else if ((c >= 'a') && (c <= 'f')) + total += (int) c - (int) 'a' + 10; + else { + error = true; + return 0; + } + + error = false; + return (byte) total; + } + + // + // Parses #pragma checksum + // + bool ParsePragmaChecksum () + { + // + // The syntax is ` "foo.txt" "{guid}" "hash"' + // + // guid is predefined hash algorithm guid {406ea660-64cf-4c82-b6f0-42d48172a799} for md5 + // + int c = get_char (); + + if (c != '"') + return false; + + string file_name = TokenizeFileName (ref c); + + // TODO: Any white-spaces count + if (c != ' ') + return false; + + SourceFile file = context.LookupFile (source_file, file_name); + + if (get_char () != '"' || get_char () != '{') + return false; + + bool error; + byte[] guid_bytes = new byte [16]; + int i = 0; + + for (; i < 4; i++) { + guid_bytes [i] = read_hex (out error); + if (error) + return false; + } + + if (get_char () != '-') + return false; + + for (; i < 10; i++) { + guid_bytes [i] = read_hex (out error); + if (error) + return false; + + guid_bytes [i++] = read_hex (out error); + if (error) + return false; + + if (get_char () != '-') + return false; + } + + for (; i < 16; i++) { + guid_bytes [i] = read_hex (out error); + if (error) + return false; + } + + if (get_char () != '}' || get_char () != '"') + return false; + + // TODO: Any white-spaces count + c = get_char (); + if (c != ' ') + return false; + + if (get_char () != '"') + return false; + + // Any length of checksum + List checksum_bytes = new List (16); + + var checksum_location = Location; + c = peek_char (); + while (c != '"' && c != -1) { + checksum_bytes.Add (read_hex (out error)); + if (error) + return false; + + c = peek_char (); + } + + if (c == '/') { + ReadSingleLineComment (); + } else if (get_char () != '"') { + return false; + } + + if (context.Settings.GenerateDebugInfo) { + var chsum = checksum_bytes.ToArray (); + + if (file.HasChecksum) { + if (!ArrayComparer.IsEqual (file.Checksum, chsum)) { + // TODO: Report.SymbolRelatedToPreviousError + Report.Warning (1697, 1, checksum_location, "Different checksum values specified for file `{0}'", file.Name); + } + } + + file.SetChecksum (guid_bytes, chsum); + current_source.AutoGenerated = true; + } + + return true; + } + + bool IsTokenIdentifierEqual (char[] identifier) + { + for (int i = 0; i < identifier.Length; ++i) { + if (identifier[i] != id_builder[i]) + return false; + } + + return true; + } + + int TokenizeNumber (int value) + { + number_pos = 0; + + decimal_digits (value); + uint ui = (uint) (number_builder[0] - '0'); + + try { + for (int i = 1; i < number_pos; i++) { + ui = checked ((ui * 10) + ((uint) (number_builder[i] - '0'))); + } + + return (int) ui; + } catch (OverflowException) { + Error_NumericConstantTooLong (); + return -1; + } + } + + string TokenizeFileName (ref int c) + { + var string_builder = new StringBuilder (); + while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS) { + c = get_char (); + if (c == '"') { + c = get_char (); + break; + } + + string_builder.Append ((char) c); + } + + if (string_builder.Length == 0) { + Report.Warning (1709, 1, Location, "Filename specified for preprocessor directive is empty"); + } + + + return string_builder.ToString (); + } + + int TokenizePragmaNumber (ref int c) + { + number_pos = 0; + + int number; + + if (c >= '0' && c <= '9') { + number = TokenizeNumber (c); + + c = get_char (); + + // skip over white space + while (c == ' ' || c == '\t') + c = get_char (); + + if (c == ',') { + c = get_char (); + } + + // skip over white space + while (c == ' ' || c == '\t') + c = get_char (); + } else { + number = -1; + if (c == '/') { + ReadSingleLineComment (); + } else { + Report.Warning (1692, 1, Location, "Invalid number"); + + // Read everything till the end of the line or file + ReadToEndOfLine (); + } + } + + return number; + } + + void ReadToEndOfLine () + { + int c; + do { + c = get_char (); + } while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS); + } + + void ReadSingleLineComment () + { + if (peek_char () != '/') + Report.Warning (1696, 1, Location, "Single-line comment or end-of-line expected"); + if (position_stack.Count == 0) + sbag.StartComment (SpecialsBag.CommentType.Single, startsLine, line, col - 1); + // Read everything till the end of the line or file + int c; + do { + c = get_char (); + if (position_stack.Count == 0) + sbag.PushCommentChar (c); + var pc = peek_char (); + if ((pc == '\n' || pc == -1 || pc == UnicodeLS || pc == UnicodePS) && position_stack.Count == 0) + sbag.EndComment (line, col + 1); + } while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS); + } + + /// + /// Handles #pragma directive + /// + void ParsePragmaDirective () + { + int c; + int startCol, endLine, endCol; + int length = TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol); + #if FULL_AST + var pragmaDirective = sbag.GetPragmaPreProcessorDirective(); + if (pragmaDirective != null) + pragmaDirective.WarningColumn = startCol; + #endif + if (length == pragma_warning.Length && IsTokenIdentifierEqual (pragma_warning)) { + length = TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol); + #if FULL_AST + if (pragmaDirective != null) + pragmaDirective.DisableRestoreColumn = startCol; + #endif + + // + // #pragma warning disable + // #pragma warning restore + // + if (length == pragma_warning_disable.Length) { + bool disable = IsTokenIdentifierEqual (pragma_warning_disable); + #if FULL_AST + if (pragmaDirective != null) + pragmaDirective.Disalbe = disable; + #endif + if (disable || IsTokenIdentifierEqual (pragma_warning_restore)) { + // skip over white space + while (c == ' ' || c == '\t') + c = get_char (); + + var loc = Location; + + if (c == '\n' || c == '/' || c == UnicodeLS || c == UnicodePS) { + if (c == '/') + ReadSingleLineComment (); + + // + // Disable/Restore all warnings + // + if (disable) { + Report.RegisterWarningRegion (loc).WarningDisable (loc.Row); + } else { + Report.RegisterWarningRegion (loc).WarningEnable (loc.Row); + } + } else { + // + // Disable/Restore a warning or group of warnings + // + int code; + do { + var startLoc = loc; + #if FULL_AST + // int read_start = reader.Position; + #endif + code = TokenizePragmaNumber (ref c); + if (code > 0) { + #if FULL_AST + var literal = new IntConstant(context.BuiltinTypes, code, startLoc); + if (pragmaDirective != null) + pragmaDirective.Codes.Add (literal); + // literal.ParsedValue = reader.ReadChars (read_start, reader.Position + 1); + #endif + if (disable) { + Report.RegisterWarningRegion (loc).WarningDisable (loc, code, context.Report); + } else { + Report.RegisterWarningRegion (loc).WarningEnable (loc, code, context); + } + } + } while (code >= 0 && c != '\n' && c != -1 && c != UnicodeLS && c != UnicodePS); + } + + return; + } + } + + Report.Warning (1634, 1, Location, "Expected disable or restore"); + + // Eat any remaining characters on the line + ReadToEndOfLine (); + + return; + } + + + // + // #pragma checksum + // + if (length == pragma_checksum.Length && IsTokenIdentifierEqual (pragma_checksum)) { + if (c != ' ' || !ParsePragmaChecksum ()) { + Report.Warning (1695, 1, Location, + "Invalid #pragma checksum syntax. Expected \"filename\" \"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\" \"XXXX...\""); + } + + return; + } + + Report.Warning (1633, 1, Location, "Unrecognized #pragma directive"); + } + + bool eval_val (string s) + { + if (s == "true") + return true; + if (s == "false") + return false; + + return source_file.IsConditionalDefined (s); + } + + bool pp_primary (ref string s) + { + s = s.Trim (); + int len = s.Length; + + if (len > 0){ + char c = s [0]; + + if (c == '('){ + s = s.Substring (1); + bool val = pp_expr (ref s, false); + if (s.Length > 0 && s [0] == ')'){ + s = s.Substring (1); + return val; + } + Error_InvalidDirective (); + return false; + } + + if (is_identifier_start_character (c)){ + int j = 1; + + while (j < len){ + c = s [j]; + + if (is_identifier_part_character (c)){ + j++; + continue; + } + bool v = eval_val (s.Substring (0, j)); + s = s.Substring (j); + return v; + } + bool vv = eval_val (s); + s = ""; + return vv; + } + } + Error_InvalidDirective (); + return false; + } + + bool pp_unary (ref string s) + { + s = s.Trim (); + int len = s.Length; + + if (len > 0){ + if (s [0] == '!'){ + if (len > 1 && s [1] == '='){ + Error_InvalidDirective (); + return false; + } + s = s.Substring (1); + return ! pp_primary (ref s); + } else + return pp_primary (ref s); + } else { + Error_InvalidDirective (); + return false; + } + } + + bool pp_eq (ref string s) + { + bool va = pp_unary (ref s); + + s = s.Trim (); + int len = s.Length; + if (len > 0){ + if (s [0] == '='){ + if (len > 2 && s [1] == '='){ + s = s.Substring (2); + return va == pp_unary (ref s); + } else { + Error_InvalidDirective (); + return false; + } + } else if (s [0] == '!' && len > 1 && s [1] == '='){ + s = s.Substring (2); + + return va != pp_unary (ref s); + + } + } + + return va; + + } + + bool pp_and (ref string s) + { + bool va = pp_eq (ref s); + + s = s.Trim (); + int len = s.Length; + if (len > 0){ + if (s [0] == '&'){ + if (len > 2 && s [1] == '&'){ + s = s.Substring (2); + return (va & pp_and (ref s)); + } else { + Error_InvalidDirective (); + return false; + } + } + } + return va; + } + + // + // Evaluates an expression for `#if' or `#elif' + // + bool pp_expr (ref string s, bool isTerm) + { + bool va = pp_and (ref s); + s = s.Trim (); + int len = s.Length; + if (len > 0){ + char c = s [0]; + + if (c == '|'){ + if (len > 2 && s [1] == '|'){ + s = s.Substring (2); + return va | pp_expr (ref s, isTerm); + } else { + Error_InvalidDirective (); + return false; + } + } + if (isTerm) { + Error_EndLineExpected (); + return false; + } + } + + return va; + } + + bool eval (string s) + { + bool v = pp_expr (ref s, true); + s = s.Trim (); + if (s.Length != 0){ + return false; + } + + return v; + } + + void Error_NumericConstantTooLong () + { + Report.Error (1021, Location, "Integral constant is too large"); + } + + void Error_InvalidDirective () + { + Report.Error (1517, Location, "Invalid preprocessor directive"); + } + + void Error_UnexpectedDirective (string extra) + { + Report.Error ( + 1028, Location, + "Unexpected processor directive ({0})", extra); + } + + void Error_TokensSeen () + { + Report.Error (1032, Location, + "Cannot define or undefine preprocessor symbols after first token in file"); + } + + void Eror_WrongPreprocessorLocation () + { + Report.Error (1040, Location, + "Preprocessor directives must appear as the first non-whitespace character on a line"); + } + + void Error_EndLineExpected () + { + Report.Error (1025, Location, "Single-line comment or end-of-line expected"); + } + + // + // Raises a warning when tokenizer found documentation comment + // on unexpected place + // + void WarningMisplacedComment (Location loc) + { + if (doc_state != XmlCommentState.Error) { + doc_state = XmlCommentState.Error; + Report.Warning (1587, 2, loc, "XML comment is not placed on a valid language element"); + } + } + + // + // if true, then the code continues processing the code + // if false, the code stays in a loop until another directive is + // reached. + // When caller_is_taking is false we ignore all directives except the ones + // which can help us to identify where the #if block ends + bool ParsePreprocessingDirective (bool caller_is_taking) + { + string arg; + bool region_directive = false; + + var directive = get_cmd_arg (out arg); + + // + // The first group of pre-processing instructions is always processed + // + switch (directive) { + case PreprocessorDirective.Region: + region_directive = true; + arg = "true"; + goto case PreprocessorDirective.If; + + case PreprocessorDirective.Endregion: + if (ifstack == null || ifstack.Count == 0){ + Error_UnexpectedDirective ("no #region for this #endregion"); + return true; + } + int pop = ifstack.Pop (); + + if ((pop & REGION) == 0) + Report.Error (1027, Location, "Expected `#endif' directive"); + + return caller_is_taking; + + case PreprocessorDirective.If: + if (ifstack == null) + ifstack = new Stack (2); + + int flags = region_directive ? REGION : 0; + if (ifstack.Count == 0){ + flags |= PARENT_TAKING; + } else { + int state = ifstack.Peek (); + if ((state & TAKING) != 0) { + flags |= PARENT_TAKING; + } + } + + if (eval (arg) && caller_is_taking) { + ifstack.Push (flags | TAKING); + return true; + } + sbag.SkipIf (); + ifstack.Push (flags); + return false; + + case PreprocessorDirective.Endif: + if (ifstack == null || ifstack.Count == 0){ + Error_UnexpectedDirective ("no #if for this #endif"); + return true; + } else { + pop = ifstack.Pop (); + + if ((pop & REGION) != 0) + Report.Error (1038, Location, "#endregion directive expected"); + + if (arg.Length != 0) { + Error_EndLineExpected (); + } + + if (ifstack.Count == 0) + return true; + + int state = ifstack.Peek (); + return (state & TAKING) != 0; + } + + case PreprocessorDirective.Elif: + if (ifstack == null || ifstack.Count == 0){ + Error_UnexpectedDirective ("no #if for this #elif"); + return true; + } else { + int state = ifstack.Pop (); + + if ((state & REGION) != 0) { + Report.Error (1038, Location, "#endregion directive expected"); + return true; + } + + if ((state & ELSE_SEEN) != 0){ + Error_UnexpectedDirective ("#elif not valid after #else"); + return true; + } + + if ((state & TAKING) != 0) { + sbag.SkipIf (); + ifstack.Push (0); + return false; + } + + if (eval (arg) && ((state & PARENT_TAKING) != 0)){ + ifstack.Push (state | TAKING); + return true; + } + + sbag.SkipIf (); + ifstack.Push (state); + return false; + } + + case PreprocessorDirective.Else: + if (ifstack == null || ifstack.Count == 0){ + Error_UnexpectedDirective ("no #if for this #else"); + return true; + } else { + int state = ifstack.Peek (); + + if ((state & REGION) != 0) { + Report.Error (1038, Location, "#endregion directive expected"); + return true; + } + + if ((state & ELSE_SEEN) != 0){ + Error_UnexpectedDirective ("#else within #else"); + return true; + } + + ifstack.Pop (); + + if (arg.Length != 0) { + Error_EndLineExpected (); + return true; + } + + bool ret = false; + if ((state & PARENT_TAKING) != 0) { + ret = (state & TAKING) == 0; + + if (ret) + state |= TAKING; + else + state &= ~TAKING; + } + + ifstack.Push (state | ELSE_SEEN); + + return ret; + } + case PreprocessorDirective.Define: + if (any_token_seen){ + if (caller_is_taking) + Error_TokensSeen (); + return caller_is_taking; + } + PreProcessDefinition (true, arg, caller_is_taking); + return caller_is_taking; + + case PreprocessorDirective.Undef: + if (any_token_seen){ + if (caller_is_taking) + Error_TokensSeen (); + return caller_is_taking; + } + PreProcessDefinition (false, arg, caller_is_taking); + return caller_is_taking; + + case PreprocessorDirective.Invalid: + Report.Error (1024, Location, "Wrong preprocessor directive"); + return true; + } + + // + // These are only processed if we are in a `taking' block + // + if (!caller_is_taking) + return false; + + switch (directive){ + case PreprocessorDirective.Error: + Report.Error (1029, Location, "#error: '{0}'", arg); + return true; + + case PreprocessorDirective.Warning: + Report.Warning (1030, 1, Location, "#warning: `{0}'", arg); + return true; + + case PreprocessorDirective.Pragma: + if (context.Settings.Version == LanguageVersion.ISO_1) { + Report.FeatureIsNotAvailable (context, Location, "#pragma"); + } + + ParsePragmaDirective (); + return true; + + case PreprocessorDirective.Line: + Location loc = Location; + if (!PreProcessLine ()) + Report.Error (1576, loc, "The line number specified for #line directive is missing or invalid"); + + return caller_is_taking; + } + + throw new NotImplementedException (directive.ToString ()); + } + + private int consume_string (bool quoted) + { + int c; + int pos = 0; + Location start_location = Location; + if (quoted) { + start_location = start_location - 1; + recordNewLine = false; + } + +#if FULL_AST + int reader_pos = reader.Position; +#endif + + while (true){ + // Cannot use get_char because of \r in quoted strings + if (putback_char != -1) { + c = putback_char; + putback_char = -1; + } else { + c = reader.Read (); + } + + if (c == '"') { + ++col; + + if (quoted && peek_char () == '"') { + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder[pos++] = (char) c; + get_char (); + continue; + } + + string s; + if (pos == 0) + s = string.Empty; + else if (pos <= 4) + s = InternIdentifier (value_builder, pos); + else + s = new string (value_builder, 0, pos); + + ILiteralConstant res = new StringLiteral (context.BuiltinTypes, s, start_location); + val = res; +#if FULL_AST + res.ParsedValue = quoted ? + reader.ReadChars (reader_pos - 2, reader.Position - 1) : + reader.ReadChars (reader_pos - 1, reader.Position); +#endif + recordNewLine = true; + return Token.LITERAL; + } + + if (c == '\n' || c == UnicodeLS || c == UnicodePS) { + if (!quoted) { + Report.Error (1010, Location, "Newline in constant"); + + + // Don't add \r to string literal + if (pos > 1 && value_builder [pos - 1] == '\r') { + advance_line (SpecialsBag.NewLine.Windows); + --pos; + } else { + advance_line (SpecialsBag.NewLine.Unix); + } + + val = new StringLiteral (context.BuiltinTypes, new string (value_builder, 0, pos), start_location); + recordNewLine = true; + return Token.LITERAL; + } + + advance_line (SpecialsBag.NewLine.Unix); + } else if (c == '\\' && !quoted) { + ++col; + int surrogate; + c = escape (c, out surrogate); + if (c == -1) { + recordNewLine = true; + return Token.ERROR; + } + if (surrogate != 0) { + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder[pos++] = (char) c; + c = surrogate; + } + } else if (c == -1) { + Report.Error (1039, Location, "Unterminated string literal"); + recordNewLine = true; + return Token.EOF; + } else { + ++col; + } + + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder[pos++] = (char) c; + } + recordNewLine = true; + } + + private int consume_identifier (int s) + { + int res = consume_identifier (s, false); + + if (doc_state == XmlCommentState.Allowed) + doc_state = XmlCommentState.NotAllowed; + startsLine = false; + return res; + } + + int consume_identifier (int c, bool quoted) + { + // + // This method is very performance sensitive. It accounts + // for approximately 25% of all parser time + // + + int pos = 0; + int column = col; + if (quoted) + --column; + + if (c == '\\') { + int surrogate; + c = escape (c, out surrogate); + if (surrogate != 0) { + id_builder [pos++] = (char) c; + c = surrogate; + } + } + + id_builder [pos++] = (char) c; + + try { + while (true) { + c = reader.Read (); + + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9')) { + id_builder [pos++] = (char) c; + continue; + } + + if (c < 0x80) { + if (c == '\\') { + int surrogate; + c = escape (c, out surrogate); + if (is_identifier_part_character ((char) c)) + id_builder[pos++] = (char) c; + + if (surrogate != 0) { + c = surrogate; + } + + continue; + } + } else if (is_identifier_part_character_slow_part ((char) c)) { + id_builder [pos++] = (char) c; + continue; + } + + putback_char = c; + break; + } + } catch (IndexOutOfRangeException) { + Report.Error (645, Location, "Identifier too long (limit is 512 chars)"); + --pos; + col += pos; + } + + col += pos - 1; + + // + // Optimization: avoids doing the keyword lookup + // on uppercase letters + // + if (id_builder [0] >= '_' && !quoted) { + int keyword = GetKeyword (id_builder, pos); + if (keyword != -1) { + val = ltb.Create (keyword == Token.AWAIT ? "await" : null, current_source, ref_line, column); + return keyword; + } + } + + string s = InternIdentifier (id_builder, pos); +#if FULL_AST + if (quoted) { + val = ltb.Create ("@" + s, current_source, ref_line, column - 1); + } else { + val = ltb.Create (s, current_source, ref_line, column); + } +#else + val = ltb.Create (s, current_source, ref_line, column); +#endif + if (quoted && parsing_attribute_section) + AddEscapedIdentifier (((LocatedToken) val).Location); + + return Token.IDENTIFIER; + } + + string InternIdentifier (char[] charBuffer, int length) + { + // + // Keep identifiers in an array of hashtables to avoid needless + // allocations + // + var identifiers_group = identifiers [length]; + string s; + if (identifiers_group != null) { + if (identifiers_group.TryGetValue (charBuffer, out s)) { + return s; + } + } else { + // TODO: this should be number of files dependant + // corlib compilation peaks at 1000 and System.Core at 150 + int capacity = length > 20 ? 10 : 100; + identifiers_group = new Dictionary (capacity, new IdentifiersComparer (length)); + identifiers [length] = identifiers_group; + } + + char[] chars = new char[length]; + Array.Copy (charBuffer, chars, length); + + s = new string (charBuffer, 0, length); + identifiers_group.Add (chars, s); + + return s; + } + + public int xtoken () + { + int d, c; + + // Whether we have seen comments on the current line + bool comments_seen = false; + while ((c = get_char ()) != -1) { + switch (c) { + case '\t': + col = ((col - 1 + tab_size) / tab_size) * tab_size; + continue; + + case ' ': + case '\f': + case '\v': + case 0xa0: + case 0: + case 0xFEFF: // Ignore BOM anywhere in the file + continue; + +/* This is required for compatibility with .NET + case 0xEF: + if (peek_char () == 0xBB) { + PushPosition (); + get_char (); + if (get_char () == 0xBF) + continue; + PopPosition (); + } + break; +*/ + case '\\': + tokens_seen = true; + return consume_identifier (c); + + case '{': + val = ltb.Create (current_source, ref_line, col); + return Token.OPEN_BRACE; + case '}': + val = ltb.Create (current_source, ref_line, col); + return Token.CLOSE_BRACE; + case '[': + // To block doccomment inside attribute declaration. + if (doc_state == XmlCommentState.Allowed) + doc_state = XmlCommentState.NotAllowed; + + val = ltb.Create (current_source, ref_line, col); + + if (parsing_block == 0 || lambda_arguments_parsing) + return Token.OPEN_BRACKET; + + int next = peek_char (); + switch (next) { + case ']': + case ',': + return Token.OPEN_BRACKET; + + case ' ': + case '\f': + case '\v': + case '\r': + case '\n': + case UnicodeLS: + case UnicodePS: + case '/': + next = peek_token (); + if (next == Token.COMMA || next == Token.CLOSE_BRACKET) + return Token.OPEN_BRACKET; + + return Token.OPEN_BRACKET_EXPR; + default: + return Token.OPEN_BRACKET_EXPR; + } + case ']': + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.CLOSE_BRACKET; + case '(': + val = ltb.Create (current_source, ref_line, col); + // + // An expression versions of parens can appear in block context only + // + if (parsing_block != 0 && !lambda_arguments_parsing) { + + // + // Optmize most common case where we know that parens + // is not special + // + switch (current_token) { + case Token.IDENTIFIER: + case Token.IF: + case Token.FOR: + case Token.FOREACH: + case Token.TYPEOF: + case Token.WHILE: + case Token.SWITCH: + case Token.USING: + case Token.DEFAULT: + case Token.DELEGATE: + case Token.OP_GENERICS_GT: + return Token.OPEN_PARENS; + } + + // Optimize using peek + int xx = peek_char (); + switch (xx) { + case '(': + case '\'': + case '"': + case '0': + case '1': + return Token.OPEN_PARENS; + } + + lambda_arguments_parsing = true; + PushPosition (); + d = TokenizeOpenParens (); + PopPosition (); + lambda_arguments_parsing = false; + return d; + } + + return Token.OPEN_PARENS; + case ')': + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.CLOSE_PARENS; + case ',': + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.COMMA; + case ';': + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.SEMICOLON; + case '~': + val = ltb.Create (current_source, ref_line, col); + return Token.TILDE; + case '?': + val = ltb.Create (current_source, ref_line, col); + return TokenizePossibleNullableType (); + case '<': + val = ltb.Create (current_source, ref_line, col); + if (parsing_generic_less_than++ > 0) + return Token.OP_GENERICS_LT; + + return TokenizeLessThan (); + + case '>': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + + if (d == '=') { + get_char (); + return Token.OP_GE; + } + + if (parsing_generic_less_than > 1 || (parsing_generic_less_than == 1 && d != '>')) { + parsing_generic_less_than--; + return Token.OP_GENERICS_GT; + } + + if (d == '>') { + get_char (); + d = peek_char (); + + if (d == '=') { + get_char (); + return Token.OP_SHIFT_RIGHT_ASSIGN; + } + return Token.OP_SHIFT_RIGHT; + } + + return Token.OP_GT; + + case '+': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '+') { + d = Token.OP_INC; + } else if (d == '=') { + d = Token.OP_ADD_ASSIGN; + } else { + return Token.PLUS; + } + get_char (); + return d; + + case '-': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '-') { + d = Token.OP_DEC; + } else if (d == '=') + d = Token.OP_SUB_ASSIGN; + else if (d == '>') + d = Token.OP_PTR; + else { + return Token.MINUS; + } + get_char (); + return d; + + case '!': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == '='){ + get_char (); + return Token.OP_NE; + } + return Token.BANG; + + case '=': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '=') { + get_char (); + return Token.OP_EQ; + } + if (d == '>') { + get_char (); + return Token.ARROW; + } + + return Token.ASSIGN; + + case '&': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '&') { + get_char (); + return Token.OP_AND; + } + if (d == '=') { + get_char (); + return Token.OP_AND_ASSIGN; + } + return Token.BITWISE_AND; + + case '|': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '|') { + get_char (); + return Token.OP_OR; + } + if (d == '=') { + get_char (); + return Token.OP_OR_ASSIGN; + } + return Token.BITWISE_OR; + + case '*': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == '='){ + get_char (); + return Token.OP_MULT_ASSIGN; + } + return Token.STAR; + + case '/': + d = peek_char (); + if (d == '='){ + val = ltb.Create (current_source, ref_line, col); + get_char (); + return Token.OP_DIV_ASSIGN; + } + // Handle double-slash comments. + if (d == '/') { + get_char (); + if (doc_processing) { + if (peek_char () == '/') { + get_char (); + // Don't allow ////. + if ((d = peek_char ()) != '/') { + if (position_stack.Count == 0) + sbag.PushCommentChar (d); + if (doc_state == XmlCommentState.Allowed) + handle_one_line_xml_comment (); + else if (doc_state == XmlCommentState.NotAllowed) + WarningMisplacedComment (Location - 3); + } + } else { + if (xml_comment_buffer.Length > 0) + doc_state = XmlCommentState.NotAllowed; + } + } else { + bool isDoc = peek_char () == '/'; + if (position_stack.Count == 0) + sbag.StartComment (isDoc ? SpecialsBag.CommentType.Documentation : SpecialsBag.CommentType.Single, startsLine, line, col - 1); + if (isDoc) + get_char (); + } + + d = peek_char (); + int endLine = line, endCol = col; + while ((d = get_char ()) != -1 && (d != '\n') && d != '\r' && d != UnicodePS && d != UnicodeLS) { + if (position_stack.Count == 0) + sbag.PushCommentChar (d); + endLine = line; + endCol = col; + } + if (position_stack.Count == 0) + sbag.EndComment (endLine, endCol + 1); + any_token_seen |= tokens_seen; + tokens_seen = false; + comments_seen = false; + continue; + } else if (d == '*'){ + if (position_stack.Count == 0) { + sbag.StartComment (SpecialsBag.CommentType.Multi, startsLine, line, col); + recordNewLine = false; + } + get_char (); + bool docAppend = false; + if (doc_processing && peek_char () == '*') { + int ch = get_char (); + // But when it is /**/, just do nothing. + if (peek_char () == '/') { + ch = get_char (); + if (position_stack.Count == 0) { + recordNewLine = true; + sbag.EndComment (line, col + 1); + } + continue; + } else { + if (position_stack.Count == 0) + sbag.PushCommentChar (ch); + } + if (doc_state == XmlCommentState.Allowed) + docAppend = true; + else if (doc_state == XmlCommentState.NotAllowed) { + WarningMisplacedComment (Location - 2); + } + } + + int current_comment_start = 0; + if (docAppend) { + current_comment_start = xml_comment_buffer.Length; + xml_comment_buffer.Append (Environment.NewLine); + } + + while ((d = get_char ()) != -1){ + if (d == '*' && peek_char () == '/'){ + get_char (); + if (position_stack.Count == 0) { + recordNewLine = true; + sbag.EndComment (line, col + 1); + } + comments_seen = true; + break; + } else { + if (position_stack.Count == 0) + sbag.PushCommentChar (d); + } + if (docAppend) + xml_comment_buffer.Append ((char) d); + + if (d == '\n' || d == UnicodeLS || d == UnicodePS){ + any_token_seen |= tokens_seen; + tokens_seen = false; + // + // Reset 'comments_seen' just to be consistent. + // It doesn't matter either way, here. + // + comments_seen = false; + } + } + + if (!comments_seen) + Report.Error (1035, Location, "End-of-file found, '*/' expected"); + + if (docAppend) + update_formatted_doc_comment (current_comment_start); + continue; + } + val = ltb.Create (current_source, ref_line, col); + return Token.DIV; + + case '%': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == '='){ + get_char (); + return Token.OP_MOD_ASSIGN; + } + return Token.PERCENT; + + case '^': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == '='){ + get_char (); + return Token.OP_XOR_ASSIGN; + } + return Token.CARRET; + + case ':': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == ':') { + get_char (); + return Token.DOUBLE_COLON; + } + return Token.COLON; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + tokens_seen = true; + return is_number (c, false); + + case '\n': // white space + case UnicodeLS: + case UnicodePS: + any_token_seen |= tokens_seen; + tokens_seen = false; + comments_seen = false; + continue; + + case '.': + tokens_seen = true; + d = peek_char (); + if (d >= '0' && d <= '9') + return is_number (c, true); + + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.DOT; + + case '#': + if (tokens_seen || comments_seen) { + Eror_WrongPreprocessorLocation(); + return Token.ERROR; + } + + if (ParsePreprocessingDirective(true)) + continue; + sbag.StartComment(SpecialsBag.CommentType.InactiveCode, false, line, 1); + recordNewLine = false; + bool directive_expected = false; + while ((c = get_char ()) != -1) { + if (col == 1) { + directive_expected = true; + } else if (!directive_expected) { + // TODO: Implement comment support for disabled code and uncomment this code +// if (c == '#') { +// Eror_WrongPreprocessorLocation (); +// return Token.ERROR; +// } + if (c != '#') + sbag.PushCommentChar (c); + continue; + } + + if (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\v' || c == UnicodeLS || c == UnicodePS) { + sbag.PushCommentChar (c); + continue; + } + + if (c == '#') { + var oldNL = recordNewLine; + recordNewLine = true; + var continueNormalLexing = ParsePreprocessingDirective(false); + recordNewLine = oldNL; + if (continueNormalLexing) + break; + sbag.StartComment(SpecialsBag.CommentType.InactiveCode, false, line, 1); + } + sbag.PushCommentChar (c); + directive_expected = false; + } + recordNewLine = true; + sbag.EndComment (line, col); + if (c != -1) { + tokens_seen = false; + continue; + } + + return Token.EOF; + + case '"': + return consume_string (false); + + case '\'': + return TokenizeBackslash (); + + case '@': + c = get_char (); + if (c == '"') { + tokens_seen = true; + return consume_string (true); + } + + if (is_identifier_start_character (c)){ + return consume_identifier (c, true); + } + + Report.Error (1646, Location, "Keyword, identifier, or string expected after verbatim specifier: @"); + return Token.ERROR; + + case EvalStatementParserCharacter: + return Token.EVAL_STATEMENT_PARSER; + case EvalCompilationUnitParserCharacter: + return Token.EVAL_COMPILATION_UNIT_PARSER; + case EvalUsingDeclarationsParserCharacter: + return Token.EVAL_USING_DECLARATIONS_UNIT_PARSER; + case DocumentationXref: + return Token.DOC_SEE; + } + + if (is_identifier_start_character (c)) { + tokens_seen = true; + return consume_identifier (c); + } + + if (char.IsWhiteSpace ((char) c)) + continue; + + Report.Error (1056, Location, "Unexpected character `{0}'", ((char) c).ToString ()); + } + + if (CompleteOnEOF){ + if (generated) + return Token.COMPLETE_COMPLETION; + + generated = true; + return Token.GENERATE_COMPLETION; + } + + + return Token.EOF; + } + + int TokenizeBackslash () + { +#if FULL_AST + int read_start = reader.Position; +#endif + Location start_location = Location; + int c = get_char (); + tokens_seen = true; + if (c == '\'') { + val = new CharLiteral (context.BuiltinTypes, (char) c, start_location); + Report.Error (1011, start_location, "Empty character literal"); + return Token.LITERAL; + } + + if (c == '\n' || c == UnicodeLS || c == UnicodePS) { + Report.Error (1010, start_location, "Newline in constant"); + return Token.ERROR; + } + + int d; + c = escape (c, out d); + if (c == -1) + return Token.ERROR; + if (d != 0) + throw new NotImplementedException (); + + ILiteralConstant res = new CharLiteral (context.BuiltinTypes, (char) c, start_location); + val = res; + c = get_char (); + + if (c != '\'') { + Report.Error (1012, start_location, "Too many characters in character literal"); + + // Try to recover, read until newline or next "'" + while ((c = get_char ()) != -1) { + if (c == '\n' || c == '\'' || c == UnicodeLS || c == UnicodePS) + break; + } + } + +#if FULL_AST + res.ParsedValue = reader.ReadChars (read_start - 1, reader.Position); +#endif + + return Token.LITERAL; + } + + int TokenizeLessThan () + { + int d; + + // Save current position and parse next token. + PushPosition (); + int generic_dimension = 0; + if (parse_less_than (ref generic_dimension)) { + if (parsing_generic_declaration && (parsing_generic_declaration_doc || token () != Token.DOT)) { + d = Token.OP_GENERICS_LT_DECL; + } else { + if (generic_dimension > 0) { + val = generic_dimension; + DiscardPosition (); + return Token.GENERIC_DIMENSION; + } + + d = Token.OP_GENERICS_LT; + } + PopPosition (); + return d; + } + + PopPosition (); + parsing_generic_less_than = 0; + + d = peek_char (); + if (d == '<') { + get_char (); + d = peek_char (); + + if (d == '=') { + get_char (); + return Token.OP_SHIFT_LEFT_ASSIGN; + } + return Token.OP_SHIFT_LEFT; + } + + if (d == '=') { + get_char (); + return Token.OP_LE; + } + return Token.OP_LT; + } + + // + // Handles one line xml comment + // + private void handle_one_line_xml_comment () + { + int c; + while ((c = peek_char ()) == ' ') { + if (position_stack.Count == 0) + sbag.PushCommentChar (c); + get_char (); // skip heading whitespaces. + } + while ((c = peek_char ()) != -1 && c != '\n' && c != '\r') { + if (position_stack.Count == 0) + sbag.PushCommentChar (c); + xml_comment_buffer.Append ((char) get_char ()); + } + if (c == '\r' || c == '\n') + xml_comment_buffer.Append (Environment.NewLine); + } + + // + // Remove heading "*" in Javadoc-like xml documentation. + // + private void update_formatted_doc_comment (int current_comment_start) + { + int length = xml_comment_buffer.Length - current_comment_start; + string [] lines = xml_comment_buffer.ToString ( + current_comment_start, + length).Replace ("\r", "").Split ('\n'); + + // The first line starts with /**, thus it is not target + // for the format check. + for (int i = 1; i < lines.Length; i++) { + string s = lines [i]; + int idx = s.IndexOf ('*'); + string head = null; + if (idx < 0) { + if (i < lines.Length - 1) + return; + head = s; + } else + head = s.Substring (0, idx); + foreach (char c in head) + if (c != ' ') + return; + lines [i] = s.Substring (idx + 1); + } + xml_comment_buffer.Remove (current_comment_start, length); + xml_comment_buffer.Insert (current_comment_start, String.Join (Environment.NewLine, lines)); + } + + // + // Checks if there was incorrect doc comments and raise + // warnings. + // + public void check_incorrect_doc_comment () + { + if (xml_comment_buffer.Length > 0) + WarningMisplacedComment (Location); + } + + // + // Consumes the saved xml comment lines (if any) + // as for current target member or type. + // + public string consume_doc_comment () + { + if (xml_comment_buffer.Length > 0) { + string ret = xml_comment_buffer.ToString (); + reset_doc_comment (); + return ret; + } + return null; + } + + void reset_doc_comment () + { + xml_comment_buffer.Length = 0; + } + + public void cleanup () + { + if (ifstack != null && ifstack.Count >= 1) { + int state = ifstack.Pop (); + if ((state & REGION) != 0) + Report.Error (1038, Location, "#endregion directive expected"); + else + Report.Error (1027, Location, "Expected `#endif' directive"); + } + } + } + + // + // Indicates whether it accepts XML documentation or not. + // + public enum XmlCommentState { + // comment is allowed in this state. + Allowed, + // comment is not allowed in this state. + NotAllowed, + // once comments appeared when it is NotAllowed, then the + // state is changed to it, until the state is changed to + // .Allowed. + Error + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/decl.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/decl.cs new file mode 100644 index 000000000..56d423ad2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/decl.cs @@ -0,0 +1,1297 @@ +// +// decl.cs: Declaration base class for structs, classes, enums and interfaces. +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using Mono.CompilerServices.SymbolWriter; + +#if NET_2_1 +using XmlElement = System.Object; +#else +using System.Xml; +#endif + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + // + // Better name would be DottenName + // + [DebuggerDisplay ("{GetSignatureForError()}")] + public class MemberName + { + public static readonly MemberName Null = new MemberName (""); + + public readonly string Name; + public TypeParameters TypeParameters; + public readonly FullNamedExpression ExplicitInterface; + public readonly Location Location; + + public readonly MemberName Left; + + public MemberName (string name) + : this (name, Location.Null) + { } + + public MemberName (string name, Location loc) + : this (null, name, loc) + { } + + public MemberName (string name, TypeParameters tparams, Location loc) + { + this.Name = name; + this.Location = loc; + + this.TypeParameters = tparams; + } + + public MemberName (string name, TypeParameters tparams, FullNamedExpression explicitInterface, Location loc) + : this (name, tparams, loc) + { + this.ExplicitInterface = explicitInterface; + } + + public MemberName (MemberName left, string name, Location loc) + { + this.Name = name; + this.Location = loc; + this.Left = left; + } + + public MemberName (MemberName left, string name, FullNamedExpression explicitInterface, Location loc) + : this (left, name, loc) + { + this.ExplicitInterface = explicitInterface; + } + + public MemberName (MemberName left, MemberName right) + { + this.Name = right.Name; + this.Location = right.Location; + this.TypeParameters = right.TypeParameters; + this.Left = left; + } + + public int Arity { + get { + return TypeParameters == null ? 0 : TypeParameters.Count; + } + } + + public bool IsGeneric { + get { + return TypeParameters != null; + } + } + + public string Basename { + get { + if (TypeParameters != null) + return MakeName (Name, TypeParameters); + return Name; + } + } + + public void CreateMetadataName (StringBuilder sb) + { + if (Left != null) + Left.CreateMetadataName (sb); + + if (sb.Length != 0) { + sb.Append ("."); + } + + sb.Append (Basename); + } + + public string GetSignatureForDocumentation () + { + var s = Basename; + + if (ExplicitInterface != null) + s = ExplicitInterface.GetSignatureForError () + "." + s; + + if (Left == null) + return s; + + return Left.GetSignatureForDocumentation () + "." + s; + } + + public string GetSignatureForError () + { + string s = TypeParameters == null ? null : "<" + TypeParameters.GetSignatureForError () + ">"; + s = Name + s; + + if (ExplicitInterface != null) + s = ExplicitInterface.GetSignatureForError () + "." + s; + + if (Left == null) + return s; + + return Left.GetSignatureForError () + "." + s; + } + + public override bool Equals (object other) + { + return Equals (other as MemberName); + } + + public bool Equals (MemberName other) + { + if (this == other) + return true; + if (other == null || Name != other.Name) + return false; + + if ((TypeParameters != null) && + (other.TypeParameters == null || TypeParameters.Count != other.TypeParameters.Count)) + return false; + + if ((TypeParameters == null) && (other.TypeParameters != null)) + return false; + + if (Left == null) + return other.Left == null; + + return Left.Equals (other.Left); + } + + public override int GetHashCode () + { + int hash = Name.GetHashCode (); + for (MemberName n = Left; n != null; n = n.Left) + hash ^= n.Name.GetHashCode (); + + if (TypeParameters != null) + hash ^= TypeParameters.Count << 5; + + return hash & 0x7FFFFFFF; + } + + public static string MakeName (string name, TypeParameters args) + { + if (args == null) + return name; + + return name + "`" + args.Count; + } + + public static string MakeName (string name, int count) + { + return name + "`" + count; + } + } + + public class SimpleMemberName + { + public string Value; + public Location Location; + + public SimpleMemberName (string name, Location loc) + { + this.Value = name; + this.Location = loc; + } + } + + /// + /// Base representation for members. This is used to keep track + /// of Name, Location and Modifier flags, and handling Attributes. + /// + [System.Diagnostics.DebuggerDisplay ("{GetSignatureForError()}")] + public abstract class MemberCore : Attributable, IMemberContext, IMemberDefinition + { + string IMemberDefinition.Name { + get { + return member_name.Name; + } + } + + // Is not readonly because of IndexerName attribute + private MemberName member_name; + public MemberName MemberName { + get { return member_name; } + } + + /// + /// Modifier flags that the user specified in the source code + /// + private Modifiers mod_flags; + public Modifiers ModFlags { + set { + mod_flags = value; + if ((value & Modifiers.COMPILER_GENERATED) != 0) + caching_flags = Flags.IsUsed | Flags.IsAssigned; + } + get { + return mod_flags; + } + } + + public virtual ModuleContainer Module { + get { + return Parent.Module; + } + } + + public /*readonly*/ TypeContainer Parent; + + /// + /// Location where this declaration happens + /// + public Location Location { + get { return member_name.Location; } + } + + /// + /// XML documentation comment + /// + protected string comment; + + /// + /// Represents header string for documentation comment + /// for each member types. + /// + public abstract string DocCommentHeader { get; } + + [Flags] + public enum Flags { + Obsolete_Undetected = 1, // Obsolete attribute has not been detected yet + Obsolete = 1 << 1, // Type has obsolete attribute + ClsCompliance_Undetected = 1 << 2, // CLS Compliance has not been detected yet + ClsCompliant = 1 << 3, // Type is CLS Compliant + CloseTypeCreated = 1 << 4, // Tracks whether we have Closed the type + HasCompliantAttribute_Undetected = 1 << 5, // Presence of CLSCompliantAttribute has not been detected + HasClsCompliantAttribute = 1 << 6, // Type has CLSCompliantAttribute + ClsCompliantAttributeFalse = 1 << 7, // Member has CLSCompliant(false) + Excluded_Undetected = 1 << 8, // Conditional attribute has not been detected yet + Excluded = 1 << 9, // Method is conditional + MethodOverloadsExist = 1 << 10, // Test for duplication must be performed + IsUsed = 1 << 11, + IsAssigned = 1 << 12, // Field is assigned + HasExplicitLayout = 1 << 13, + PartialDefinitionExists = 1 << 14, // Set when corresponding partial method definition exists + HasStructLayout = 1 << 15, // Has StructLayoutAttribute + HasInstanceConstructor = 1 << 16, + HasUserOperators = 1 << 17, + CanBeReused = 1 << 18, + InterfacesExpanded = 1 << 19 + } + + /// + /// MemberCore flags at first detected then cached + /// + internal Flags caching_flags; + + protected MemberCore (TypeContainer parent, MemberName name, Attributes attrs) + { + this.Parent = parent; + member_name = name; + caching_flags = Flags.Obsolete_Undetected | Flags.ClsCompliance_Undetected | Flags.HasCompliantAttribute_Undetected | Flags.Excluded_Undetected; + AddAttributes (attrs, this); + } + + protected virtual void SetMemberName (MemberName new_name) + { + member_name = new_name; + } + + public virtual void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + protected bool CheckAbstractAndExtern (bool has_block) + { + if (Parent.PartialContainer.Kind == MemberKind.Interface) + return true; + + if (has_block) { + if ((ModFlags & Modifiers.EXTERN) != 0) { + Report.Error (179, Location, "`{0}' cannot declare a body because it is marked extern", + GetSignatureForError ()); + return false; + } + + if ((ModFlags & Modifiers.ABSTRACT) != 0) { + Report.Error (500, Location, "`{0}' cannot declare a body because it is marked abstract", + GetSignatureForError ()); + return false; + } + } else { + if ((ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN | Modifiers.PARTIAL)) == 0 && !(Parent is Delegate)) { + if (Compiler.Settings.Version >= LanguageVersion.V_3) { + Property.PropertyMethod pm = this as Property.PropertyMethod; + if (pm is Indexer.GetIndexerMethod || pm is Indexer.SetIndexerMethod) + pm = null; + + if (pm != null && pm.Property.AccessorSecond == null) { + Report.Error (840, Location, + "`{0}' must have a body because it is not marked abstract or extern. The property can be automatically implemented when you define both accessors", + GetSignatureForError ()); + return false; + } + } + + Report.Error (501, Location, "`{0}' must have a body because it is not marked abstract, extern, or partial", + GetSignatureForError ()); + return false; + } + } + + return true; + } + + protected void CheckProtectedModifier () + { + if ((ModFlags & Modifiers.PROTECTED) == 0) + return; + + if (Parent.PartialContainer.Kind == MemberKind.Struct) { + Report.Error (666, Location, "`{0}': Structs cannot contain protected members", + GetSignatureForError ()); + return; + } + + if ((Parent.ModFlags & Modifiers.STATIC) != 0) { + Report.Error (1057, Location, "`{0}': Static classes cannot contain protected members", + GetSignatureForError ()); + return; + } + + if ((Parent.ModFlags & Modifiers.SEALED) != 0 && (ModFlags & Modifiers.OVERRIDE) == 0 && + !(this is Destructor)) { + Report.Warning (628, 4, Location, "`{0}': new protected member declared in sealed class", + GetSignatureForError ()); + return; + } + } + + public abstract bool Define (); + + public virtual string DocComment { + get { + return comment; + } + set { + comment = value; + } + } + + // + // Returns full member name for error message + // + public virtual string GetSignatureForError () + { + var parent = Parent.GetSignatureForError (); + if (parent == null) + return member_name.GetSignatureForError (); + + return parent + "." + member_name.GetSignatureForError (); + } + + /// + /// Base Emit method. This is also entry point for CLS-Compliant verification. + /// + public virtual void Emit () + { + if (!Compiler.Settings.VerifyClsCompliance) + return; + + VerifyClsCompliance (); + } + + public bool IsAvailableForReuse { + get { + return (caching_flags & Flags.CanBeReused) != 0; + } + set { + caching_flags = value ? (caching_flags | Flags.CanBeReused) : (caching_flags & ~Flags.CanBeReused); + } + } + + public bool IsCompilerGenerated { + get { + if ((mod_flags & Modifiers.COMPILER_GENERATED) != 0) + return true; + + return Parent != null && Parent.IsCompilerGenerated; + } + } + + public bool IsImported { + get { + return false; + } + } + + public virtual bool IsUsed { + get { + return (caching_flags & Flags.IsUsed) != 0; + } + } + + protected Report Report { + get { + return Compiler.Report; + } + } + + public void SetIsUsed () + { + caching_flags |= Flags.IsUsed; + } + + public void SetIsAssigned () + { + caching_flags |= Flags.IsAssigned; + } + + public virtual void SetConstraints (List constraints_list) + { + var tparams = member_name.TypeParameters; + if (tparams == null) { + Report.Error (80, Location, "Constraints are not allowed on non-generic declarations"); + return; + } + + foreach (var c in constraints_list) { + var tp = tparams.Find (c.TypeParameter.Value); + if (tp == null) { + Report.Error (699, c.Location, "`{0}': A constraint references nonexistent type parameter `{1}'", + GetSignatureForError (), c.TypeParameter.Value); + continue; + } + + tp.Constraints = c; + } + } + + /// + /// Returns instance of ObsoleteAttribute for this MemberCore + /// + public virtual ObsoleteAttribute GetAttributeObsolete () + { + if ((caching_flags & (Flags.Obsolete_Undetected | Flags.Obsolete)) == 0) + return null; + + caching_flags &= ~Flags.Obsolete_Undetected; + + if (OptAttributes == null) + return null; + + Attribute obsolete_attr = OptAttributes.Search (Module.PredefinedAttributes.Obsolete); + if (obsolete_attr == null) + return null; + + caching_flags |= Flags.Obsolete; + + ObsoleteAttribute obsolete = obsolete_attr.GetObsoleteAttribute (); + if (obsolete == null) + return null; + + return obsolete; + } + + /// + /// Checks for ObsoleteAttribute presence. It's used for testing of all non-types elements + /// + public virtual void CheckObsoleteness (Location loc) + { + ObsoleteAttribute oa = GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, GetSignatureForError (), loc, Report); + } + + // + // Checks whether the type P is as accessible as this member + // + public bool IsAccessibleAs (TypeSpec p) + { + // + // if M is private, its accessibility is the same as this declspace. + // we already know that P is accessible to T before this method, so we + // may return true. + // + if ((mod_flags & Modifiers.PRIVATE) != 0) + return true; + + while (TypeManager.HasElementType (p)) + p = TypeManager.GetElementType (p); + + if (p.IsGenericParameter) + return true; + + for (TypeSpec p_parent; p != null; p = p_parent) { + p_parent = p.DeclaringType; + + if (p.IsGeneric) { + foreach (TypeSpec t in p.TypeArguments) { + if (!IsAccessibleAs (t)) + return false; + } + } + + var pAccess = p.Modifiers & Modifiers.AccessibilityMask; + if (pAccess == Modifiers.PUBLIC) + continue; + + bool same_access_restrictions = false; + for (MemberCore mc = this; !same_access_restrictions && mc != null && mc.Parent != null; mc = mc.Parent) { + var al = mc.ModFlags & Modifiers.AccessibilityMask; + switch (pAccess) { + case Modifiers.INTERNAL: + if (al == Modifiers.PRIVATE || al == Modifiers.INTERNAL) + same_access_restrictions = p.MemberDefinition.IsInternalAsPublic (mc.Module.DeclaringAssembly); + + break; + + case Modifiers.PROTECTED: + if (al == Modifiers.PROTECTED) { + same_access_restrictions = mc.Parent.PartialContainer.IsBaseTypeDefinition (p_parent); + break; + } + + if (al == Modifiers.PRIVATE) { + // + // When type is private and any of its parents derives from + // protected type then the type is accessible + // + while (mc.Parent != null && mc.Parent.PartialContainer != null) { + if (mc.Parent.PartialContainer.IsBaseTypeDefinition (p_parent)) + same_access_restrictions = true; + mc = mc.Parent; + } + } + + break; + + case Modifiers.PROTECTED | Modifiers.INTERNAL: + if (al == Modifiers.INTERNAL) + same_access_restrictions = p.MemberDefinition.IsInternalAsPublic (mc.Module.DeclaringAssembly); + else if (al == (Modifiers.PROTECTED | Modifiers.INTERNAL)) + same_access_restrictions = mc.Parent.PartialContainer.IsBaseTypeDefinition (p_parent) && p.MemberDefinition.IsInternalAsPublic (mc.Module.DeclaringAssembly); + else + goto case Modifiers.PROTECTED; + + break; + + case Modifiers.PRIVATE: + // + // Both are private and share same parent + // + if (al == Modifiers.PRIVATE) { + var decl = mc.Parent; + do { + same_access_restrictions = decl.CurrentType.MemberDefinition == p_parent.MemberDefinition; + } while (!same_access_restrictions && !decl.PartialContainer.IsTopLevel && (decl = decl.Parent) != null); + } + + break; + + default: + throw new InternalErrorException (al.ToString ()); + } + } + + if (!same_access_restrictions) + return false; + } + + return true; + } + + /// + /// Analyze whether CLS-Compliant verification must be execute for this MemberCore. + /// + public override bool IsClsComplianceRequired () + { + if ((caching_flags & Flags.ClsCompliance_Undetected) == 0) + return (caching_flags & Flags.ClsCompliant) != 0; + + caching_flags &= ~Flags.ClsCompliance_Undetected; + + if (HasClsCompliantAttribute) { + if ((caching_flags & Flags.ClsCompliantAttributeFalse) != 0) + return false; + + caching_flags |= Flags.ClsCompliant; + return true; + } + + if (Parent.IsClsComplianceRequired ()) { + caching_flags |= Flags.ClsCompliant; + return true; + } + + return false; + } + + public virtual string[] ConditionalConditions () + { + return null; + } + + /// + /// Returns true when MemberCore is exposed from assembly. + /// + public bool IsExposedFromAssembly () + { + if ((ModFlags & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0) + return this is NamespaceContainer; + + var parentContainer = Parent.PartialContainer; + while (parentContainer != null) { + if ((parentContainer.ModFlags & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0) + return false; + + parentContainer = parentContainer.Parent.PartialContainer; + } + + return true; + } + + // + // Does extension methods look up to find a method which matches name and extensionType. + // Search starts from this namespace and continues hierarchically up to top level. + // + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + var m = Parent; + do { + var ns = m as NamespaceContainer; + if (ns != null) + return ns.LookupExtensionMethod (this, extensionType, name, arity, 0); + + m = m.Parent; + } while (m != null); + + return null; + } + + public virtual FullNamedExpression LookupNamespaceAlias (string name) + { + return Parent.LookupNamespaceAlias (name); + } + + public virtual FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + return Parent.LookupNamespaceOrType (name, arity, mode, loc); + } + + /// + /// Goes through class hierarchy and gets value of first found CLSCompliantAttribute. + /// If no is attribute exists then assembly CLSCompliantAttribute is returned. + /// + public bool? CLSAttributeValue { + get { + if ((caching_flags & Flags.HasCompliantAttribute_Undetected) == 0) { + if ((caching_flags & Flags.HasClsCompliantAttribute) == 0) + return null; + + return (caching_flags & Flags.ClsCompliantAttributeFalse) == 0; + } + + caching_flags &= ~Flags.HasCompliantAttribute_Undetected; + + if (OptAttributes != null) { + Attribute cls_attribute = OptAttributes.Search (Module.PredefinedAttributes.CLSCompliant); + if (cls_attribute != null) { + caching_flags |= Flags.HasClsCompliantAttribute; + if (cls_attribute.GetClsCompliantAttributeValue ()) + return true; + + caching_flags |= Flags.ClsCompliantAttributeFalse; + return false; + } + } + + return null; + } + } + + /// + /// Returns true if MemberCore is explicitly marked with CLSCompliantAttribute + /// + protected bool HasClsCompliantAttribute { + get { + return CLSAttributeValue.HasValue; + } + } + + /// + /// Returns true when a member supports multiple overloads (methods, indexers, etc) + /// + public virtual bool EnableOverloadChecks (MemberCore overload) + { + return false; + } + + /// + /// The main virtual method for CLS-Compliant verifications. + /// The method returns true if member is CLS-Compliant and false if member is not + /// CLS-Compliant which means that CLS-Compliant tests are not necessary. A descendants override it + /// and add their extra verifications. + /// + protected virtual bool VerifyClsCompliance () + { + if (HasClsCompliantAttribute) { + if (!Module.DeclaringAssembly.HasCLSCompliantAttribute) { + Attribute a = OptAttributes.Search (Module.PredefinedAttributes.CLSCompliant); + if ((caching_flags & Flags.ClsCompliantAttributeFalse) != 0) { + Report.Warning (3021, 2, a.Location, + "`{0}' does not need a CLSCompliant attribute because the assembly is not marked as CLS-compliant", + GetSignatureForError ()); + } else { + Report.Warning (3014, 1, a.Location, + "`{0}' cannot be marked as CLS-compliant because the assembly is not marked as CLS-compliant", + GetSignatureForError ()); + } + return false; + } + + if (!IsExposedFromAssembly ()) { + Attribute a = OptAttributes.Search (Module.PredefinedAttributes.CLSCompliant); + Report.Warning (3019, 2, a.Location, "CLS compliance checking will not be performed on `{0}' because it is not visible from outside this assembly", GetSignatureForError ()); + return false; + } + + if ((caching_flags & Flags.ClsCompliantAttributeFalse) != 0) { + if (Parent is Interface && Parent.IsClsComplianceRequired ()) { + Report.Warning (3010, 1, Location, "`{0}': CLS-compliant interfaces must have only CLS-compliant members", GetSignatureForError ()); + } else if (Parent.Kind == MemberKind.Class && (ModFlags & Modifiers.ABSTRACT) != 0 && Parent.IsClsComplianceRequired ()) { + Report.Warning (3011, 1, Location, "`{0}': only CLS-compliant members can be abstract", GetSignatureForError ()); + } + + return false; + } + + if (Parent.Kind != MemberKind.Namespace && Parent.Kind != 0 && !Parent.IsClsComplianceRequired ()) { + Attribute a = OptAttributes.Search (Module.PredefinedAttributes.CLSCompliant); + Report.Warning (3018, 1, a.Location, "`{0}' cannot be marked as CLS-compliant because it is a member of non CLS-compliant type `{1}'", + GetSignatureForError (), Parent.GetSignatureForError ()); + return false; + } + } else { + if (!IsExposedFromAssembly ()) + return false; + + if (!Parent.IsClsComplianceRequired ()) + return false; + } + + if (member_name.Name [0] == '_') { + Warning_IdentifierNotCompliant (); + } + + if (member_name.TypeParameters != null) + member_name.TypeParameters.VerifyClsCompliance (); + + return true; + } + + protected void Warning_IdentifierNotCompliant () + { + Report.Warning (3008, 1, MemberName.Location, "Identifier `{0}' is not CLS-compliant", GetSignatureForError ()); + } + + public virtual string GetCallerMemberName () + { + return MemberName.Name; + } + + // + // Returns a string that represents the signature for this + // member which should be used in XML documentation. + // + public abstract string GetSignatureForDocumentation (); + + public virtual void GetCompletionStartingWith (string prefix, List results) + { + Parent.GetCompletionStartingWith (prefix, results); + } + + // + // Generates xml doc comments (if any), and if required, + // handle warning report. + // + internal virtual void GenerateDocComment (DocumentationBuilder builder) + { + if (DocComment == null) { + if (IsExposedFromAssembly ()) { + Constructor c = this as Constructor; + if (c == null || !c.IsDefault ()) + Report.Warning (1591, 4, Location, + "Missing XML comment for publicly visible type or member `{0}'", GetSignatureForError ()); + } + + return; + } + + try { + builder.GenerateDocumentationForMember (this); + } catch (Exception e) { + throw new InternalErrorException (this, e); + } + } + + public virtual void WriteDebugSymbol (MonoSymbolFile file) + { + } + + #region IMemberContext Members + + public virtual CompilerContext Compiler { + get { + return Module.Compiler; + } + } + + public virtual TypeSpec CurrentType { + get { return Parent.CurrentType; } + } + + public MemberCore CurrentMemberDefinition { + get { return this; } + } + + public virtual TypeParameters CurrentTypeParameters { + get { return null; } + } + + public bool IsObsolete { + get { + if (GetAttributeObsolete () != null) + return true; + + return Parent != null && Parent.IsObsolete; + } + } + + public bool IsUnsafe { + get { + if ((ModFlags & Modifiers.UNSAFE) != 0) + return true; + + return Parent != null && Parent.IsUnsafe; + } + } + + public bool IsStatic { + get { + return (ModFlags & Modifiers.STATIC) != 0; + } + } + + #endregion + } + + // + // Base member specification. A member specification contains + // member details which can alter in the context (e.g. generic instances) + // + public abstract class MemberSpec + { + [Flags] + public enum StateFlags + { + Obsolete_Undetected = 1, // Obsolete attribute has not been detected yet + Obsolete = 1 << 1, // Member has obsolete attribute + CLSCompliant_Undetected = 1 << 2, // CLSCompliant attribute has not been detected yet + CLSCompliant = 1 << 3, // Member is CLS Compliant + MissingDependency_Undetected = 1 << 4, + MissingDependency = 1 << 5, + HasDynamicElement = 1 << 6, + ConstraintsChecked = 1 << 7, + + IsAccessor = 1 << 9, // Method is an accessor + IsGeneric = 1 << 10, // Member contains type arguments + + PendingMetaInflate = 1 << 12, + PendingMakeMethod = 1 << 13, + PendingMemberCacheMembers = 1 << 14, + PendingBaseTypeInflate = 1 << 15, + InterfacesExpanded = 1 << 16, + IsNotCSharpCompatible = 1 << 17, + SpecialRuntimeType = 1 << 18, + InflatedExpressionType = 1 << 19, + InflatedNullableType = 1 << 20, + GenericIterateInterface = 1 << 21, + GenericTask = 1 << 22, + InterfacesImported = 1 << 23, + } + + // + // Some flags can be copied directly from other member + // + protected const StateFlags SharedStateFlags = + StateFlags.CLSCompliant | StateFlags.CLSCompliant_Undetected | + StateFlags.Obsolete | StateFlags.Obsolete_Undetected | + StateFlags.MissingDependency | StateFlags.MissingDependency_Undetected | + StateFlags.HasDynamicElement; + + protected Modifiers modifiers; + public StateFlags state; + protected IMemberDefinition definition; + public readonly MemberKind Kind; + protected TypeSpec declaringType; + +#if DEBUG + static int counter; + public int ID = counter++; +#endif + + protected MemberSpec (MemberKind kind, TypeSpec declaringType, IMemberDefinition definition, Modifiers modifiers) + { + this.Kind = kind; + this.declaringType = declaringType; + this.definition = definition; + this.modifiers = modifiers; + + if (kind == MemberKind.MissingType) + state = StateFlags.MissingDependency; + else + state = StateFlags.Obsolete_Undetected | StateFlags.CLSCompliant_Undetected | StateFlags.MissingDependency_Undetected; + } + + #region Properties + + public virtual int Arity { + get { + return 0; + } + } + + public TypeSpec DeclaringType { + get { + return declaringType; + } + set { + declaringType = value; + } + } + + public IMemberDefinition MemberDefinition { + get { + return definition; + } + } + + public Modifiers Modifiers { + get { + return modifiers; + } + set { + modifiers = value; + } + } + + public virtual string Name { + get { + return definition.Name; + } + } + + public bool IsAbstract { + get { return (modifiers & Modifiers.ABSTRACT) != 0; } + } + + public bool IsAccessor { + get { + return (state & StateFlags.IsAccessor) != 0; + } + set { + state = value ? state | StateFlags.IsAccessor : state & ~StateFlags.IsAccessor; + } + } + + // + // Return true when this member is a generic in C# terms + // A nested non-generic type of generic type will return false + // + public bool IsGeneric { + get { + return (state & StateFlags.IsGeneric) != 0; + } + set { + state = value ? state | StateFlags.IsGeneric : state & ~StateFlags.IsGeneric; + } + } + + // + // Returns true for imported members which are not compatible with C# language + // + public bool IsNotCSharpCompatible { + get { + return (state & StateFlags.IsNotCSharpCompatible) != 0; + } + set { + state = value ? state | StateFlags.IsNotCSharpCompatible : state & ~StateFlags.IsNotCSharpCompatible; + } + } + + public bool IsPrivate { + get { return (modifiers & Modifiers.PRIVATE) != 0; } + } + + public bool IsPublic { + get { return (modifiers & Modifiers.PUBLIC) != 0; } + } + + public bool IsStatic { + get { + return (modifiers & Modifiers.STATIC) != 0; + } + } + + #endregion + + public virtual ObsoleteAttribute GetAttributeObsolete () + { + if ((state & (StateFlags.Obsolete | StateFlags.Obsolete_Undetected)) == 0) + return null; + + state &= ~StateFlags.Obsolete_Undetected; + + var oa = definition.GetAttributeObsolete (); + if (oa != null) + state |= StateFlags.Obsolete; + + return oa; + } + + // + // Returns a list of missing dependencies of this member. The list + // will contain types only but it can have numerous values for members + // like methods where both return type and all parameters are checked + // + public List GetMissingDependencies () + { + return GetMissingDependencies (this); + } + + public List GetMissingDependencies (MemberSpec caller) + { + if ((state & (StateFlags.MissingDependency | StateFlags.MissingDependency_Undetected)) == 0) + return null; + + state &= ~StateFlags.MissingDependency_Undetected; + + var imported = definition as ImportedDefinition; + List missing; + if (imported != null) { + missing = ResolveMissingDependencies (caller); + } else if (this is ElementTypeSpec) { + missing = ((ElementTypeSpec) this).Element.GetMissingDependencies (caller); + } else { + missing = null; + } + + if (missing != null) { + state |= StateFlags.MissingDependency; + } + + return missing; + } + + public abstract List ResolveMissingDependencies (MemberSpec caller); + + protected virtual bool IsNotCLSCompliant (out bool attrValue) + { + var cls = MemberDefinition.CLSAttributeValue; + attrValue = cls ?? false; + return cls == false; + } + + public virtual string GetSignatureForDocumentation () + { + return DeclaringType.GetSignatureForDocumentation () + "." + Name; + } + + public virtual string GetSignatureForError () + { + var bf = MemberDefinition as Property.BackingField; + string name; + if (bf == null) { + name = Name; + } else { + name = bf.OriginalProperty.MemberName.Name; + } + + return DeclaringType.GetSignatureForError () + "." + name; + } + + public virtual MemberSpec InflateMember (TypeParameterInflator inflator) + { + var inflated = (MemberSpec) MemberwiseClone (); + inflated.declaringType = inflator.TypeInstance; + if (DeclaringType.IsGenericOrParentIsGeneric) + inflated.state |= StateFlags.PendingMetaInflate; +#if DEBUG + inflated.ID += 1000000; +#endif + return inflated; + } + + // + // Is this member accessible from invocation context + // + public bool IsAccessible (IMemberContext ctx) + { + var ma = Modifiers & Modifiers.AccessibilityMask; + if (ma == Modifiers.PUBLIC) + return true; + + var parentType = /* this as TypeSpec ?? */ DeclaringType; + var ctype = ctx.CurrentType; + + if (ma == Modifiers.PRIVATE) { + if (ctype == null || parentType == null) + return false; + // + // It's only accessible to the current class or children + // + if (parentType.MemberDefinition == ctype.MemberDefinition) + return true; + + return TypeManager.IsNestedChildOf (ctype, parentType.MemberDefinition); + } + + if ((ma & Modifiers.INTERNAL) != 0) { + bool b; + var assembly = ctype == null ? ctx.Module.DeclaringAssembly : ctype.MemberDefinition.DeclaringAssembly; + + if (parentType == null) { + b = ((ITypeDefinition) MemberDefinition).IsInternalAsPublic (assembly); + } else { + b = DeclaringType.MemberDefinition.IsInternalAsPublic (assembly); + } + + if (b || ma == Modifiers.INTERNAL) + return b; + } + + // + // Checks whether `ctype' is a subclass or nested child of `parentType'. + // + while (ctype != null) { + if (TypeManager.IsFamilyAccessible (ctype, parentType)) + return true; + + // Handle nested types. + ctype = ctype.DeclaringType; // TODO: Untested ??? + } + + return false; + } + + // + // Returns member CLS compliance based on full member hierarchy + // + public bool IsCLSCompliant () + { + if ((state & StateFlags.CLSCompliant_Undetected) != 0) { + state &= ~StateFlags.CLSCompliant_Undetected; + + bool compliant; + if (IsNotCLSCompliant (out compliant)) + return false; + + if (!compliant) { + if (DeclaringType != null) { + compliant = DeclaringType.IsCLSCompliant (); + } else { + compliant = ((ITypeDefinition) MemberDefinition).DeclaringAssembly.IsCLSCompliant; + } + } + + if (compliant) + state |= StateFlags.CLSCompliant; + } + + return (state & StateFlags.CLSCompliant) != 0; + } + + public bool IsConditionallyExcluded (IMemberContext ctx) + { + if ((Kind & (MemberKind.Class | MemberKind.Method)) == 0) + return false; + + var conditions = MemberDefinition.ConditionalConditions (); + if (conditions == null) + return false; + + var m = ctx.CurrentMemberDefinition; + CompilationSourceFile unit = null; + while (m != null && unit == null) { + unit = m as CompilationSourceFile; + m = m.Parent; + } + + if (unit != null) { + foreach (var condition in conditions) { + if (unit.IsConditionalDefined (condition)) + return false; + } + } + + return true; + } + + public override string ToString () + { + return GetSignatureForError (); + } + } + + // + // Member details which are same between all member + // specifications + // + public interface IMemberDefinition + { + bool? CLSAttributeValue { get; } + string Name { get; } + bool IsImported { get; } + + string[] ConditionalConditions (); + ObsoleteAttribute GetAttributeObsolete (); + void SetIsAssigned (); + void SetIsUsed (); + } + + public interface IMethodDefinition : IMemberDefinition + { + MethodBase Metadata { get; } + } + + public interface IParametersMember : IInterfaceMemberSpec + { + AParametersCollection Parameters { get; } + } + + public interface IInterfaceMemberSpec + { + TypeSpec MemberType { get; } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/delegate.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/delegate.cs new file mode 100644 index 000000000..612d11636 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/delegate.cs @@ -0,0 +1,952 @@ +// +// delegate.cs: Delegate Handler +// +// Authors: +// Ravi Pratap (ravi@ximian.com) +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2009 Novell, Inc (http://www.novell.com) +// Copyright 2011 Xamarin Inc +// + +using System; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + // + // Delegate container implementation + // + public class Delegate : TypeDefinition, IParametersMember + { + public FullNamedExpression ReturnType; + readonly ParametersCompiled parameters; + + Constructor Constructor; + Method InvokeBuilder; + Method BeginInvokeBuilder; + Method EndInvokeBuilder; + + static readonly string[] attribute_targets = new string [] { "type", "return" }; + + public static readonly string InvokeMethodName = "Invoke"; + + Expression instance_expr; + ReturnParameter return_attributes; + + const Modifiers MethodModifiers = Modifiers.PUBLIC | Modifiers.VIRTUAL; + + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Delegate (TypeContainer parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, ParametersCompiled param_list, + Attributes attrs) + : base (parent, name, attrs, MemberKind.Delegate) + + { + this.ReturnType = type; + ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod_flags, + IsTopLevel ? Modifiers.INTERNAL : + Modifiers.PRIVATE, name.Location, Report); + parameters = param_list; + spec = new TypeSpec (Kind, null, this, null, ModFlags | Modifiers.SEALED); + } + + #region Properties + public TypeSpec MemberType { + get { + return ReturnType.Type; + } + } + + public AParametersCollection Parameters { + get { + return parameters; + } + } + + public FullNamedExpression TypExpression { + get { + return ReturnType; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.ReturnValue) { + if (return_attributes == null) + return_attributes = new ReturnParameter (this, InvokeBuilder.MethodBuilder, Location); + + return_attributes.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Delegate; + } + } + + protected override bool DoDefineMembers () + { + var builtin_types = Compiler.BuiltinTypes; + + var ctor_parameters = ParametersCompiled.CreateFullyResolved ( + new [] { + new Parameter (new TypeExpression (builtin_types.Object, Location), "object", Parameter.Modifier.NONE, null, Location), + new Parameter (new TypeExpression (builtin_types.IntPtr, Location), "method", Parameter.Modifier.NONE, null, Location) + }, + new [] { + builtin_types.Object, + builtin_types.IntPtr + } + ); + + Constructor = new Constructor (this, Constructor.ConstructorName, + Modifiers.PUBLIC, null, ctor_parameters, Location); + Constructor.Define (); + + // + // Here the various methods like Invoke, BeginInvoke etc are defined + // + // First, call the `out of band' special method for + // defining recursively any types we need: + // + var p = parameters; + + if (!p.Resolve (this)) + return false; + + // + // Invoke method + // + + // Check accessibility + foreach (var partype in p.Types) { + if (!IsAccessibleAs (partype)) { + Report.SymbolRelatedToPreviousError (partype); + Report.Error (59, Location, + "Inconsistent accessibility: parameter type `{0}' is less accessible than delegate `{1}'", + partype.GetSignatureForError (), GetSignatureForError ()); + } + } + + var ret_type = ReturnType.ResolveAsType (this); + if (ret_type == null) + return false; + + // + // We don't have to check any others because they are all + // guaranteed to be accessible - they are standard types. + // + if (!IsAccessibleAs (ret_type)) { + Report.SymbolRelatedToPreviousError (ret_type); + Report.Error (58, Location, + "Inconsistent accessibility: return type `" + + ret_type.GetSignatureForError () + "' is less " + + "accessible than delegate `" + GetSignatureForError () + "'"); + return false; + } + + CheckProtectedModifier (); + + if (Compiler.Settings.StdLib && ret_type.IsSpecialRuntimeType) { + Method.Error1599 (Location, ret_type, Report); + return false; + } + + VarianceDecl.CheckTypeVariance (ret_type, Variance.Covariant, this); + + var resolved_rt = new TypeExpression (ret_type, Location); + InvokeBuilder = new Method (this, resolved_rt, MethodModifiers, new MemberName (InvokeMethodName), p, null); + InvokeBuilder.Define (); + + // + // Don't emit async method for compiler generated delegates (e.g. dynamic site containers) + // + if (!IsCompilerGenerated) { + DefineAsyncMethods (resolved_rt); + } + + return true; + } + + void DefineAsyncMethods (TypeExpression returnType) + { + var iasync_result = Module.PredefinedTypes.IAsyncResult; + var async_callback = Module.PredefinedTypes.AsyncCallback; + + // + // It's ok when async types don't exist, the delegate will have Invoke method only + // + if (!iasync_result.Define () || !async_callback.Define ()) + return; + + // + // BeginInvoke + // + ParametersCompiled async_parameters; + if (Parameters.Count == 0) { + async_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } else { + var compiled = new Parameter[Parameters.Count]; + for (int i = 0; i < compiled.Length; ++i) { + var p = parameters[i]; + compiled[i] = new Parameter (new TypeExpression (parameters.Types[i], Location), + p.Name, + p.ModFlags & Parameter.Modifier.RefOutMask, + p.OptAttributes == null ? null : p.OptAttributes.Clone (), Location); + } + + async_parameters = new ParametersCompiled (compiled); + } + + async_parameters = ParametersCompiled.MergeGenerated (Compiler, async_parameters, false, + new Parameter[] { + new Parameter (new TypeExpression (async_callback.TypeSpec, Location), "callback", Parameter.Modifier.NONE, null, Location), + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, Location), "object", Parameter.Modifier.NONE, null, Location) + }, + new [] { + async_callback.TypeSpec, + Compiler.BuiltinTypes.Object + } + ); + + BeginInvokeBuilder = new Method (this, + new TypeExpression (iasync_result.TypeSpec, Location), MethodModifiers, + new MemberName ("BeginInvoke"), async_parameters, null); + BeginInvokeBuilder.Define (); + + // + // EndInvoke is a bit more interesting, all the parameters labeled as + // out or ref have to be duplicated here. + // + + // + // Define parameters, and count out/ref parameters + // + ParametersCompiled end_parameters; + int out_params = 0; + + foreach (Parameter p in Parameters.FixedParameters) { + if ((p.ModFlags & Parameter.Modifier.RefOutMask) != 0) + ++out_params; + } + + if (out_params > 0) { + Parameter[] end_params = new Parameter[out_params]; + + int param = 0; + for (int i = 0; i < Parameters.FixedParameters.Length; ++i) { + Parameter p = parameters [i]; + if ((p.ModFlags & Parameter.Modifier.RefOutMask) == 0) + continue; + + end_params [param++] = new Parameter (new TypeExpression (p.Type, Location), + p.Name, + p.ModFlags & Parameter.Modifier.RefOutMask, + p.OptAttributes == null ? null : p.OptAttributes.Clone (), Location); + } + + end_parameters = new ParametersCompiled (end_params); + } else { + end_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + + end_parameters = ParametersCompiled.MergeGenerated (Compiler, end_parameters, false, + new Parameter ( + new TypeExpression (iasync_result.TypeSpec, Location), + "result", Parameter.Modifier.NONE, null, Location), + iasync_result.TypeSpec); + + // + // Create method, define parameters, register parameters with type system + // + EndInvokeBuilder = new Method (this, returnType, MethodModifiers, new MemberName ("EndInvoke"), end_parameters, null); + EndInvokeBuilder.Define (); + } + + public override void PrepareEmit () + { + if (!Parameters.IsEmpty) { + parameters.ResolveDefaultValues (this); + } + + InvokeBuilder.PrepareEmit (); + if (BeginInvokeBuilder != null) { + BeginInvokeBuilder.PrepareEmit (); + EndInvokeBuilder.PrepareEmit (); + } + } + + public override void Emit () + { + base.Emit (); + + if (ReturnType.Type != null) { + if (ReturnType.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + return_attributes = new ReturnParameter (this, InvokeBuilder.MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder); + } else if (ReturnType.Type.HasDynamicElement) { + return_attributes = new ReturnParameter (this, InvokeBuilder.MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder, ReturnType.Type, Location); + } + + ConstraintChecker.Check (this, ReturnType.Type, ReturnType.Location); + } + + Constructor.ParameterInfo.ApplyAttributes (this, Constructor.ConstructorBuilder); + Constructor.ConstructorBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + + parameters.CheckConstraints (this); + parameters.ApplyAttributes (this, InvokeBuilder.MethodBuilder); + InvokeBuilder.MethodBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + + if (BeginInvokeBuilder != null) { + BeginInvokeBuilder.ParameterInfo.ApplyAttributes (this, BeginInvokeBuilder.MethodBuilder); + EndInvokeBuilder.ParameterInfo.ApplyAttributes (this, EndInvokeBuilder.MethodBuilder); + + BeginInvokeBuilder.MethodBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + EndInvokeBuilder.MethodBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + } + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_type = Compiler.BuiltinTypes.MulticastDelegate; + base_class = null; + return null; + } + + protected override TypeAttributes TypeAttr { + get { + return base.TypeAttr | TypeAttributes.Class | TypeAttributes.Sealed; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + //TODO: duplicate + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) { + return false; + } + + parameters.VerifyClsCompliance (this); + + if (!InvokeBuilder.MemberType.IsCLSCompliant ()) { + Report.Warning (3002, 1, Location, "Return type of `{0}' is not CLS-compliant", + GetSignatureForError ()); + } + return true; + } + + + public static MethodSpec GetConstructor (TypeSpec delType) + { + var ctor = MemberCache.FindMember (delType, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly); + return (MethodSpec) ctor; + } + + // + // Returns the "Invoke" from a delegate type + // + public static MethodSpec GetInvokeMethod (TypeSpec delType) + { + var invoke = MemberCache.FindMember (delType, + MemberFilter.Method (InvokeMethodName, 0, null, null), + BindingRestriction.DeclaredOnly); + + return (MethodSpec) invoke; + } + + public static AParametersCollection GetParameters (TypeSpec delType) + { + var invoke_mb = GetInvokeMethod (delType); + return invoke_mb.Parameters; + } + + // + // 15.2 Delegate compatibility + // + public static bool IsTypeCovariant (ResolveContext rc, TypeSpec a, TypeSpec b) + { + // + // For each value parameter (a parameter with no ref or out modifier), an + // identity conversion or implicit reference conversion exists from the + // parameter type in D to the corresponding parameter type in M + // + if (a == b) + return true; + + if (rc.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) + return false; + + if (a.IsGenericParameter && b.IsGenericParameter) + return a == b; + + return Convert.ImplicitReferenceConversionExists (a, b); + } + + public static string FullDelegateDesc (MethodSpec invoke_method) + { + return TypeManager.GetFullNameSignature (invoke_method).Replace (".Invoke", ""); + } + + public Expression InstanceExpression { + get { + return instance_expr; + } + set { + instance_expr = value; + } + } + } + + // + // Base class for `NewDelegate' and `ImplicitDelegateCreation' + // + public abstract class DelegateCreation : Expression, OverloadResolver.IErrorHandler + { + bool conditional_access_receiver; + protected MethodSpec constructor_method; + protected MethodGroupExpr method_group; + + public bool AllowSpecialMethodsInvocation { get; set; } + + public override bool ContainsEmitWithAwait () + { + var instance = method_group.InstanceExpression; + return instance != null && instance.ContainsEmitWithAwait (); + } + + public static Arguments CreateDelegateMethodArguments (ResolveContext rc, AParametersCollection pd, TypeSpec[] types, Location loc) + { + Arguments delegate_arguments = new Arguments (pd.Count); + for (int i = 0; i < pd.Count; ++i) { + Argument.AType atype_modifier; + switch (pd.FixedParameters [i].ModFlags & Parameter.Modifier.RefOutMask) { + case Parameter.Modifier.REF: + atype_modifier = Argument.AType.Ref; + break; + case Parameter.Modifier.OUT: + atype_modifier = Argument.AType.Out; + break; + default: + atype_modifier = 0; + break; + } + + var ptype = types[i]; + if (ptype.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + ptype = rc.BuiltinTypes.Object; + + delegate_arguments.Add (new Argument (new TypeExpression (ptype, loc), atype_modifier)); + } + + return delegate_arguments; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + MemberAccess ma = new MemberAccess (new MemberAccess (new QualifiedAliasMember ("global", "System", loc), "Delegate", loc), "CreateDelegate", loc); + + Arguments args = new Arguments (3); + args.Add (new Argument (new TypeOf (type, loc))); + + if (method_group.InstanceExpression == null) + args.Add (new Argument (new NullLiteral (loc))); + else + args.Add (new Argument (method_group.InstanceExpression)); + + args.Add (new Argument (method_group.CreateExpressionTree (ec))); + Expression e = new Invocation (ma, args).Resolve (ec); + if (e == null) + return null; + + e = Convert.ExplicitConversion (ec, e, type, loc); + if (e == null) + return null; + + return e.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + constructor_method = Delegate.GetConstructor (type); + + var invoke_method = Delegate.GetInvokeMethod (type); + + if (!ec.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) { + if (method_group.HasConditionalAccess ()) { + conditional_access_receiver = true; + ec.Set (ResolveContext.Options.ConditionalAccessReceiver); + } + } + + Arguments arguments = CreateDelegateMethodArguments (ec, invoke_method.Parameters, invoke_method.Parameters.Types, loc); + method_group = method_group.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.CovariantDelegate); + + if (conditional_access_receiver) + ec.With (ResolveContext.Options.ConditionalAccessReceiver, false); + + if (method_group == null) + return null; + + var delegate_method = method_group.BestCandidate; + + if (delegate_method.DeclaringType.IsNullableType) { + ec.Report.Error (1728, loc, "Cannot create delegate from method `{0}' because it is a member of System.Nullable type", + delegate_method.GetSignatureForError ()); + return null; + } + + if (!AllowSpecialMethodsInvocation) + Invocation.IsSpecialMethodInvocation (ec, delegate_method, loc); + + ExtensionMethodGroupExpr emg = method_group as ExtensionMethodGroupExpr; + if (emg != null) { + method_group.InstanceExpression = emg.ExtensionExpression; + TypeSpec e_type = emg.ExtensionExpression.Type; + if (TypeSpec.IsValueType (e_type)) { + ec.Report.Error (1113, loc, "Extension method `{0}' of value type `{1}' cannot be used to create delegates", + delegate_method.GetSignatureForError (), e_type.GetSignatureForError ()); + } + } + + TypeSpec rt = method_group.BestCandidateReturnType; + if (rt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + rt = ec.BuiltinTypes.Object; + + if (!Delegate.IsTypeCovariant (ec, rt, invoke_method.ReturnType)) { + Expression ret_expr = new TypeExpression (delegate_method.ReturnType, loc); + Error_ConversionFailed (ec, delegate_method, ret_expr); + } + + if (method_group.IsConditionallyExcluded) { + ec.Report.SymbolRelatedToPreviousError (delegate_method); + MethodOrOperator m = delegate_method.MemberDefinition as MethodOrOperator; + if (m != null && m.IsPartialDefinition) { + ec.Report.Error (762, loc, "Cannot create delegate from partial method declaration `{0}'", + delegate_method.GetSignatureForError ()); + } else { + ec.Report.Error (1618, loc, "Cannot create delegate with `{0}' because it has a Conditional attribute", + TypeManager.CSharpSignature (delegate_method)); + } + } + + var expr = method_group.InstanceExpression; + if (expr != null && (expr.Type.IsGenericParameter || !TypeSpec.IsReferenceType (expr.Type))) + method_group.InstanceExpression = new BoxedCast (expr, ec.BuiltinTypes.Object); + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + if (conditional_access_receiver) + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + + if (method_group.InstanceExpression == null) { + ec.EmitNull (); + } else { + var ie = new InstanceEmitter (method_group.InstanceExpression, false); + ie.Emit (ec, method_group.ConditionalAccess); + } + + var delegate_method = method_group.BestCandidate; + + // Any delegate must be sealed + if (!delegate_method.DeclaringType.IsDelegate && delegate_method.IsVirtual && !method_group.IsBase) { + ec.Emit (OpCodes.Dup); + ec.Emit (OpCodes.Ldvirtftn, delegate_method); + } else { + ec.Emit (OpCodes.Ldftn, delegate_method); + } + + ec.Emit (OpCodes.Newobj, constructor_method); + + if (conditional_access_receiver) + ec.CloseConditionalAccess (null); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + method_group.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + void Error_ConversionFailed (ResolveContext ec, MethodSpec method, Expression return_type) + { + var invoke_method = Delegate.GetInvokeMethod (type); + string member_name = method_group.InstanceExpression != null ? + Delegate.FullDelegateDesc (method) : + TypeManager.GetFullNameSignature (method); + + ec.Report.SymbolRelatedToPreviousError (type); + ec.Report.SymbolRelatedToPreviousError (method); + if (ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) { + ec.Report.Error (410, loc, "A method or delegate `{0} {1}' parameters and return type must be same as delegate `{2} {3}' parameters and return type", + method.ReturnType.GetSignatureForError (), member_name, + invoke_method.ReturnType.GetSignatureForError (), Delegate.FullDelegateDesc (invoke_method)); + return; + } + + if (return_type == null) { + ec.Report.Error (123, loc, "A method or delegate `{0}' parameters do not match delegate `{1}' parameters", + member_name, Delegate.FullDelegateDesc (invoke_method)); + return; + } + + ec.Report.Error (407, loc, "A method or delegate `{0} {1}' return type does not match delegate `{2} {3}' return type", + return_type.GetSignatureForError (), member_name, + invoke_method.ReturnType.GetSignatureForError (), Delegate.FullDelegateDesc (invoke_method)); + } + + public static bool ImplicitStandardConversionExists (ResolveContext ec, MethodGroupExpr mg, TypeSpec target_type) + { +// if (target_type == TypeManager.delegate_type || target_type == TypeManager.multicast_delegate_type) +// return false; + + var invoke = Delegate.GetInvokeMethod (target_type); + + Arguments arguments = CreateDelegateMethodArguments (ec, invoke.Parameters, invoke.Parameters.Types, mg.Location); + return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.CovariantDelegate | OverloadResolver.Restrictions.ProbingOnly) != null; + } + + #region IErrorHandler Members + + bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous) + { + return false; + } + + bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index) + { + Error_ConversionFailed (rc, best as MethodSpec, null); + return true; + } + + bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best) + { + Error_ConversionFailed (rc, best as MethodSpec, null); + return true; + } + + bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best) + { + return false; + } + + #endregion + } + + // + // Created from the conversion code + // + public class ImplicitDelegateCreation : DelegateCreation + { + Field mg_cache; + + public ImplicitDelegateCreation (TypeSpec delegateType, MethodGroupExpr mg, Location loc) + { + type = delegateType; + this.method_group = mg; + this.loc = loc; + } + + // + // Returns true when type is MVAR or has MVAR reference + // + public static bool ContainsMethodTypeParameter (TypeSpec type) + { + var tps = type as TypeParameterSpec; + if (tps != null) + return tps.IsMethodOwned; + + var ec = type as ElementTypeSpec; + if (ec != null) + return ContainsMethodTypeParameter (ec.Element); + + foreach (var t in type.TypeArguments) { + if (ContainsMethodTypeParameter (t)) { + return true; + } + } + + if (type.IsNested) + return ContainsMethodTypeParameter (type.DeclaringType); + + return false; + } + + bool HasMvar () + { + if (ContainsMethodTypeParameter (type)) + return false; + + var best = method_group.BestCandidate; + if (ContainsMethodTypeParameter (best.DeclaringType)) + return false; + + if (best.TypeArguments != null) { + foreach (var ta in best.TypeArguments) { + if (ContainsMethodTypeParameter (ta)) + return false; + } + } + + return true; + } + + protected override Expression DoResolve (ResolveContext ec) + { + var expr = base.DoResolve (ec); + if (expr == null) + return ErrorExpression.Instance; + + if (ec.IsInProbingMode) + return expr; + + // + // Cache any static delegate creation + // + if (method_group.InstanceExpression != null) + return expr; + + // + // Cannot easily cache types with MVAR + // + if (!HasMvar ()) + return expr; + + // + // Create type level cache for a delegate instance + // + var parent = ec.CurrentMemberDefinition.Parent.PartialContainer; + int id = parent.MethodGroupsCounter++; + + mg_cache = new Field (parent, new TypeExpression (type, loc), + Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, + new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "mg$cache", id), loc), null); + mg_cache.Define (); + parent.AddField (mg_cache); + + return expr; + } + + public override void Emit (EmitContext ec) + { + Label l_initialized = ec.DefineLabel (); + + if (mg_cache != null) { + ec.Emit (OpCodes.Ldsfld, mg_cache.Spec); + ec.Emit (OpCodes.Brtrue_S, l_initialized); + } + + base.Emit (ec); + + if (mg_cache != null) { + ec.Emit (OpCodes.Stsfld, mg_cache.Spec); + ec.MarkLabel (l_initialized); + ec.Emit (OpCodes.Ldsfld, mg_cache.Spec); + } + } + } + + // + // A delegate-creation-expression, invoked from the `New' class + // + public class NewDelegate : DelegateCreation + { + public Arguments Arguments; + + // + // This constructor is invoked from the `New' expression + // + public NewDelegate (TypeSpec type, Arguments Arguments, Location loc) + { + this.type = type; + this.Arguments = Arguments; + this.loc = loc; + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (Arguments == null || Arguments.Count != 1) { + ec.Report.Error (149, loc, "Method name expected"); + return null; + } + + Argument a = Arguments [0]; + if (!a.ResolveMethodGroup (ec)) + return null; + + Expression e = a.Expr; + + AnonymousMethodExpression ame = e as AnonymousMethodExpression; + if (ame != null && ec.Module.Compiler.Settings.Version != LanguageVersion.ISO_1) { + e = ame.Compatible (ec, type); + if (e == null) + return null; + + return e.Resolve (ec); + } + + method_group = e as MethodGroupExpr; + if (method_group == null) { + if (e.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + e = Convert.ImplicitConversionRequired (ec, e, type, loc); + } else if (!e.Type.IsDelegate) { + e.Error_UnexpectedKind (ec, ResolveFlags.MethodGroup | ResolveFlags.Type, loc); + return null; + } + + // + // An argument is not a method but another delegate + // + method_group = new MethodGroupExpr (Delegate.GetInvokeMethod (e.Type), e.Type, loc); + method_group.InstanceExpression = e; + } + + return base.DoResolve (ec); + } + } + + // + // Invocation converted to delegate Invoke call + // + class DelegateInvocation : ExpressionStatement + { + readonly Expression InstanceExpr; + readonly bool conditionalAccessReceiver; + Arguments arguments; + MethodSpec method; + + public DelegateInvocation (Expression instance_expr, Arguments args, bool conditionalAccessReceiver, Location loc) + { + this.InstanceExpr = instance_expr; + this.arguments = args; + this.conditionalAccessReceiver = conditionalAccessReceiver; + this.loc = loc; + } + + public override bool ContainsEmitWithAwait () + { + return InstanceExpr.ContainsEmitWithAwait () || (arguments != null && arguments.ContainsEmitWithAwait ()); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = Arguments.CreateForExpressionTree (ec, this.arguments, + InstanceExpr.CreateExpressionTree (ec)); + + return CreateExpressionFactoryCall (ec, "Invoke", args); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + InstanceExpr.FlowAnalysis (fc); + if (arguments != null) + arguments.FlowAnalysis (fc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + TypeSpec del_type = InstanceExpr.Type; + if (del_type == null) + return null; + + // + // Do only core overload resolution the rest of the checks has been + // done on primary expression + // + method = Delegate.GetInvokeMethod (del_type); + var res = new OverloadResolver (new MemberSpec[] { method }, OverloadResolver.Restrictions.DelegateInvoke, loc); + var valid = res.ResolveMember (ec, ref arguments); + if (valid == null && !res.BestCandidateIsDynamic) + return null; + + type = method.ReturnType; + if (conditionalAccessReceiver) + type = LiftMemberType (ec, type); + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + if (conditionalAccessReceiver) { + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + } + + // + // Invocation on delegates call the virtual Invoke member + // so we are always `instance' calls + // + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpr; + call.Emit (ec, method, arguments, loc); + + if (conditionalAccessReceiver) + ec.CloseConditionalAccess (type.IsNullableType && type != method.ReturnType ? type : null); + } + + public override void EmitStatement (EmitContext ec) + { + if (conditionalAccessReceiver) { + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()) { + Statement = true + }; + } + + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpr; + call.EmitStatement (ec, method, arguments, loc); + + if (conditionalAccessReceiver) + ec.CloseConditionalAccess (null); + } + + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { + return Invocation.MakeExpression (ctx, InstanceExpr, method, arguments); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs new file mode 100644 index 000000000..35fe58529 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs @@ -0,0 +1,755 @@ +// +// doc.cs: Support for XML documentation comment. +// +// Authors: +// Atsushi Enomoto +// Marek Safar (marek.safar@gmail.com> +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2004 Novell, Inc. +// Copyright 2011 Xamarin Inc +// +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Linq; + +namespace Mono.CSharp +{ + // + // Implements XML documentation generation. + // + class DocumentationBuilder + { + // + // Used to create element which helps well-formedness checking. + // + readonly XmlDocument XmlDocumentation; + + readonly ModuleContainer module; + readonly ModuleContainer doc_module; + + // + // The output for XML documentation. + // + XmlWriter XmlCommentOutput; + + static readonly string line_head = Environment.NewLine + " "; + + // + // Stores XmlDocuments that are included in XML documentation. + // Keys are included filenames, values are XmlDocuments. + // + Dictionary StoredDocuments = new Dictionary (); + + ParserSession session; + + public DocumentationBuilder (ModuleContainer module) + { + doc_module = new ModuleContainer (module.Compiler); + doc_module.DocumentationBuilder = this; + + this.module = module; + XmlDocumentation = new XmlDocument (); + XmlDocumentation.PreserveWhitespace = false; + } + + Report Report { + get { + return module.Compiler.Report; + } + } + + public MemberName ParsedName { + get; set; + } + + public List ParsedParameters { + get; set; + } + + public TypeExpression ParsedBuiltinType { + get; set; + } + + public Operator.OpType? ParsedOperator { + get; set; + } + + XmlNode GetDocCommentNode (MemberCore mc, string name) + { + // FIXME: It could be even optimizable as not + // to use XmlDocument. But anyways the nodes + // are not kept in memory. + XmlDocument doc = XmlDocumentation; + try { + XmlElement el = doc.CreateElement ("member"); + el.SetAttribute ("name", name); + string normalized = mc.DocComment; + el.InnerXml = normalized; + // csc keeps lines as written in the sources + // and inserts formatting indentation (which + // is different from XmlTextWriter.Formatting + // one), but when a start tag contains an + // endline, it joins the next line. We don't + // have to follow such a hacky behavior. + string [] split = + normalized.Split ('\n'); + int j = 0; + for (int i = 0; i < split.Length; i++) { + string s = split [i].TrimEnd (); + if (s.Length > 0) + split [j++] = s; + } + el.InnerXml = line_head + String.Join ( + line_head, split, 0, j); + return el; + } catch (Exception ex) { + Report.Warning (1570, 1, mc.Location, "XML documentation comment on `{0}' is not well-formed XML markup ({1})", + mc.GetSignatureForError (), ex.Message); + + return doc.CreateComment (String.Format ("FIXME: Invalid documentation markup was found for member {0}", name)); + } + } + + // + // Generates xml doc comments (if any), and if required, + // handle warning report. + // + internal void GenerateDocumentationForMember (MemberCore mc) + { + string name = mc.DocCommentHeader + mc.GetSignatureForDocumentation (); + + XmlNode n = GetDocCommentNode (mc, name); + + XmlElement el = n as XmlElement; + if (el != null) { + var pm = mc as IParametersMember; + if (pm != null) { + CheckParametersComments (mc, pm, el); + } + + // FIXME: it could be done with XmlReader + XmlNodeList nl = n.SelectNodes (".//include"); + if (nl.Count > 0) { + // It could result in current node removal, so prepare another list to iterate. + var al = new List (nl.Count); + foreach (XmlNode inc in nl) + al.Add (inc); + foreach (XmlElement inc in al) + if (!HandleInclude (mc, inc)) + inc.ParentNode.RemoveChild (inc); + } + + // FIXME: it could be done with XmlReader + + foreach (XmlElement see in n.SelectNodes (".//see")) + HandleSee (mc, see); + foreach (XmlElement seealso in n.SelectNodes (".//seealso")) + HandleSeeAlso (mc, seealso); + foreach (XmlElement see in n.SelectNodes (".//exception")) + HandleException (mc, see); + foreach (XmlElement node in n.SelectNodes (".//typeparam")) + HandleTypeParam (mc, node); + foreach (XmlElement node in n.SelectNodes (".//typeparamref")) + HandleTypeParamRef (mc, node); + } + + n.WriteTo (XmlCommentOutput); + } + + // + // Processes "include" element. Check included file and + // embed the document content inside this documentation node. + // + bool HandleInclude (MemberCore mc, XmlElement el) + { + bool keep_include_node = false; + string file = el.GetAttribute ("file"); + string path = el.GetAttribute ("path"); + + if (file == "") { + Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `file' attribute"); + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el); + keep_include_node = true; + } else if (path.Length == 0) { + Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `path' attribute"); + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el); + keep_include_node = true; + } else { + XmlDocument doc; + Exception exception = null; + var full_path = Path.Combine (Path.GetDirectoryName (mc.Location.NameFullPath), file); + + if (!StoredDocuments.TryGetValue (full_path, out doc)) { + try { + doc = new XmlDocument (); + doc.Load (full_path); + StoredDocuments.Add (full_path, doc); + } catch (Exception e) { + exception = e; + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (String.Format (" Badly formed XML in at comment file `{0}': cannot be included ", file)), el); + } + } + + if (doc != null) { + try { + XmlNodeList nl = doc.SelectNodes (path); + if (nl.Count == 0) { + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" No matching elements were found for the include tag embedded here. "), el); + + keep_include_node = true; + } + foreach (XmlNode n in nl) + el.ParentNode.InsertBefore (el.OwnerDocument.ImportNode (n, true), el); + } catch (Exception ex) { + exception = ex; + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Failed to insert some or all of included XML "), el); + } + } + + if (exception != null) { + Report.Warning (1589, 1, mc.Location, "Unable to include XML fragment `{0}' of file `{1}'. {2}", + path, file, exception.Message); + } + } + + return keep_include_node; + } + + // + // Handles elements. + // + void HandleSee (MemberCore mc, XmlElement see) + { + HandleXrefCommon (mc, see); + } + + // + // Handles elements. + // + void HandleSeeAlso (MemberCore mc, XmlElement seealso) + { + HandleXrefCommon (mc, seealso); + } + + // + // Handles elements. + // + void HandleException (MemberCore mc, XmlElement seealso) + { + HandleXrefCommon (mc, seealso); + } + + // + // Handles node + // + static void HandleTypeParam (MemberCore mc, XmlElement node) + { + if (!node.HasAttribute ("name")) + return; + + string tp_name = node.GetAttribute ("name"); + if (mc.CurrentTypeParameters != null) { + if (mc.CurrentTypeParameters.Find (tp_name) != null) + return; + } + + // TODO: CS1710, CS1712 + + mc.Compiler.Report.Warning (1711, 2, mc.Location, + "XML comment on `{0}' has a typeparam name `{1}' but there is no type parameter by that name", + mc.GetSignatureForError (), tp_name); + } + + // + // Handles node + // + static void HandleTypeParamRef (MemberCore mc, XmlElement node) + { + if (!node.HasAttribute ("name")) + return; + + string tp_name = node.GetAttribute ("name"); + var member = mc; + do { + if (member.CurrentTypeParameters != null) { + if (member.CurrentTypeParameters.Find (tp_name) != null) + return; + } + + member = member.Parent; + } while (member != null); + + mc.Compiler.Report.Warning (1735, 2, mc.Location, + "XML comment on `{0}' has a typeparamref name `{1}' that could not be resolved", + mc.GetSignatureForError (), tp_name); + } + + FullNamedExpression ResolveMemberName (IMemberContext context, MemberName mn) + { + if (mn.Left == null) + return context.LookupNamespaceOrType (mn.Name, mn.Arity, LookupMode.Probing, Location.Null); + + var left = ResolveMemberName (context, mn.Left); + var ns = left as NamespaceExpression; + if (ns != null) + return ns.LookupTypeOrNamespace (context, mn.Name, mn.Arity, LookupMode.Probing, Location.Null); + + TypeExpr texpr = left as TypeExpr; + if (texpr != null) { + var found = MemberCache.FindNestedType (texpr.Type, mn.Name, mn.Arity); + if (found != null) + return new TypeExpression (found, Location.Null); + + return null; + } + + return left; + } + + // + // Processes "see" or "seealso" elements from cref attribute. + // + void HandleXrefCommon (MemberCore mc, XmlElement xref) + { + string cref = xref.GetAttribute ("cref"); + // when, XmlReader, "if (cref == null)" + if (!xref.HasAttribute ("cref")) + return; + + // Nothing to be resolved the reference is marked explicitly + if (cref.Length > 2 && cref [1] == ':') + return; + + // Additional symbols for < and > are allowed for easier XML typing + cref = cref.Replace ('{', '<').Replace ('}', '>'); + + var encoding = module.Compiler.Settings.Encoding; + var s = new MemoryStream (encoding.GetBytes (cref)); + + var source_file = new CompilationSourceFile (doc_module, mc.Location.SourceFile); + var report = new Report (doc_module.Compiler, new NullReportPrinter ()); + + if (session == null) + session = new ParserSession { + UseJayGlobalArrays = true + }; + + SeekableStreamReader seekable = new SeekableStreamReader (s, encoding, session.StreamReaderBuffer); + + var parser = new CSharpParser (seekable, source_file, report, session); + ParsedParameters = null; + ParsedName = null; + ParsedBuiltinType = null; + ParsedOperator = null; + parser.Lexer.putback_char = Tokenizer.DocumentationXref; + parser.Lexer.parsing_generic_declaration_doc = true; + parser.parse (); + if (report.Errors > 0) { + Report.Warning (1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'", + mc.GetSignatureForError (), cref); + + xref.SetAttribute ("cref", "!:" + cref); + return; + } + + MemberSpec member; + string prefix = null; + FullNamedExpression fne = null; + + // + // Try built-in type first because we are using ParsedName as identifier of + // member names on built-in types + // + if (ParsedBuiltinType != null && (ParsedParameters == null || ParsedName != null)) { + member = ParsedBuiltinType.Type; + } else { + member = null; + } + + if (ParsedName != null || ParsedOperator.HasValue) { + TypeSpec type = null; + string member_name = null; + + if (member == null) { + if (ParsedOperator.HasValue) { + type = mc.CurrentType; + } else if (ParsedName.Left != null) { + fne = ResolveMemberName (mc, ParsedName.Left); + if (fne != null) { + var ns = fne as NamespaceExpression; + if (ns != null) { + fne = ns.LookupTypeOrNamespace (mc, ParsedName.Name, ParsedName.Arity, LookupMode.Probing, Location.Null); + if (fne != null) { + member = fne.Type; + } + } else { + type = fne.Type; + } + } + } else { + fne = ResolveMemberName (mc, ParsedName); + if (fne == null) { + type = mc.CurrentType; + } else if (ParsedParameters == null) { + member = fne.Type; + } else if (fne.Type.MemberDefinition == mc.CurrentType.MemberDefinition) { + member_name = Constructor.ConstructorName; + type = fne.Type; + } + } + } else { + type = (TypeSpec) member; + member = null; + } + + if (ParsedParameters != null) { + var old_printer = mc.Module.Compiler.Report.SetPrinter (new NullReportPrinter ()); + try { + var context = new DocumentationMemberContext (mc, ParsedName ?? MemberName.Null); + + foreach (var pp in ParsedParameters) { + pp.Resolve (context); + } + } finally { + mc.Module.Compiler.Report.SetPrinter (old_printer); + } + } + + if (type != null) { + if (member_name == null) + member_name = ParsedOperator.HasValue ? + Operator.GetMetadataName (ParsedOperator.Value) : ParsedName.Name; + + int parsed_param_count; + if (ParsedOperator == Operator.OpType.Explicit || ParsedOperator == Operator.OpType.Implicit) { + parsed_param_count = ParsedParameters.Count - 1; + } else if (ParsedParameters != null) { + parsed_param_count = ParsedParameters.Count; + } else { + parsed_param_count = 0; + } + + int parameters_match = -1; + do { + var members = MemberCache.FindMembers (type, member_name, true); + if (members != null) { + foreach (var m in members) { + if (ParsedName != null && m.Arity != ParsedName.Arity) + continue; + + if (ParsedParameters != null) { + IParametersMember pm = m as IParametersMember; + if (pm == null) + continue; + + if (m.Kind == MemberKind.Operator && !ParsedOperator.HasValue) + continue; + + var pm_params = pm.Parameters; + + int i; + for (i = 0; i < parsed_param_count; ++i) { + var pparam = ParsedParameters[i]; + + if (i >= pm_params.Count || pparam == null || pparam.TypeSpec == null || + !TypeSpecComparer.Override.IsEqual (pparam.TypeSpec, pm_params.Types[i]) || + (pparam.Modifier & Parameter.Modifier.RefOutMask) != (pm_params.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { + + if (i > parameters_match) { + parameters_match = i; + } + + i = -1; + break; + } + } + + if (i < 0) + continue; + + if (ParsedOperator == Operator.OpType.Explicit || ParsedOperator == Operator.OpType.Implicit) { + if (pm.MemberType != ParsedParameters[parsed_param_count].TypeSpec) { + parameters_match = parsed_param_count + 1; + continue; + } + } else { + if (parsed_param_count != pm_params.Count) + continue; + } + } + + if (member != null) { + Report.Warning (419, 3, mc.Location, + "Ambiguous reference in cref attribute `{0}'. Assuming `{1}' but other overloads including `{2}' have also matched", + cref, member.GetSignatureForError (), m.GetSignatureForError ()); + + break; + } + + member = m; + } + } + + // Continue with parent type for nested types + if (member == null) { + type = type.DeclaringType; + } else { + type = null; + } + } while (type != null); + + if (member == null && parameters_match >= 0) { + for (int i = parameters_match; i < parsed_param_count; ++i) { + Report.Warning (1580, 1, mc.Location, "Invalid type for parameter `{0}' in XML comment cref attribute `{1}'", + (i + 1).ToString (), cref); + } + + if (parameters_match == parsed_param_count + 1) { + Report.Warning (1581, 1, mc.Location, "Invalid return type in XML comment cref attribute `{0}'", cref); + } + } + } + } + + if (member == null) { + Report.Warning (1574, 1, mc.Location, "XML comment on `{0}' has cref attribute `{1}' that could not be resolved", + mc.GetSignatureForError (), cref); + cref = "!:" + cref; + } else if (member == InternalType.Namespace) { + cref = "N:" + fne.GetSignatureForError (); + } else { + prefix = GetMemberDocHead (member); + cref = prefix + member.GetSignatureForDocumentation (); + } + + xref.SetAttribute ("cref", cref); + } + + // + // Get a prefix from member type for XML documentation (used + // to formalize cref target name). + // + static string GetMemberDocHead (MemberSpec type) + { + if (type is FieldSpec) + return "F:"; + if (type is MethodSpec) + return "M:"; + if (type is EventSpec) + return "E:"; + if (type is PropertySpec) + return "P:"; + if (type is TypeSpec) + return "T:"; + + throw new NotImplementedException (type.GetType ().ToString ()); + } + + // + // Raised (and passed an XmlElement that contains the comment) + // when GenerateDocComment is writing documentation expectedly. + // + // FIXME: with a few effort, it could be done with XmlReader, + // that means removal of DOM use. + // + void CheckParametersComments (MemberCore member, IParametersMember paramMember, XmlElement el) + { + HashSet found_tags = null; + foreach (XmlElement pelem in el.SelectNodes ("param")) { + string xname = pelem.GetAttribute ("name"); + if (xname.Length == 0) + continue; // really? but MS looks doing so + + if (found_tags == null) { + found_tags = new HashSet (); + } + + if (xname != "" && paramMember.Parameters.GetParameterIndexByName (xname) < 0) { + Report.Warning (1572, 2, member.Location, + "XML comment on `{0}' has a param tag for `{1}', but there is no parameter by that name", + member.GetSignatureForError (), xname); + continue; + } + + if (found_tags.Contains (xname)) { + Report.Warning (1571, 2, member.Location, + "XML comment on `{0}' has a duplicate param tag for `{1}'", + member.GetSignatureForError (), xname); + continue; + } + + found_tags.Add (xname); + } + + if (found_tags != null) { + foreach (Parameter p in paramMember.Parameters.FixedParameters) { + if (!found_tags.Contains (p.Name) && !(p is ArglistParameter)) + Report.Warning (1573, 4, member.Location, + "Parameter `{0}' has no matching param tag in the XML comment for `{1}'", + p.Name, member.GetSignatureForError ()); + } + } + } + + // + // Outputs XML documentation comment from tokenized comments. + // + public bool OutputDocComment (string asmfilename, string xmlFileName) + { + XmlTextWriter w = null; + try { + w = new XmlTextWriter (xmlFileName, null); + w.Indentation = 4; + w.Formatting = Formatting.Indented; + w.WriteStartDocument (); + w.WriteStartElement ("doc"); + w.WriteStartElement ("assembly"); + w.WriteStartElement ("name"); + w.WriteString (Path.GetFileNameWithoutExtension (asmfilename)); + w.WriteEndElement (); // name + w.WriteEndElement (); // assembly + w.WriteStartElement ("members"); + XmlCommentOutput = w; + module.GenerateDocComment (this); + w.WriteFullEndElement (); // members + w.WriteEndElement (); + w.WriteWhitespace (Environment.NewLine); + w.WriteEndDocument (); + return true; + } catch (Exception ex) { + Report.Error (1569, "Error generating XML documentation file `{0}' (`{1}')", xmlFileName, ex.Message); + return false; + } finally { + if (w != null) + w.Close (); + } + } + } + + // + // Type lookup of documentation references uses context of type where + // the reference is used but type parameters from cref value + // + sealed class DocumentationMemberContext : IMemberContext + { + readonly MemberCore host; + MemberName contextName; + + public DocumentationMemberContext (MemberCore host, MemberName contextName) + { + this.host = host; + this.contextName = contextName; + } + + public TypeSpec CurrentType { + get { + return host.CurrentType; + } + } + + public TypeParameters CurrentTypeParameters { + get { + return contextName.TypeParameters; + } + } + + public MemberCore CurrentMemberDefinition { + get { + return host.CurrentMemberDefinition; + } + } + + public bool IsObsolete { + get { + return false; + } + } + + public bool IsUnsafe { + get { + return host.IsStatic; + } + } + + public bool IsStatic { + get { + return host.IsStatic; + } + } + + public ModuleContainer Module { + get { + return host.Module; + } + } + + public string GetSignatureForError () + { + return host.GetSignatureForError (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + return null; + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + if (arity == 0) { + var tp = CurrentTypeParameters; + if (tp != null) { + for (int i = 0; i < tp.Count; ++i) { + var t = tp[i]; + if (t.Name == name) { + t.Type.DeclaredPosition = i; + return new TypeParameterExpr (t, loc); + } + } + } + } + + return host.Parent.LookupNamespaceOrType (name, arity, mode, loc); + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + throw new NotImplementedException (); + } + } + + class DocumentationParameter + { + public readonly Parameter.Modifier Modifier; + public FullNamedExpression Type; + TypeSpec type; + + public DocumentationParameter (Parameter.Modifier modifier, FullNamedExpression type) + : this (type) + { + this.Modifier = modifier; + } + + public DocumentationParameter (FullNamedExpression type) + { + this.Type = type; + } + + public TypeSpec TypeSpec { + get { + return type; + } + } + + public void Resolve (IMemberContext context) + { + type = Type.ResolveAsType (context); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs new file mode 100644 index 000000000..6ef6859e8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs @@ -0,0 +1,475 @@ +// +// driver.cs: The compiler command line driver. +// +// Authors: +// Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Globalization; +using System.Diagnostics; +using System.Threading; + +namespace Mono.CSharp +{ + /// + /// The compiler driver. + /// + class Driver + { + readonly CompilerContext ctx; + + public Driver (CompilerContext ctx) + { + this.ctx = ctx; + } + + Report Report { + get { + return ctx.Report; + } + } + + void tokenize_file (SourceFile sourceFile, ModuleContainer module, ParserSession session) + { + Stream input; + + try { + input = File.OpenRead (sourceFile.Name); + } catch { + Report.Error (2001, "Source file `" + sourceFile.Name + "' could not be found"); + return; + } + + using (input){ + SeekableStreamReader reader = new SeekableStreamReader (input, ctx.Settings.Encoding); + var file = new CompilationSourceFile (module, sourceFile); + + Tokenizer lexer = new Tokenizer (reader, file, session, ctx.Report); + int token, tokens = 0, errors = 0; + + while ((token = lexer.token ()) != Token.EOF){ + tokens++; + if (token == Token.ERROR) + errors++; + } + Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors"); + } + + return; + } + + void Parse (ModuleContainer module) + { + bool tokenize_only = module.Compiler.Settings.TokenizeOnly; + var sources = module.Compiler.SourceFiles; + + Location.Initialize (sources); + + var session = new ParserSession { + UseJayGlobalArrays = true, + LocatedTokens = new LocatedToken[15000] + }; + + for (int i = 0; i < sources.Count; ++i) { + if (tokenize_only) { + tokenize_file (sources[i], module, session); + } else { + Parse (sources[i], module, session, Report); + } + } + } + +#if false + void ParseParallel (ModuleContainer module) + { + var sources = module.Compiler.SourceFiles; + + Location.Initialize (sources); + + var pcount = Environment.ProcessorCount; + var threads = new Thread[System.Math.Max (2, pcount - 1)]; + + for (int i = 0; i < threads.Length; ++i) { + var t = new Thread (l => { + var session = new ParserSession () { + //UseJayGlobalArrays = true, + }; + + var report = new Report (ctx, Report.Printer); // TODO: Implement flush at once printer + + for (int ii = (int) l; ii < sources.Count; ii += threads.Length) { + Parse (sources[ii], module, session, report); + } + + // TODO: Merge warning regions + }); + + t.Start (i); + threads[i] = t; + } + + for (int t = 0; t < threads.Length; ++t) { + threads[t].Join (); + } + } +#endif + + public void Parse (SourceFile file, ModuleContainer module, ParserSession session, Report report) + { + Stream input; + + try { + input = File.OpenRead (file.Name); + } catch { + report.Error (2001, "Source file `{0}' could not be found", file.Name); + return; + } + + // Check 'MZ' header + if (input.ReadByte () == 77 && input.ReadByte () == 90) { + + report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name); + input.Close (); + return; + } + + input.Position = 0; + SeekableStreamReader reader = new SeekableStreamReader (input, ctx.Settings.Encoding, session.StreamReaderBuffer); + + Parse (reader, file, module, session, report); + + if (ctx.Settings.GenerateDebugInfo && report.Errors == 0 && !file.HasChecksum) { + input.Position = 0; + var checksum = session.GetChecksumAlgorithm (); + file.SetChecksum (checksum.ComputeHash (input)); + } + + reader.Dispose (); + input.Close (); + } + + public static CSharpParser Parse (SeekableStreamReader reader, SourceFile sourceFile, ModuleContainer module, ParserSession session, Report report, int lineModifier = 0, int colModifier = 0) + { + var file = new CompilationSourceFile (module, sourceFile); + module.AddTypeContainer(file); + + CSharpParser parser = new CSharpParser (reader, file, report, session); + parser.Lexer.Line += lineModifier; + parser.Lexer.Column += colModifier; + parser.Lexer.sbag = new SpecialsBag (); + parser.parse (); + return parser; + } + + public static int Main (string[] args) + { + Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t"; + + CommandLineParser cmd = new CommandLineParser (Console.Out); + var settings = cmd.ParseArguments (args); + if (settings == null) + return 1; + + if (cmd.HasBeenStopped) + return 0; + + Driver d = new Driver (new CompilerContext (settings, new ConsoleReportPrinter ())); + + if (d.Compile () && d.Report.Errors == 0) { + if (d.Report.Warnings > 0) { + Console.WriteLine ("Compilation succeeded - {0} warning(s)", d.Report.Warnings); + } + Environment.Exit (0); + return 0; + } + + + Console.WriteLine("Compilation failed: {0} error(s), {1} warnings", + d.Report.Errors, d.Report.Warnings); + Environment.Exit (1); + return 1; + } + + public static string GetPackageFlags (string packages, Report report) + { + ProcessStartInfo pi = new ProcessStartInfo (); + pi.FileName = "pkg-config"; + pi.RedirectStandardOutput = true; + pi.UseShellExecute = false; + pi.Arguments = "--libs " + packages; + Process p = null; + try { + p = Process.Start (pi); + } catch (Exception e) { + if (report == null) + throw; + + report.Error (-27, "Couldn't run pkg-config: " + e.Message); + return null; + } + + if (p.StandardOutput == null) { + if (report == null) + throw new ApplicationException ("Specified package did not return any information"); + + report.Warning (-27, 1, "Specified package did not return any information"); + p.Close (); + return null; + } + + string pkgout = p.StandardOutput.ReadToEnd (); + p.WaitForExit (); + if (p.ExitCode != 0) { + if (report == null) + throw new ApplicationException (pkgout); + + report.Error (-27, "Error running pkg-config. Check the above output."); + p.Close (); + return null; + } + + p.Close (); + return pkgout; + } + + // + // Main compilation method + // + public bool Compile () + { + var settings = ctx.Settings; + + // + // If we are an exe, require a source file for the entry point or + // if there is nothing to put in the assembly, and we are not a library + // + if (settings.FirstSourceFile == null && + ((settings.Target == Target.Exe || settings.Target == Target.WinExe || settings.Target == Target.Module) || + settings.Resources == null)) { + Report.Error (2008, "No files to compile were specified"); + return false; + } + + if (settings.Platform == Platform.AnyCPU32Preferred && (settings.Target == Target.Library || settings.Target == Target.Module)) { + Report.Error (4023, "Platform option `anycpu32bitpreferred' is valid only for executables"); + return false; + } + + TimeReporter tr = new TimeReporter (settings.Timestamps); + ctx.TimeReporter = tr; + tr.StartTotal (); + + var module = new ModuleContainer (ctx); + RootContext.ToplevelTypes = module; + + tr.Start (TimeReporter.TimerType.ParseTotal); + Parse (module); + tr.Stop (TimeReporter.TimerType.ParseTotal); + + if (Report.Errors > 0) + return false; + + if (settings.TokenizeOnly || settings.ParseOnly) { + tr.StopTotal (); + tr.ShowStats (); + return true; + } + + var output_file = settings.OutputFile; + string output_file_name; + if (output_file == null) { + var source_file = settings.FirstSourceFile; + + if (source_file == null) { + Report.Error (1562, "If no source files are specified you must specify the output file with -out:"); + return false; + } + + output_file_name = source_file.Name; + int pos = output_file_name.LastIndexOf ('.'); + + if (pos > 0) + output_file_name = output_file_name.Substring (0, pos); + + output_file_name += settings.TargetExt; + output_file = output_file_name; + } else { + output_file_name = Path.GetFileName (output_file); + + if (string.IsNullOrEmpty (Path.GetFileNameWithoutExtension (output_file_name)) || + output_file_name.IndexOfAny (Path.GetInvalidFileNameChars ()) >= 0) { + Report.Error (2021, "Output file name is not valid"); + return false; + } + } + +#if STATIC + var importer = new StaticImporter (module); + var references_loader = new StaticLoader (importer, ctx); + + tr.Start (TimeReporter.TimerType.AssemblyBuilderSetup); + var assembly = new AssemblyDefinitionStatic (module, references_loader, output_file_name, output_file); + assembly.Create (references_loader.Domain); + tr.Stop (TimeReporter.TimerType.AssemblyBuilderSetup); + + // Create compiler types first even before any referenced + // assembly is loaded to allow forward referenced types from + // loaded assembly into compiled builder to be resolved + // correctly + tr.Start (TimeReporter.TimerType.CreateTypeTotal); + module.CreateContainer (); + importer.AddCompiledAssembly (assembly); + references_loader.CompiledAssembly = assembly; + tr.Stop (TimeReporter.TimerType.CreateTypeTotal); + + references_loader.LoadReferences (module); + + tr.Start (TimeReporter.TimerType.PredefinedTypesInit); + if (!ctx.BuiltinTypes.CheckDefinitions (module)) + return false; + + tr.Stop (TimeReporter.TimerType.PredefinedTypesInit); + + references_loader.LoadModules (assembly, module.GlobalRootNamespace); +#else + var assembly = new AssemblyDefinitionDynamic (module, output_file_name, output_file); + module.SetDeclaringAssembly (assembly); + + var importer = new ReflectionImporter (module, ctx.BuiltinTypes); + assembly.Importer = importer; + + var loader = new DynamicLoader (importer, ctx); + loader.LoadReferences (module); + + if (!ctx.BuiltinTypes.CheckDefinitions (module)) + return false; + + if (!assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Save)) + return false; + + module.CreateContainer (); + + loader.LoadModules (assembly, module.GlobalRootNamespace); +#endif + module.InitializePredefinedTypes (); + + tr.Start (TimeReporter.TimerType.ModuleDefinitionTotal); + module.Define (); + tr.Stop (TimeReporter.TimerType.ModuleDefinitionTotal); + + if (Report.Errors > 0) + return false; + + if (settings.DocumentationFile != null) { + var doc = new DocumentationBuilder (module); + doc.OutputDocComment (output_file, settings.DocumentationFile); + } + + assembly.Resolve (); + + if (Report.Errors > 0) + return false; + + + tr.Start (TimeReporter.TimerType.EmitTotal); + assembly.Emit (); + tr.Stop (TimeReporter.TimerType.EmitTotal); + + if (Report.Errors > 0){ + return false; + } + + tr.Start (TimeReporter.TimerType.CloseTypes); + module.CloseContainer (); + tr.Stop (TimeReporter.TimerType.CloseTypes); + + tr.Start (TimeReporter.TimerType.Resouces); + if (!settings.WriteMetadataOnly) + assembly.EmbedResources (); + tr.Stop (TimeReporter.TimerType.Resouces); + + if (Report.Errors > 0) + return false; + + assembly.Save (); + +#if STATIC + references_loader.Dispose (); +#endif + tr.StopTotal (); + tr.ShowStats (); + + return Report.Errors == 0; + } + } + + public class CompilerCompilationUnit { + public ModuleContainer ModuleCompiled { get; set; } + public LocationsBag LocationsBag { get; set; } + public SpecialsBag SpecialsBag { get; set; } + public IDictionary Conditionals { get; set; } + public object LastYYValue { get; set; } + } + + // + // This is the only public entry point + // + public class CompilerCallableEntryPoint : MarshalByRefObject + { + public static bool InvokeCompiler (string [] args, TextWriter error) + { + try { + CommandLineParser cmd = new CommandLineParser (error); + var setting = cmd.ParseArguments (args); + if (setting == null) + return false; + + var d = new Driver (new CompilerContext (setting, new StreamReportPrinter (error))); + return d.Compile (); + } finally { + Reset (); + } + } + + public static int[] AllWarningNumbers { + get { + return Report.AllWarnings; + } + } + + public static void Reset () + { + Reset (true); + } + + public static void PartialReset () + { + Reset (false); + } + + public static void Reset (bool full_flag) + { + Location.Reset (); + + if (!full_flag) + return; + + Linq.QueryBlock.TransparentParameter.Reset (); + TypeInfo.Reset (); + } + } + +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/dynamic.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/dynamic.cs new file mode 100644 index 000000000..5604a3fcd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/dynamic.cs @@ -0,0 +1,986 @@ +// +// dynamic.cs: support for dynamic expressions +// +// Authors: Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2009 Novell, Inc +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Linq; +using SLE = System.Linq.Expressions; + +#if NET_4_0 || MOBILE_DYNAMIC +using System.Dynamic; +#endif + +namespace Mono.CSharp +{ + // + // A copy of Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/CSharpBinderFlags.cs + // has to be kept in sync + // + [Flags] + public enum CSharpBinderFlags + { + None = 0, + CheckedContext = 1, + InvokeSimpleName = 1 << 1, + InvokeSpecialName = 1 << 2, + BinaryOperationLogical = 1 << 3, + ConvertExplicit = 1 << 4, + ConvertArrayIndex = 1 << 5, + ResultIndexed = 1 << 6, + ValueFromCompoundAssignment = 1 << 7, + ResultDiscarded = 1 << 8 + } + + // + // Type expression with internal dynamic type symbol + // + class DynamicTypeExpr : TypeExpr + { + public DynamicTypeExpr (Location loc) + { + this.loc = loc; + } + + public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments) + { + eclass = ExprClass.Type; + type = ec.Module.Compiler.BuiltinTypes.Dynamic; + return type; + } + } + + #region Dynamic runtime binder expressions + + // + // Expression created from runtime dynamic object value by dynamic binder + // + public class RuntimeValueExpression : Expression, IDynamicAssign, IMemoryLocation + { +#if !NET_4_0 && !MOBILE_DYNAMIC + public class DynamicMetaObject + { + public TypeSpec RuntimeType; + public TypeSpec LimitType; + public SLE.Expression Expression; + } +#endif + + readonly DynamicMetaObject obj; + + public RuntimeValueExpression (DynamicMetaObject obj, TypeSpec type) + { + this.obj = obj; + this.type = type; + this.eclass = ExprClass.Variable; + } + + #region Properties + + public bool IsSuggestionOnly { get; set; } + + public DynamicMetaObject MetaObject { + get { return obj; } + } + + #endregion + + public void AddressOf (EmitContext ec, AddressOp mode) + { + throw new NotImplementedException (); + } + + public override bool ContainsEmitWithAwait () + { + throw new NotSupportedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return this; + } + + public override void Emit (EmitContext ec) + { + throw new NotImplementedException (); + } + + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + throw new NotImplementedException (); + } + + #endregion + + public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { + return obj.Expression; + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + +#if NET_4_0 || MOBILE_DYNAMIC + if (type.IsStruct && !obj.Expression.Type.IsValueType) + return SLE.Expression.Unbox (obj.Expression, type.GetMetaInfo ()); + + if (obj.Expression.NodeType == SLE.ExpressionType.Parameter) { + if (((SLE.ParameterExpression) obj.Expression).IsByRef) + return obj.Expression; + } + #endif + + return SLE.Expression.Convert (obj.Expression, type.GetMetaInfo ()); +#endif + } + } + + // + // Wraps runtime dynamic expression into expected type. Needed + // to satify expected type check by dynamic binder and no conversion + // is required (ResultDiscarded). + // + public class DynamicResultCast : ShimExpression + { + public DynamicResultCast (TypeSpec type, Expression expr) + : base (expr) + { + this.type = type; + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + eclass = ExprClass.Value; + return this; + } + +#if NET_4_0 || MOBILE_DYNAMIC + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Block (expr.MakeExpression (ctx), SLE.Expression.Default (type.GetMetaInfo ())); +#endif + } +#endif + } + + #endregion + + // + // Creates dynamic binder expression + // + interface IDynamicBinder + { + Expression CreateCallSiteBinder (ResolveContext ec, Arguments args); + } + + // + // Extends standard assignment interface for expressions + // supported by dynamic resolver + // + interface IDynamicAssign : IAssignMethod + { + SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source); + } + + // + // Base dynamic expression statement creator + // + class DynamicExpressionStatement : ExpressionStatement + { + // + // Binder flag dynamic constant, the value is combination of + // flags known at resolve stage and flags known only at emit + // stage + // + protected class BinderFlags : EnumConstant + { + readonly DynamicExpressionStatement statement; + readonly CSharpBinderFlags flags; + + public BinderFlags (CSharpBinderFlags flags, DynamicExpressionStatement statement) + : base (statement.loc) + { + this.flags = flags; + this.statement = statement; + eclass = 0; + } + + protected override Expression DoResolve (ResolveContext ec) + { + Child = new IntConstant (ec.BuiltinTypes, (int) (flags | statement.flags), statement.loc); + + type = ec.Module.PredefinedTypes.BinderFlags.Resolve (); + eclass = Child.eclass; + return this; + } + } + + readonly Arguments arguments; + protected IDynamicBinder binder; + protected Expression binder_expr; + + // Used by BinderFlags + protected CSharpBinderFlags flags; + + TypeSpec binder_type; + TypeParameters context_mvars; + + public DynamicExpressionStatement (IDynamicBinder binder, Arguments args, Location loc) + { + this.binder = binder; + this.arguments = args; + this.loc = loc; + } + + public Arguments Arguments { + get { + return arguments; + } + } + + public override bool ContainsEmitWithAwait () + { + return arguments.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (1963, loc, "An expression tree cannot contain a dynamic operation"); + return null; + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (DoResolveCore (rc)) + binder_expr = binder.CreateCallSiteBinder (rc, arguments); + + return this; + } + + protected bool DoResolveCore (ResolveContext rc) + { + if (rc.CurrentTypeParameters != null && rc.CurrentTypeParameters[0].IsMethodTypeParameter) + context_mvars = rc.CurrentTypeParameters; + + int errors = rc.Report.Errors; + var pt = rc.Module.PredefinedTypes; + + binder_type = pt.Binder.Resolve (); + pt.CallSite.Resolve (); + pt.CallSiteGeneric.Resolve (); + + eclass = ExprClass.Value; + + if (type == null) + type = rc.BuiltinTypes.Dynamic; + + if (rc.Report.Errors == errors) + return true; + + rc.Report.Error (1969, loc, + "Dynamic operation cannot be compiled without `Microsoft.CSharp.dll' assembly reference"); + return false; + } + + public override void Emit (EmitContext ec) + { + EmitCall (ec, binder_expr, arguments, false); + } + + public override void EmitStatement (EmitContext ec) + { + EmitCall (ec, binder_expr, arguments, true); + } + + protected void EmitCall (EmitContext ec, Expression binder, Arguments arguments, bool isStatement) + { + // + // This method generates all internal infrastructure for a dynamic call. The + // reason why it's quite complicated is the mixture of dynamic and anonymous + // methods. Dynamic itself requires a temporary class (ContainerX) and anonymous + // methods can generate temporary storey as well (AnonStorey). Handling MVAR + // type parameters rewrite is non-trivial in such case as there are various + // combinations possible therefore the mutator is not straightforward. Secondly + // we need to keep both MVAR(possibly VAR for anon storey) and type VAR to emit + // correct Site field type and its access from EmitContext. + // + + int dyn_args_count = arguments == null ? 0 : arguments.Count; + int default_args = isStatement ? 1 : 2; + var module = ec.Module; + + bool has_ref_out_argument = false; + var targs = new TypeExpression[dyn_args_count + default_args]; + targs[0] = new TypeExpression (module.PredefinedTypes.CallSite.TypeSpec, loc); + + TypeExpression[] targs_for_instance = null; + TypeParameterMutator mutator; + + var site_container = ec.CreateDynamicSite (); + + if (context_mvars != null) { + TypeParameters tparam; + TypeContainer sc = site_container; + do { + tparam = sc.CurrentTypeParameters; + sc = sc.Parent; + } while (tparam == null); + + mutator = new TypeParameterMutator (context_mvars, tparam); + + if (!ec.IsAnonymousStoreyMutateRequired) { + targs_for_instance = new TypeExpression[targs.Length]; + targs_for_instance[0] = targs[0]; + } + } else { + mutator = null; + } + + for (int i = 0; i < dyn_args_count; ++i) { + Argument a = arguments[i]; + if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref) + has_ref_out_argument = true; + + var t = a.Type; + + // Convert any internal type like dynamic or null to object + if (t.Kind == MemberKind.InternalCompilerType) + t = ec.BuiltinTypes.Object; + + if (targs_for_instance != null) + targs_for_instance[i + 1] = new TypeExpression (t, loc); + + if (mutator != null) + t = t.Mutate (mutator); + + targs[i + 1] = new TypeExpression (t, loc); + } + + TypeExpr del_type = null; + TypeExpr del_type_instance_access = null; + if (!has_ref_out_argument) { + string d_name = isStatement ? "Action" : "Func"; + + TypeSpec te = null; + Namespace type_ns = module.GlobalRootNamespace.GetNamespace ("System", true); + if (type_ns != null) { + te = type_ns.LookupType (module, d_name, dyn_args_count + default_args, LookupMode.Normal, loc); + } + + if (te != null) { + if (!isStatement) { + var t = type; + if (t.Kind == MemberKind.InternalCompilerType) + t = ec.BuiltinTypes.Object; + + if (targs_for_instance != null) + targs_for_instance[targs_for_instance.Length - 1] = new TypeExpression (t, loc); + + if (mutator != null) + t = t.Mutate (mutator); + + targs[targs.Length - 1] = new TypeExpression (t, loc); + } + + del_type = new GenericTypeExpr (te, new TypeArguments (targs), loc); + if (targs_for_instance != null) + del_type_instance_access = new GenericTypeExpr (te, new TypeArguments (targs_for_instance), loc); + else + del_type_instance_access = del_type; + } + } + + // + // Create custom delegate when no appropriate predefined delegate has been found + // + Delegate d; + if (del_type == null) { + TypeSpec rt = isStatement ? ec.BuiltinTypes.Void : type; + Parameter[] p = new Parameter[dyn_args_count + 1]; + p[0] = new Parameter (targs[0], "p0", Parameter.Modifier.NONE, null, loc); + + var site = ec.CreateDynamicSite (); + int index = site.Containers == null ? 0 : site.Containers.Count; + + if (mutator != null) + rt = mutator.Mutate (rt); + + for (int i = 1; i < dyn_args_count + 1; ++i) { + p[i] = new Parameter (targs[i], "p" + i.ToString ("X"), arguments[i - 1].Modifier, null, loc); + } + + d = new Delegate (site, new TypeExpression (rt, loc), + Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED, + new MemberName ("Container" + index.ToString ("X")), + new ParametersCompiled (p), null); + + d.CreateContainer (); + d.DefineContainer (); + d.Define (); + d.PrepareEmit (); + + site.AddTypeContainer (d); + + // + // Add new container to inflated site container when the + // member cache already exists + // + if (site.CurrentType is InflatedTypeSpec && index > 0) + site.CurrentType.MemberCache.AddMember (d.CurrentType); + + del_type = new TypeExpression (d.CurrentType, loc); + if (targs_for_instance != null) { + del_type_instance_access = null; + } else { + del_type_instance_access = del_type; + } + } else { + d = null; + } + + var site_type_decl = new GenericTypeExpr (module.PredefinedTypes.CallSiteGeneric.TypeSpec, new TypeArguments (del_type), loc); + var field = site_container.CreateCallSiteField (site_type_decl, loc); + if (field == null) + return; + + if (del_type_instance_access == null) { + var dt = d.CurrentType.DeclaringType.MakeGenericType (module, context_mvars.Types); + del_type_instance_access = new TypeExpression (MemberCache.GetMember (dt, d.CurrentType), loc); + } + + var instanceAccessExprType = new GenericTypeExpr (module.PredefinedTypes.CallSiteGeneric.TypeSpec, + new TypeArguments (del_type_instance_access), loc); + + if (instanceAccessExprType.ResolveAsType (ec.MemberContext) == null) + return; + + bool inflate_using_mvar = context_mvars != null && ec.IsAnonymousStoreyMutateRequired; + + TypeSpec gt; + if (inflate_using_mvar || context_mvars == null) { + gt = site_container.CurrentType; + } else { + gt = site_container.CurrentType.MakeGenericType (module, context_mvars.Types); + } + + // When site container already exists the inflated version has to be + // updated manually to contain newly created field + if (gt is InflatedTypeSpec && site_container.AnonymousMethodsCounter > 1) { + var tparams = gt.MemberDefinition.TypeParametersCount > 0 ? gt.MemberDefinition.TypeParameters : TypeParameterSpec.EmptyTypes; + var inflator = new TypeParameterInflator (module, gt, tparams, gt.TypeArguments); + gt.MemberCache.AddMember (field.InflateMember (inflator)); + } + + FieldExpr site_field_expr = new FieldExpr (MemberCache.GetMember (gt, field), loc); + + BlockContext bc = new BlockContext (ec.MemberContext, null, ec.BuiltinTypes.Void); + + Arguments args = new Arguments (1); + args.Add (new Argument (binder)); + StatementExpression s = new StatementExpression (new SimpleAssign (site_field_expr, new Invocation (new MemberAccess (instanceAccessExprType, "Create"), args))); + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + if (s.Resolve (bc)) { + Statement init = new If (new Binary (Binary.Operator.Equality, site_field_expr, new NullLiteral (loc)), s, loc); + init.Emit (ec); + } + + args = new Arguments (1 + dyn_args_count); + args.Add (new Argument (site_field_expr)); + if (arguments != null) { + int arg_pos = 1; + foreach (Argument a in arguments) { + if (a is NamedArgument) { + // Name is not valid in this context + args.Add (new Argument (a.Expr, a.ArgType)); + } else { + args.Add (a); + } + + if (inflate_using_mvar && a.Type != targs[arg_pos].Type) + a.Expr.Type = targs[arg_pos].Type; + + ++arg_pos; + } + } + + Expression target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (bc), args, false, loc).Resolve (bc); + if (target != null) + target.Emit (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + arguments.FlowAnalysis (fc); + } + + public static MemberAccess GetBinderNamespace (Location loc) + { + return new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "Microsoft", loc), "CSharp", loc), "RuntimeBinder", loc); + } + + protected MemberAccess GetBinder (string name, Location loc) + { + return new MemberAccess (new TypeExpression (binder_type, loc), name, loc); + } + } + + // + // Dynamic member access compound assignment for events + // + class DynamicEventCompoundAssign : ExpressionStatement + { + class IsEvent : DynamicExpressionStatement, IDynamicBinder + { + string name; + + public IsEvent (string name, Arguments args, Location loc) + : base (null, args, loc) + { + this.name = name; + binder = this; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + type = ec.BuiltinTypes.Bool; + + Arguments binder_args = new Arguments (3); + + binder_args.Add (new Argument (new BinderFlags (0, this))); + binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, name, loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + + return new Invocation (GetBinder ("IsEvent", loc), binder_args); + } + } + + Expression condition; + ExpressionStatement invoke, assign; + + public DynamicEventCompoundAssign (string name, Arguments args, ExpressionStatement assignment, ExpressionStatement invoke, Location loc) + { + condition = new IsEvent (name, args, loc); + this.invoke = invoke; + this.assign = assignment; + this.loc = loc; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return condition.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext rc) + { + type = rc.BuiltinTypes.Dynamic; + eclass = ExprClass.Value; + condition = condition.Resolve (rc); + return this; + } + + public override void Emit (EmitContext ec) + { + var rc = new ResolveContext (ec.MemberContext); + var expr = new Conditional (new BooleanExpression (condition), invoke, assign, loc).Resolve (rc); + expr.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + var stmt = new If (condition, new StatementExpression (invoke), new StatementExpression (assign), loc); + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + stmt.Emit (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + invoke.FlowAnalysis (fc); + } + } + + class DynamicConversion : DynamicExpressionStatement, IDynamicBinder + { + public DynamicConversion (TypeSpec targetType, CSharpBinderFlags flags, Arguments args, Location loc) + : base (null, args, loc) + { + type = targetType; + base.flags = flags; + base.binder = this; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (3); + + flags |= ec.HasSet (ResolveContext.Options.CheckedScope) ? CSharpBinderFlags.CheckedContext : 0; + + binder_args.Add (new Argument (new BinderFlags (flags, this))); + binder_args.Add (new Argument (new TypeOf (type, loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + return new Invocation (GetBinder ("Convert", loc), binder_args); + } + } + + class DynamicConstructorBinder : DynamicExpressionStatement, IDynamicBinder + { + public DynamicConstructorBinder (TypeSpec type, Arguments args, Location loc) + : base (null, args, loc) + { + this.type = type; + base.binder = this; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (3); + + binder_args.Add (new Argument (new BinderFlags (0, this))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + return new Invocation (GetBinder ("InvokeConstructor", loc), binder_args); + } + } + + class DynamicIndexBinder : DynamicMemberAssignable + { + bool can_be_mutator; + + public DynamicIndexBinder (Arguments args, Location loc) + : base (args, loc) + { + } + + public DynamicIndexBinder (CSharpBinderFlags flags, Arguments args, Location loc) + : this (args, loc) + { + base.flags = flags; + } + + protected override Expression DoResolve (ResolveContext ec) + { + can_be_mutator = true; + return base.DoResolve (ec); + } + + protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet) + { + Arguments binder_args = new Arguments (3); + + binder_args.Add (new Argument (new BinderFlags (flags, this))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0; + return new Invocation (GetBinder (isSet ? "SetIndex" : "GetIndex", loc), binder_args); + } + + protected override Arguments CreateSetterArguments (ResolveContext rc, Expression rhs) + { + // + // Indexer has arguments which complicates things as the setter and getter + // are called in two steps when unary mutator is used. We have to make a + // copy of all variable arguments to not duplicate any side effect. + // + // ++d[++arg, Foo ()] + // + + if (!can_be_mutator) + return base.CreateSetterArguments (rc, rhs); + + var setter_args = new Arguments (Arguments.Count + 1); + for (int i = 0; i < Arguments.Count; ++i) { + var expr = Arguments[i].Expr; + + if (expr is Constant || expr is VariableReference || expr is This) { + setter_args.Add (Arguments [i]); + continue; + } + + LocalVariable temp = LocalVariable.CreateCompilerGenerated (expr.Type, rc.CurrentBlock, loc); + expr = new SimpleAssign (temp.CreateReferenceExpression (rc, expr.Location), expr).Resolve (rc); + Arguments[i].Expr = temp.CreateReferenceExpression (rc, expr.Location).Resolve (rc); + setter_args.Add (Arguments [i].Clone (expr)); + } + + setter_args.Add (new Argument (rhs)); + return setter_args; + } + } + + class DynamicInvocation : DynamicExpressionStatement, IDynamicBinder + { + readonly ATypeNameExpression member; + + public DynamicInvocation (ATypeNameExpression member, Arguments args, Location loc) + : base (null, args, loc) + { + base.binder = this; + this.member = member; + } + + public static DynamicInvocation CreateSpecialNameInvoke (ATypeNameExpression member, Arguments args, Location loc) + { + return new DynamicInvocation (member, args, loc) { + flags = CSharpBinderFlags.InvokeSpecialName + }; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (member != null ? 5 : 3); + bool is_member_access = member is MemberAccess; + + CSharpBinderFlags call_flags; + if (!is_member_access && member is SimpleName) { + call_flags = CSharpBinderFlags.InvokeSimpleName; + is_member_access = true; + } else { + call_flags = 0; + } + + binder_args.Add (new Argument (new BinderFlags (call_flags, this))); + + if (is_member_access) + binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, member.Name, member.Location))); + + if (member != null && member.HasTypeArguments) { + TypeArguments ta = member.TypeArguments; + if (ta.Resolve (ec)) { + var targs = new ArrayInitializer (ta.Count, loc); + foreach (TypeSpec t in ta.Arguments) + targs.Add (new TypeOf (t, loc)); + + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (targs, loc))); + } + } else if (is_member_access) { + binder_args.Add (new Argument (new NullLiteral (loc))); + } + + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + + Expression real_args; + if (args == null) { + // Cannot be null because .NET trips over + real_args = new ArrayCreation ( + new MemberAccess (GetBinderNamespace (loc), "CSharpArgumentInfo", loc), + new ArrayInitializer (0, loc), loc); + } else { + real_args = new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc); + } + + binder_args.Add (new Argument (real_args)); + + return new Invocation (GetBinder (is_member_access ? "InvokeMember" : "Invoke", loc), binder_args); + } + + public override void EmitStatement (EmitContext ec) + { + flags |= CSharpBinderFlags.ResultDiscarded; + base.EmitStatement (ec); + } + } + + class DynamicMemberBinder : DynamicMemberAssignable + { + readonly string name; + + public DynamicMemberBinder (string name, Arguments args, Location loc) + : base (args, loc) + { + this.name = name; + } + + public DynamicMemberBinder (string name, CSharpBinderFlags flags, Arguments args, Location loc) + : this (name, args, loc) + { + base.flags = flags; + } + + protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet) + { + Arguments binder_args = new Arguments (4); + + binder_args.Add (new Argument (new BinderFlags (flags, this))); + binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, name, loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0; + return new Invocation (GetBinder (isSet ? "SetMember" : "GetMember", loc), binder_args); + } + } + + // + // Any member binder which can be source and target of assignment + // + abstract class DynamicMemberAssignable : DynamicExpressionStatement, IDynamicBinder, IAssignMethod + { + Expression setter; + Arguments setter_args; + + protected DynamicMemberAssignable (Arguments args, Location loc) + : base (null, args, loc) + { + base.binder = this; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + // + // DoResolve always uses getter + // + return CreateCallSiteBinder (ec, args, false); + } + + protected abstract Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet); + + protected virtual Arguments CreateSetterArguments (ResolveContext rc, Expression rhs) + { + var setter_args = new Arguments (Arguments.Count + 1); + setter_args.AddRange (Arguments); + setter_args.Add (new Argument (rhs)); + return setter_args; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + if (right_side == EmptyExpression.OutAccess) { + right_side.DoResolveLValue (rc, this); + return null; + } + + if (DoResolveCore (rc)) { + setter_args = CreateSetterArguments (rc, right_side); + setter = CreateCallSiteBinder (rc, setter_args, true); + } + + eclass = ExprClass.Variable; + return this; + } + + public override void Emit (EmitContext ec) + { + // It's null for ResolveLValue used without assignment + if (binder_expr == null) + EmitCall (ec, setter, Arguments, false); + else + base.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + // It's null for ResolveLValue used without assignment + if (binder_expr == null) + EmitCall (ec, setter, Arguments, true); + else + base.EmitStatement (ec); + } + + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + EmitCall (ec, setter, setter_args, !leave_copy); + } + + #endregion + } + + class DynamicUnaryConversion : DynamicExpressionStatement, IDynamicBinder + { + readonly string name; + + public DynamicUnaryConversion (string name, Arguments args, Location loc) + : base (null, args, loc) + { + this.name = name; + base.binder = this; + } + + public static DynamicUnaryConversion CreateIsTrue (ResolveContext rc, Arguments args, Location loc) + { + return new DynamicUnaryConversion ("IsTrue", args, loc) { type = rc.BuiltinTypes.Bool }; + } + + public static DynamicUnaryConversion CreateIsFalse (ResolveContext rc, Arguments args, Location loc) + { + return new DynamicUnaryConversion ("IsFalse", args, loc) { type = rc.BuiltinTypes.Bool }; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (4); + + MemberAccess sle = new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc); + + var flags = ec.HasSet (ResolveContext.Options.CheckedScope) ? CSharpBinderFlags.CheckedContext : 0; + + binder_args.Add (new Argument (new BinderFlags (flags, this))); + binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), name, loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + return new Invocation (GetBinder ("UnaryOperation", loc), binder_args); + } + } + + sealed class DynamicSiteClass : HoistedStoreyClass + { + public DynamicSiteClass (TypeDefinition parent, MemberBase host, TypeParameters tparams) + : base (parent, MakeMemberName (host, "DynamicSite", parent.DynamicSitesCounter, tparams, Location.Null), tparams, Modifiers.STATIC, MemberKind.Class) + { + parent.DynamicSitesCounter++; + } + + public FieldSpec CreateCallSiteField (FullNamedExpression type, Location loc) + { + int index = AnonymousMethodsCounter++; + Field f = new HoistedField (this, type, Modifiers.PUBLIC | Modifiers.STATIC, "Site" + index.ToString ("X"), null, loc); + f.Define (); + + AddField (f); + return f.Spec; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/ecore.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/ecore.cs new file mode 100644 index 000000000..d1e2c3b84 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/ecore.cs @@ -0,0 +1,7426 @@ +// +// ecore.cs: Core of the Expression representation for the intermediate tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011-2012 Xamarin Inc. +// +// + +using System; +using System.Collections.Generic; +using System.Text; +using SLE = System.Linq.Expressions; +using System.Linq; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// The ExprClass class contains the is used to pass the + /// classification of an expression (value, variable, namespace, + /// type, method group, property access, event access, indexer access, + /// nothing). + /// + public enum ExprClass : byte { + Unresolved = 0, + + Value, + Variable, + Namespace, + Type, + TypeParameter, + MethodGroup, + PropertyAccess, + EventAccess, + IndexerAccess, + Nothing, + } + + /// + /// This is used to tell Resolve in which types of expressions we're + /// interested. + /// + [Flags] + public enum ResolveFlags { + // Returns Value, Variable, PropertyAccess, EventAccess or IndexerAccess. + VariableOrValue = 1, + + // Returns a type expression. + Type = 1 << 1, + + // Returns a method group. + MethodGroup = 1 << 2, + + TypeParameter = 1 << 3, + + // Mask of all the expression class flags. + MaskExprClass = VariableOrValue | Type | MethodGroup | TypeParameter, + } + + // + // This is just as a hint to AddressOf of what will be done with the + // address. + [Flags] + public enum AddressOp { + Store = 1, + Load = 2, + LoadStore = 3 + }; + + /// + /// This interface is implemented by variables + /// + public interface IMemoryLocation { + /// + /// The AddressOf method should generate code that loads + /// the address of the object and leaves it on the stack. + /// + /// The `mode' argument is used to notify the expression + /// of whether this will be used to read from the address or + /// write to the address. + /// + /// This is just a hint that can be used to provide good error + /// reporting, and should have no other side effects. + /// + void AddressOf (EmitContext ec, AddressOp mode); + } + + // + // An expressions resolved as a direct variable reference + // + public interface IVariableReference : IFixedExpression + { + bool IsHoisted { get; } + string Name { get; } + VariableInfo VariableInfo { get; } + + void SetHasAddressTaken (); + } + + // + // Implemented by an expression which could be or is always + // fixed + // + public interface IFixedExpression + { + bool IsFixed { get; } + } + + public interface IExpressionCleanup + { + void EmitCleanup (EmitContext ec); + } + + /// + /// Base class for expressions + /// + public abstract class Expression { + public ExprClass eclass; + protected TypeSpec type; + protected Location loc; + + public TypeSpec Type { + get { return type; } + set { type = value; } + } + + public virtual bool IsSideEffectFree { + get { + return false; + } + } + + public Location Location { + get { return loc; } + } + + public virtual bool IsNull { + get { + return false; + } + } + + // + // Used to workaround parser limitation where we cannot get + // start of statement expression location + // + public virtual Location StartLocation { + get { + return loc; + } + } + + public virtual MethodGroupExpr CanReduceLambda (AnonymousMethodBody body) + { + // + // Return method-group expression when the expression can be used as + // lambda replacement. A good example is array sorting where instead of + // code like + // + // Array.Sort (s, (a, b) => String.Compare (a, b)); + // + // we can use method group directly + // + // Array.Sort (s, String.Compare); + // + // Correct overload will be used because we do the reduction after + // best candidate was found. + // + return null; + } + + // + // Returns true when the expression during Emit phase breaks stack + // by using await expression + // + public virtual bool ContainsEmitWithAwait () + { + return false; + } + + /// + /// Performs semantic analysis on the Expression + /// + /// + /// + /// The Resolve method is invoked to perform the semantic analysis + /// on the node. + /// + /// The return value is an expression (it can be the + /// same expression in some cases) or a new + /// expression that better represents this node. + /// + /// For example, optimizations of Unary (LiteralInt) + /// would return a new LiteralInt with a negated + /// value. + /// + /// If there is an error during semantic analysis, + /// then an error should be reported (using Report) + /// and a null value should be returned. + /// + /// There are two side effects expected from calling + /// Resolve(): the the field variable "eclass" should + /// be set to any value of the enumeration + /// `ExprClass' and the type variable should be set + /// to a valid type (this is the type of the + /// expression). + /// + protected abstract Expression DoResolve (ResolveContext rc); + + public virtual Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return null; + } + + // + // This is used if the expression should be resolved as a type or namespace name. + // the default implementation fails. + // + public virtual TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + ResolveContext ec = new ResolveContext (mc); + Expression e = Resolve (ec); + if (e != null) + e.Error_UnexpectedKind (ec, ResolveFlags.Type, loc); + + return null; + } + + public static void ErrorIsInaccesible (IMemberContext rc, string member, Location loc) + { + rc.Module.Compiler.Report.Error (122, loc, "`{0}' is inaccessible due to its protection level", member); + } + + public void Error_ExpressionMustBeConstant (ResolveContext rc, Location loc, string e_name) + { + rc.Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name); + } + + public void Error_ConstantCanBeInitializedWithNullOnly (ResolveContext rc, TypeSpec type, Location loc, string name) + { + rc.Report.Error (134, loc, "A constant `{0}' of reference type `{1}' can only be initialized with null", + name, type.GetSignatureForError ()); + } + + protected virtual void Error_InvalidExpressionStatement (Report report, Location loc) + { + report.Error (201, loc, "Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement"); + } + + public void Error_InvalidExpressionStatement (BlockContext bc) + { + Error_InvalidExpressionStatement (bc.Report, loc); + } + + public void Error_InvalidExpressionStatement (Report report) + { + Error_InvalidExpressionStatement (report, loc); + } + + public static void Error_VoidInvalidInTheContext (Location loc, Report Report) + { + Report.Error (1547, loc, "Keyword `void' cannot be used in this context"); + } + + public virtual void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + Error_ValueCannotBeConvertedCore (ec, loc, target, expl); + } + + protected void Error_ValueCannotBeConvertedCore (ResolveContext ec, Location loc, TypeSpec target, bool expl) + { + // The error was already reported as CS1660 + if (type == InternalType.AnonymousMethod) + return; + + if (type == InternalType.ErrorType || target == InternalType.ErrorType) + return; + + string from_type = type.GetSignatureForError (); + string to_type = target.GetSignatureForError (); + if (from_type == to_type) { + from_type = type.GetSignatureForErrorIncludingAssemblyName (); + to_type = target.GetSignatureForErrorIncludingAssemblyName (); + } + + if (expl) { + ec.Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'", + from_type, to_type); + return; + } + + ec.Report.DisableReporting (); + bool expl_exists = Convert.ExplicitConversion (ec, this, target, Location.Null) != null; + ec.Report.EnableReporting (); + + if (expl_exists) { + ec.Report.Error (266, loc, + "Cannot implicitly convert type `{0}' to `{1}'. An explicit conversion exists (are you missing a cast?)", + from_type, to_type); + } else { + ec.Report.Error (29, loc, "Cannot implicitly convert type `{0}' to `{1}'", + from_type, to_type); + } + } + + public void Error_TypeArgumentsCannotBeUsed (IMemberContext context, MemberSpec member, Location loc) + { + // Better message for possible generic expressions + if (member != null && (member.Kind & MemberKind.GenericMask) != 0) { + var report = context.Module.Compiler.Report; + report.SymbolRelatedToPreviousError (member); + if (member is TypeSpec) + member = ((TypeSpec) member).GetDefinition (); + else + member = ((MethodSpec) member).GetGenericMethodDefinition (); + + string name = member.Kind == MemberKind.Method ? "method" : "type"; + if (member.IsGeneric) { + report.Error (305, loc, "Using the generic {0} `{1}' requires `{2}' type argument(s)", + name, member.GetSignatureForError (), member.Arity.ToString ()); + } else { + report.Error (308, loc, "The non-generic {0} `{1}' cannot be used with the type arguments", + name, member.GetSignatureForError ()); + } + } else { + Error_TypeArgumentsCannotBeUsed (context, ExprClassName, GetSignatureForError (), loc); + } + } + + public static void Error_TypeArgumentsCannotBeUsed (IMemberContext context, string exprType, string name, Location loc) + { + context.Module.Compiler.Report.Error (307, loc, "The {0} `{1}' cannot be used with type arguments", + exprType, name); + } + + protected virtual void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name) + { + Error_TypeDoesNotContainDefinition (ec, loc, type, name); + } + + public static void Error_TypeDoesNotContainDefinition (ResolveContext ec, Location loc, TypeSpec type, string name) + { + ec.Report.SymbolRelatedToPreviousError (type); + ec.Report.Error (117, loc, "`{0}' does not contain a definition for `{1}'", + type.GetSignatureForError (), name); + } + + public virtual void Error_ValueAssignment (ResolveContext rc, Expression rhs) + { + if (rhs == EmptyExpression.LValueMemberAccess || rhs == EmptyExpression.LValueMemberOutAccess) { + // Already reported as CS1612 + } else if (rhs == EmptyExpression.OutAccess) { + rc.Report.Error (1510, loc, "A ref or out argument must be an assignable variable"); + } else { + rc.Report.Error (131, loc, "The left-hand side of an assignment must be a variable, a property or an indexer"); + } + } + + protected void Error_VoidPointerOperation (ResolveContext rc) + { + rc.Report.Error (242, loc, "The operation in question is undefined on void pointers"); + } + + public static void Warning_UnreachableExpression (ResolveContext rc, Location loc) + { + rc.Report.Warning (429, 4, loc, "Unreachable expression code detected"); + } + + public ResolveFlags ExprClassToResolveFlags { + get { + switch (eclass) { + case ExprClass.Type: + case ExprClass.Namespace: + return ResolveFlags.Type; + + case ExprClass.MethodGroup: + return ResolveFlags.MethodGroup; + + case ExprClass.TypeParameter: + return ResolveFlags.TypeParameter; + + case ExprClass.Value: + case ExprClass.Variable: + case ExprClass.PropertyAccess: + case ExprClass.EventAccess: + case ExprClass.IndexerAccess: + return ResolveFlags.VariableOrValue; + + default: + throw new InternalErrorException (loc.ToString () + " " + GetType () + " ExprClass is Invalid after resolve"); + } + } + } + + // + // Implements identical simple name and type-name resolution + // + public Expression ProbeIdenticalTypeName (ResolveContext rc, Expression left, SimpleName name) + { + var t = left.Type; + if (t.Kind == MemberKind.InternalCompilerType || t is ElementTypeSpec || t.Arity > 0) + return left; + + // In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name is + // a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name + + if (left is MemberExpr || left is VariableReference) { + var identical_type = rc.LookupNamespaceOrType (name.Name, 0, LookupMode.Probing, loc) as TypeExpr; + if (identical_type != null && identical_type.Type == left.Type) + return identical_type; + } + + return left; + } + + public virtual string GetSignatureForError () + { + return type.GetDefinition ().GetSignatureForError (); + } + + public static bool IsNeverNull (Expression expr) + { + if (expr is This || expr is New || expr is ArrayCreation || expr is DelegateCreation || expr is ConditionalMemberAccess) + return true; + + var c = expr as Constant; + if (c != null) + return !c.IsNull; + + var tc = expr as TypeCast; + if (tc != null) + return IsNeverNull (tc.Child); + + return false; + } + + protected static bool IsNullPropagatingValid (TypeSpec type) + { + switch (type.Kind) { + case MemberKind.Struct: + return type.IsNullableType; + case MemberKind.Enum: + case MemberKind.Void: + case MemberKind.PointerType: + return false; + case MemberKind.InternalCompilerType: + return type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + default: + return true; + } + } + + public virtual bool HasConditionalAccess () + { + return false; + } + + protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type) + { + return TypeSpec.IsValueType (type) && !type.IsNullableType ? + Nullable.NullableInfo.MakeType (rc.Module, type) : + type; + } + + /// + /// Resolves an expression and performs semantic analysis on it. + /// + /// + /// + /// Currently Resolve wraps DoResolve to perform sanity + /// checking and assertion checking on what we expect from Resolve. + /// + public Expression Resolve (ResolveContext ec, ResolveFlags flags) + { + if (eclass != ExprClass.Unresolved) { + if ((flags & ExprClassToResolveFlags) == 0) { + Error_UnexpectedKind (ec, flags, loc); + return null; + } + + return this; + } + + Expression e; + try { + e = DoResolve (ec); + + if (e == null) + return null; + + if ((flags & e.ExprClassToResolveFlags) == 0) { + e.Error_UnexpectedKind (ec, flags, loc); + return null; + } + + if (e.type == null) + throw new InternalErrorException ("Expression `{0}' didn't set its type in DoResolve", e.GetType ()); + + return e; + } catch (Exception ex) { + if (loc.IsNull || ec.Module.Compiler.Settings.BreakOnInternalError || ex is CompletionResult || ec.Report.IsDisabled || ex is FatalException || + ec.Report.Printer is NullReportPrinter) + throw; + + ec.Report.Error (584, loc, "Internal compiler error: {0}", ex.Message); + return ErrorExpression.Instance; // TODO: Add location + } + } + + /// + /// Resolves an expression and performs semantic analysis on it. + /// + public Expression Resolve (ResolveContext rc) + { + return Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup); + } + + /// + /// Resolves an expression for LValue assignment + /// + /// + /// + /// Currently ResolveLValue wraps DoResolveLValue to perform sanity + /// checking and assertion checking on what we expect from Resolve + /// + public Expression ResolveLValue (ResolveContext ec, Expression right_side) + { + int errors = ec.Report.Errors; + bool out_access = right_side == EmptyExpression.OutAccess; + + Expression e = DoResolveLValue (ec, right_side); + + if (e != null && out_access && !(e is IMemoryLocation)) { + // FIXME: There's no problem with correctness, the 'Expr = null' handles that. + // Enabling this 'throw' will "only" result in deleting useless code elsewhere, + + //throw new InternalErrorException ("ResolveLValue didn't return an IMemoryLocation: " + + // e.GetType () + " " + e.GetSignatureForError ()); + e = null; + } + + if (e == null) { + if (errors == ec.Report.Errors) { + Error_ValueAssignment (ec, right_side); + } + return null; + } + + if (e.eclass == ExprClass.Unresolved) + throw new Exception ("Expression " + e + " ExprClass is Invalid after resolve"); + + if ((e.type == null) && !(e is GenericTypeExpr)) + throw new Exception ("Expression " + e + " did not set its type after Resolve"); + + return e; + } + + public Constant ResolveLabelConstant (ResolveContext rc) + { + var expr = Resolve (rc); + if (expr == null) + return null; + + Constant c = expr as Constant; + if (c == null) { + if (expr.type != InternalType.ErrorType) + rc.Report.Error (150, expr.StartLocation, "A constant value is expected"); + + return null; + } + + return c; + } + + public virtual void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + if (Attribute.IsValidArgumentType (parameterType)) { + rc.Module.Compiler.Report.Error (182, loc, + "An attribute argument must be a constant expression, typeof expression or array creation expression"); + } else { + rc.Module.Compiler.Report.Error (181, loc, + "Attribute constructor parameter has type `{0}', which is not a valid attribute parameter type", + targetType.GetSignatureForError ()); + } + } + + /// + /// Emits the code for the expression + /// + /// + /// + /// The Emit method is invoked to generate the code + /// for the expression. + /// + public abstract void Emit (EmitContext ec); + + + // Emit code to branch to @target if this expression is equivalent to @on_true. + // The default implementation is to emit the value, and then emit a brtrue or brfalse. + // Subclasses can provide more efficient implementations, but those MUST be equivalent, + // including the use of conditional branches. Note also that a branch MUST be emitted + public virtual void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + Emit (ec); + ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target); + } + + // Emit this expression for its side effects, not for its value. + // The default implementation is to emit the value, and then throw it away. + // Subclasses can provide more efficient implementations, but those MUST be equivalent + public virtual void EmitSideEffect (EmitContext ec) + { + Emit (ec); + ec.Emit (OpCodes.Pop); + } + + // + // Emits the expression into temporary field variable. The method + // should be used for await expressions only + // + public virtual Expression EmitToField (EmitContext ec) + { + // + // This is the await prepare Emit method. When emitting code like + // a + b we emit code like + // + // a.Emit () + // b.Emit () + // Opcodes.Add + // + // For await a + await b we have to interfere the flow to keep the + // stack clean because await yields from the expression. The emit + // then changes to + // + // a = a.EmitToField () // a is changed to temporary field access + // b = b.EmitToField () + // a.Emit () + // b.Emit () + // Opcodes.Add + // + // + // The idea is to emit expression and leave the stack empty with + // result value still available. + // + // Expressions should override this default implementation when + // optimized version can be provided (e.g. FieldExpr) + // + // + // We can optimize for side-effect free expressions, they can be + // emitted out of order + // + if (IsSideEffectFree) + return this; + + bool needs_temporary = ContainsEmitWithAwait (); + if (!needs_temporary) + ec.EmitThis (); + + // Emit original code + var field = EmitToFieldSource (ec); + if (field == null) { + // + // Store the result to temporary field when we + // cannot load `this' directly + // + field = ec.GetTemporaryField (type); + if (needs_temporary) { + // + // Create temporary local (we cannot load `this' before Emit) + // + var temp = ec.GetTemporaryLocal (type); + ec.Emit (OpCodes.Stloc, temp); + + ec.EmitThis (); + ec.Emit (OpCodes.Ldloc, temp); + field.EmitAssignFromStack (ec); + + ec.FreeTemporaryLocal (temp, type); + } else { + field.EmitAssignFromStack (ec); + } + } + + return field; + } + + protected virtual FieldExpr EmitToFieldSource (EmitContext ec) + { + // + // Default implementation calls Emit method + // + Emit (ec); + return null; + } + + protected static void EmitExpressionsList (EmitContext ec, List expressions) + { + if (ec.HasSet (BuilderContext.Options.AsyncBody)) { + bool contains_await = false; + + for (int i = 1; i < expressions.Count; ++i) { + if (expressions[i].ContainsEmitWithAwait ()) { + contains_await = true; + break; + } + } + + if (contains_await) { + for (int i = 0; i < expressions.Count; ++i) { + expressions[i] = expressions[i].EmitToField (ec); + } + } + } + + for (int i = 0; i < expressions.Count; ++i) { + expressions[i].Emit (ec); + } + } + + /// + /// Protected constructor. Only derivate types should + /// be able to be created + /// + + protected Expression () + { + } + + /// + /// Returns a fully formed expression after a MemberLookup + /// + /// + static Expression ExprClassFromMemberInfo (MemberSpec spec, Location loc) + { + if (spec is EventSpec) + return new EventExpr ((EventSpec) spec, loc); + if (spec is ConstSpec) + return new ConstantExpr ((ConstSpec) spec, loc); + if (spec is FieldSpec) + return new FieldExpr ((FieldSpec) spec, loc); + if (spec is PropertySpec) + return new PropertyExpr ((PropertySpec) spec, loc); + if (spec is TypeSpec) + return new TypeExpression (((TypeSpec) spec), loc); + + return null; + } + + public static MethodSpec ConstructorLookup (ResolveContext rc, TypeSpec type, ref Arguments args, Location loc) + { + var ctors = MemberCache.FindMembers (type, Constructor.ConstructorName, true); + if (ctors == null) { + switch (type.Kind) { + case MemberKind.Struct: + rc.Report.SymbolRelatedToPreviousError (type); + // Report meaningful error for struct as they always have default ctor in C# context + OverloadResolver.Error_ConstructorMismatch (rc, type, args == null ? 0 : args.Count, loc); + break; + case MemberKind.MissingType: + case MemberKind.InternalCompilerType: +// LAMESPEC: dynamic is not really object +// if (type.BuiltinType == BuiltinTypeSpec.Type.Object) +// goto default; + break; + default: + rc.Report.SymbolRelatedToPreviousError (type); + rc.Report.Error (143, loc, "The class `{0}' has no constructors defined", + type.GetSignatureForError ()); + break; + } + + return null; + } + + var r = new OverloadResolver (ctors, OverloadResolver.Restrictions.NoBaseMembers, loc); + if (!rc.HasSet (ResolveContext.Options.BaseInitializer)) { + r.InstanceQualifier = new ConstructorInstanceQualifier (type); + } + + return r.ResolveMember (rc, ref args); + } + + [Flags] + public enum MemberLookupRestrictions + { + None = 0, + InvocableOnly = 1, + ExactArity = 1 << 2, + ReadAccess = 1 << 3, + EmptyArguments = 1 << 4, + IgnoreArity = 1 << 5, + IgnoreAmbiguity = 1 << 6 + } + + // + // Lookup type `queried_type' for code in class `container_type' with a qualifier of + // `qualifier_type' or null to lookup members in the current class. + // + public static Expression MemberLookup (IMemberContext rc, bool errorMode, TypeSpec queried_type, string name, int arity, MemberLookupRestrictions restrictions, Location loc) + { + var members = MemberCache.FindMembers (queried_type, name, false); + if (members == null) + return null; + + MemberSpec non_method = null; + MemberSpec ambig_non_method = null; + do { + for (int i = 0; i < members.Count; ++i) { + var member = members[i]; + + // HACK: for events because +=/-= can appear at same class only, should use OverrideToBase there + if ((member.Modifiers & Modifiers.OVERRIDE) != 0 && member.Kind != MemberKind.Event) + continue; + + if ((member.Modifiers & Modifiers.BACKING_FIELD) != 0 || member.Kind == MemberKind.Operator) + continue; + + if ((arity > 0 || (restrictions & MemberLookupRestrictions.ExactArity) != 0) && member.Arity != arity) + continue; + + if (!errorMode) { + if (!member.IsAccessible (rc)) + continue; + + // + // With runtime binder we can have a situation where queried type is inaccessible + // because it came via dynamic object, the check about inconsisted accessibility + // had no effect as the type was unknown during compilation + // + // class A { + // private class N { } + // + // public dynamic Foo () + // { + // return new N (); + // } + // } + // + if (rc.Module.Compiler.IsRuntimeBinder && !member.DeclaringType.IsAccessible (rc)) + continue; + } + + if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) { + if (member is MethodSpec) { + // + // Interface members that are hidden by class members are removed from the set. This + // step only has an effect if T is a type parameter and T has both an effective base + // class other than object and a non-empty effective interface set + // + var tps = queried_type as TypeParameterSpec; + if (tps != null && tps.HasTypeConstraint) + members = RemoveHiddenTypeParameterMethods (members); + + return new MethodGroupExpr (members, queried_type, loc); + } + + if (!Invocation.IsMemberInvocable (member)) + continue; + } + + if (non_method == null || member is MethodSpec || non_method.IsNotCSharpCompatible) { + non_method = member; + } else if (!errorMode && !member.IsNotCSharpCompatible) { + // + // Interface members that are hidden by class members are removed from the set when T is a type parameter and + // T has both an effective base class other than object and a non-empty effective interface set. + // + // The spec has more complex rules but we simply remove all members declared in an interface declaration. + // + var tps = queried_type as TypeParameterSpec; + if (tps != null && tps.HasTypeConstraint) { + if (non_method.DeclaringType.IsClass && member.DeclaringType.IsInterface) + continue; + + if (non_method.DeclaringType.IsInterface && member.DeclaringType.IsInterface) { + non_method = member; + continue; + } + } + + ambig_non_method = member; + } + } + + if (non_method != null) { + if (ambig_non_method != null && rc != null && (restrictions & MemberLookupRestrictions.IgnoreAmbiguity) == 0) { + var report = rc.Module.Compiler.Report; + report.SymbolRelatedToPreviousError (non_method); + report.SymbolRelatedToPreviousError (ambig_non_method); + report.Error (229, loc, "Ambiguity between `{0}' and `{1}'", + non_method.GetSignatureForError (), ambig_non_method.GetSignatureForError ()); + } + + if (non_method is MethodSpec) + return new MethodGroupExpr (members, queried_type, loc); + + return ExprClassFromMemberInfo (non_method, loc); + } + + if (members[0].DeclaringType.BaseType == null) + members = null; + else + members = MemberCache.FindMembers (members[0].DeclaringType.BaseType, name, false); + + } while (members != null); + + return null; + } + + static IList RemoveHiddenTypeParameterMethods (IList members) + { + if (members.Count < 2) + return members; + + // + // If M is a method, then all non-method members declared in an interface declaration + // are removed from the set, and all methods with the same signature as M declared in + // an interface declaration are removed from the set + // + + bool copied = false; + for (int i = 0; i < members.Count; ++i) { + var method = members[i] as MethodSpec; + if (method == null) { + if (!copied) { + copied = true; + members = new List (members); + } + + members.RemoveAt (i--); + continue; + } + + if (!method.DeclaringType.IsInterface) + continue; + + for (int ii = 0; ii < members.Count; ++ii) { + var candidate = members[ii] as MethodSpec; + if (candidate == null || !candidate.DeclaringType.IsClass) + continue; + + if (!TypeSpecComparer.Override.IsEqual (candidate.Parameters, method.Parameters)) + continue; + + if (!copied) { + copied = true; + members = new List (members); + } + + members.RemoveAt (i--); + break; + } + } + + return members; + } + + protected virtual void Error_NegativeArrayIndex (ResolveContext ec, Location loc) + { + throw new NotImplementedException (); + } + + public virtual void Error_OperatorCannotBeApplied (ResolveContext rc, Location loc, string oper, TypeSpec t) + { + if (t == InternalType.ErrorType) + return; + + rc.Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'", + oper, t.GetSignatureForError ()); + } + + protected void Error_PointerInsideExpressionTree (ResolveContext ec) + { + ec.Report.Error (1944, loc, "An expression tree cannot contain an unsafe pointer operation"); + } + + protected void Error_NullShortCircuitInsideExpressionTree (ResolveContext rc) + { + rc.Report.Error (8072, loc, "An expression tree cannot contain a null propagating operator"); + } + + public virtual void FlowAnalysis (FlowAnalysisContext fc) + { + } + + // + // Special version of flow analysis for expressions which can return different + // on-true and on-false result. Used by &&, ||, ?: expressions + // + public virtual void FlowAnalysisConditional (FlowAnalysisContext fc) + { + FlowAnalysis (fc); + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment; + } + + /// + /// Returns an expression that can be used to invoke operator true + /// on the expression if it exists. + /// + protected static Expression GetOperatorTrue (ResolveContext ec, Expression e, Location loc) + { + return GetOperatorTrueOrFalse (ec, e, true, loc); + } + + /// + /// Returns an expression that can be used to invoke operator false + /// on the expression if it exists. + /// + protected static Expression GetOperatorFalse (ResolveContext ec, Expression e, Location loc) + { + return GetOperatorTrueOrFalse (ec, e, false, loc); + } + + static Expression GetOperatorTrueOrFalse (ResolveContext ec, Expression e, bool is_true, Location loc) + { + var op = is_true ? Operator.OpType.True : Operator.OpType.False; + var methods = MemberCache.GetUserOperator (e.type, op, false); + if (methods == null) + return null; + + Arguments arguments = new Arguments (1); + arguments.Add (new Argument (e)); + + var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc); + var oper = res.ResolveOperator (ec, ref arguments); + + if (oper == null) + return null; + + return new UserOperatorCall (oper, arguments, null, loc); + } + + public virtual string ExprClassName + { + get { + switch (eclass){ + case ExprClass.Unresolved: + return "Unresolved"; + case ExprClass.Value: + return "value"; + case ExprClass.Variable: + return "variable"; + case ExprClass.Namespace: + return "namespace"; + case ExprClass.Type: + return "type"; + case ExprClass.MethodGroup: + return "method group"; + case ExprClass.PropertyAccess: + return "property access"; + case ExprClass.EventAccess: + return "event access"; + case ExprClass.IndexerAccess: + return "indexer access"; + case ExprClass.Nothing: + return "null"; + case ExprClass.TypeParameter: + return "type parameter"; + } + throw new Exception ("Should not happen"); + } + } + + /// + /// Reports that we were expecting `expr' to be of class `expected' + /// + public static void Error_UnexpectedKind (IMemberContext ctx, Expression memberExpr, string expected, string was, Location loc) + { + var name = memberExpr.GetSignatureForError (); + + ctx.Module.Compiler.Report.Error (118, loc, "`{0}' is a `{1}' but a `{2}' was expected", name, was, expected); + } + + public virtual void Error_UnexpectedKind (ResolveContext ec, ResolveFlags flags, Location loc) + { + string [] valid = new string [4]; + int count = 0; + + if ((flags & ResolveFlags.VariableOrValue) != 0) { + valid [count++] = "variable"; + valid [count++] = "value"; + } + + if ((flags & ResolveFlags.Type) != 0) + valid [count++] = "type"; + + if ((flags & ResolveFlags.MethodGroup) != 0) + valid [count++] = "method group"; + + if (count == 0) + valid [count++] = "unknown"; + + StringBuilder sb = new StringBuilder (valid [0]); + for (int i = 1; i < count - 1; i++) { + sb.Append ("', `"); + sb.Append (valid [i]); + } + if (count > 1) { + sb.Append ("' or `"); + sb.Append (valid [count - 1]); + } + + ec.Report.Error (119, loc, + "Expression denotes a `{0}', where a `{1}' was expected", ExprClassName, sb.ToString ()); + } + + public static void UnsafeError (ResolveContext ec, Location loc) + { + UnsafeError (ec.Report, loc); + } + + public static void UnsafeError (Report Report, Location loc) + { + Report.Error (214, loc, "Pointers and fixed size buffers may only be used in an unsafe context"); + } + + // + // Converts `source' to an int, uint, long or ulong. + // + protected Expression ConvertExpressionToArrayIndex (ResolveContext ec, Expression source, bool pointerArray = false) + { + var btypes = ec.BuiltinTypes; + + if (source.type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments args = new Arguments (1); + args.Add (new Argument (source)); + return new DynamicConversion (btypes.Int, CSharpBinderFlags.ConvertArrayIndex, args, loc).Resolve (ec); + } + + Expression converted; + + using (ec.Set (ResolveContext.Options.CheckedScope)) { + converted = Convert.ImplicitConversion (ec, source, btypes.Int, source.loc); + if (converted == null) + converted = Convert.ImplicitConversion (ec, source, btypes.UInt, source.loc); + if (converted == null) + converted = Convert.ImplicitConversion (ec, source, btypes.Long, source.loc); + if (converted == null) + converted = Convert.ImplicitConversion (ec, source, btypes.ULong, source.loc); + + if (converted == null) { + source.Error_ValueCannotBeConverted (ec, btypes.Int, false); + return null; + } + } + + if (pointerArray) + return converted; + + // + // Only positive constants are allowed at compile time + // + Constant c = converted as Constant; + if (c != null && c.IsNegative) + Error_NegativeArrayIndex (ec, source.loc); + + // No conversion needed to array index + if (converted.Type.BuiltinType == BuiltinTypeSpec.Type.Int) + return converted; + + return new ArrayIndexCast (converted, btypes.Int).Resolve (ec); + } + + // + // Derived classes implement this method by cloning the fields that + // could become altered during the Resolve stage + // + // Only expressions that are created for the parser need to implement + // this. + // + protected virtual void CloneTo (CloneContext clonectx, Expression target) + { + throw new NotImplementedException ( + String.Format ( + "CloneTo not implemented for expression {0}", this.GetType ())); + } + + // + // Clones an expression created by the parser. + // + // We only support expressions created by the parser so far, not + // expressions that have been resolved (many more classes would need + // to implement CloneTo). + // + // This infrastructure is here merely for Lambda expressions which + // compile the same code using different type values for the same + // arguments to find the correct overload + // + public virtual Expression Clone (CloneContext clonectx) + { + Expression cloned = (Expression) MemberwiseClone (); + CloneTo (clonectx, cloned); + + return cloned; + } + + // + // Implementation of expression to expression tree conversion + // + public abstract Expression CreateExpressionTree (ResolveContext ec); + + protected Expression CreateExpressionFactoryCall (ResolveContext ec, string name, Arguments args) + { + return CreateExpressionFactoryCall (ec, name, null, args, loc); + } + + protected Expression CreateExpressionFactoryCall (ResolveContext ec, string name, TypeArguments typeArguments, Arguments args) + { + return CreateExpressionFactoryCall (ec, name, typeArguments, args, loc); + } + + public static Expression CreateExpressionFactoryCall (ResolveContext ec, string name, TypeArguments typeArguments, Arguments args, Location loc) + { + return new Invocation (new MemberAccess (CreateExpressionTypeExpression (ec, loc), name, typeArguments, loc), args); + } + + protected static TypeExpr CreateExpressionTypeExpression (ResolveContext ec, Location loc) + { + var t = ec.Module.PredefinedTypes.Expression.Resolve (); + if (t == null) + return null; + + return new TypeExpression (t, loc); + } + + // + // Implemented by all expressions which support conversion from + // compiler expression to invokable runtime expression. Used by + // dynamic C# binder. + // + public virtual SLE.Expression MakeExpression (BuilderContext ctx) + { + throw new NotImplementedException ("MakeExpression for " + GetType ()); + } + + public virtual object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// This is just a base class for expressions that can + /// appear on statements (invocations, object creation, + /// assignments, post/pre increment and decrement). The idea + /// being that they would support an extra Emition interface that + /// does not leave a result on the stack. + /// + public abstract class ExpressionStatement : Expression + { + public virtual void MarkReachable (Reachability rc) + { + } + + public ExpressionStatement ResolveStatement (BlockContext ec) + { + Expression e = Resolve (ec); + if (e == null) + return null; + + ExpressionStatement es = e as ExpressionStatement; + if (es == null || e is AnonymousMethodBody) + Error_InvalidExpressionStatement (ec); + + // + // This is quite expensive warning, try to limit the damage + // + if (MemberAccess.IsValidDotExpression (e.Type) && !(e is Assign || e is Await)) { + WarningAsyncWithoutWait (ec, e); + } + + return es; + } + + static void WarningAsyncWithoutWait (BlockContext bc, Expression e) + { + if (bc.CurrentAnonymousMethod is AsyncInitializer) { + var awaiter = new AwaitStatement.AwaitableMemberAccess (e) { + ProbingMode = true + }; + + // + // Need to do full resolve because GetAwaiter can be extension method + // available only in this context + // + var mg = awaiter.Resolve (bc) as MethodGroupExpr; + if (mg == null) + return; + + var arguments = new Arguments (0); + mg = mg.OverloadResolve (bc, ref arguments, null, OverloadResolver.Restrictions.ProbingOnly); + if (mg == null) + return; + + // + // Use same check rules as for real await + // + var awaiter_definition = bc.Module.GetAwaiter (mg.BestCandidateReturnType); + if (!awaiter_definition.IsValidPattern || !awaiter_definition.INotifyCompletion) + return; + + bc.Report.Warning (4014, 1, e.Location, + "The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator"); + return; + } + + var inv = e as Invocation; + if (inv != null && inv.MethodGroup != null && inv.MethodGroup.BestCandidate.IsAsync) { + // The warning won't be reported for imported methods to maintain warning compatiblity with csc + bc.Report.Warning (4014, 1, e.Location, + "The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator or calling `Wait' method"); + return; + } + } + + /// + /// Requests the expression to be emitted in a `statement' + /// context. This means that no new value is left on the + /// stack after invoking this method (constrasted with + /// Emit that will always leave a value on the stack). + /// + public abstract void EmitStatement (EmitContext ec); + + public override void EmitSideEffect (EmitContext ec) + { + EmitStatement (ec); + } + } + + /// + /// This kind of cast is used to encapsulate the child + /// whose type is child.Type into an expression that is + /// reported to return "return_type". This is used to encapsulate + /// expressions which have compatible types, but need to be dealt + /// at higher levels with. + /// + /// For example, a "byte" expression could be encapsulated in one + /// of these as an "unsigned int". The type for the expression + /// would be "unsigned int". + /// + /// + public abstract class TypeCast : Expression + { + protected readonly Expression child; + + protected TypeCast (Expression child, TypeSpec return_type) + { + eclass = child.eclass; + loc = child.Location; + type = return_type; + this.child = child; + } + + public Expression Child { + get { + return child; + } + } + + public override bool ContainsEmitWithAwait () + { + return child.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (child.CreateExpressionTree (ec))); + args.Add (new Argument (new TypeOf (type, loc))); + + if (type.IsPointer || child.Type.IsPointer) + Error_PointerInsideExpressionTree (ec); + + return CreateExpressionFactoryCall (ec, ec.HasSet (ResolveContext.Options.CheckedScope) ? "ConvertChecked" : "Convert", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + child.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return ctx.HasSet (BuilderContext.Options.CheckedScope) ? + SLE.Expression.ConvertChecked (child.MakeExpression (ctx), type.GetMetaInfo ()) : + SLE.Expression.Convert (child.MakeExpression (ctx), type.GetMetaInfo ()); +#endif + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing to clone + } + + public override bool IsNull { + get { return child.IsNull; } + } + } + + public class EmptyCast : TypeCast { + EmptyCast (Expression child, TypeSpec target_type) + : base (child, target_type) + { + } + + public static Expression Create (Expression child, TypeSpec type) + { + Constant c = child as Constant; + if (c != null) { + var enum_constant = c as EnumConstant; + if (enum_constant != null) + c = enum_constant.Child; + + if (!(c is ReducedExpression.ReducedConstantExpression)) { + if (c.Type == type) + return c; + + var res = c.ConvertImplicitly (type); + if (res != null) + return res; + } + } + + EmptyCast e = child as EmptyCast; + if (e != null) + return new EmptyCast (e.child, type); + + return new EmptyCast (child, type); + } + + public override void EmitBranchable (EmitContext ec, Label label, bool on_true) + { + child.EmitBranchable (ec, label, on_true); + } + + public override void EmitSideEffect (EmitContext ec) + { + child.EmitSideEffect (ec); + } + } + + // + // Used for predefined type user operator (no obsolete check, etc.) + // + public class OperatorCast : TypeCast + { + readonly MethodSpec conversion_operator; + + public OperatorCast (Expression expr, TypeSpec target_type) + : this (expr, target_type, target_type, false) + { + } + + public OperatorCast (Expression expr, TypeSpec target_type, bool find_explicit) + : this (expr, target_type, target_type, find_explicit) + { + } + + public OperatorCast (Expression expr, TypeSpec declaringType, TypeSpec returnType, bool isExplicit) + : base (expr, returnType) + { + var op = isExplicit ? Operator.OpType.Explicit : Operator.OpType.Implicit; + var mi = MemberCache.GetUserOperator (declaringType, op, true); + + if (mi != null) { + foreach (MethodSpec oper in mi) { + if (oper.ReturnType != returnType) + continue; + + if (oper.Parameters.Types[0] == expr.Type) { + conversion_operator = oper; + return; + } + } + } + + throw new InternalErrorException ("Missing predefined user operator between `{0}' and `{1}'", + returnType.GetSignatureForError (), expr.Type.GetSignatureForError ()); + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + ec.Emit (OpCodes.Call, conversion_operator); + } + } + + // + // Constant specialization of EmptyCast. + // We need to special case this since an empty cast of + // a constant is still a constant. + // + public class EmptyConstantCast : Constant + { + public readonly Constant child; + + public EmptyConstantCast (Constant child, TypeSpec type) + : base (child.Location) + { + if (child == null) + throw new ArgumentNullException ("child"); + + this.child = child; + this.eclass = child.eclass; + this.type = type; + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + if (child.Type == target_type) + return child; + + // FIXME: check that 'type' can be converted to 'target_type' first + return child.ConvertExplicitly (in_checked_context, target_type); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = Arguments.CreateForExpressionTree (ec, null, + child.CreateExpressionTree (ec), + new TypeOf (type, loc)); + + if (type.IsPointer) + Error_PointerInsideExpressionTree (ec); + + return CreateExpressionFactoryCall (ec, "Convert", args); + } + + public override bool IsDefaultValue { + get { return child.IsDefaultValue; } + } + + public override bool IsNegative { + get { return child.IsNegative; } + } + + public override bool IsNull { + get { return child.IsNull; } + } + + public override bool IsOneInteger { + get { return child.IsOneInteger; } + } + + public override bool IsSideEffectFree { + get { + return child.IsSideEffectFree; + } + } + + public override bool IsZeroInteger { + get { return child.IsZeroInteger; } + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + } + + public override void EmitBranchable (EmitContext ec, Label label, bool on_true) + { + child.EmitBranchable (ec, label, on_true); + + // Only to make verifier happy + if (TypeManager.IsGenericParameter (type) && child.IsNull) + ec.Emit (OpCodes.Unbox_Any, type); + } + + public override void EmitSideEffect (EmitContext ec) + { + child.EmitSideEffect (ec); + } + + public override object GetValue () + { + return child.GetValue (); + } + + public override string GetValueAsLiteral () + { + return child.GetValueAsLiteral (); + } + + public override long GetValueAsLong () + { + return child.GetValueAsLong (); + } + + public override Constant ConvertImplicitly (TypeSpec target_type) + { + if (type == target_type) + return this; + + // FIXME: Do we need to check user conversions? + if (!Convert.ImplicitStandardConversionExists (this, target_type)) + return null; + + return child.ConvertImplicitly (target_type); + } + } + + /// + /// This class is used to wrap literals which belong inside Enums + /// + public class EnumConstant : Constant + { + public Constant Child; + + public EnumConstant (Constant child, TypeSpec enum_type) + : base (child.Location) + { + this.Child = child; + + this.eclass = ExprClass.Value; + this.type = enum_type; + } + + protected EnumConstant (Location loc) + : base (loc) + { + } + + public override void Emit (EmitContext ec) + { + Child.Emit (ec); + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + Child.EncodeAttributeValue (rc, enc, Child.Type, parameterType); + } + + public override void EmitBranchable (EmitContext ec, Label label, bool on_true) + { + Child.EmitBranchable (ec, label, on_true); + } + + public override void EmitSideEffect (EmitContext ec) + { + Child.EmitSideEffect (ec); + } + + public override string GetSignatureForError() + { + return Type.GetSignatureForError (); + } + + public override object GetValue () + { + return Child.GetValue (); + } + +#if !STATIC + public override object GetTypedValue () + { + // + // The method can be used in dynamic context only (on closed types) + // + // System.Enum.ToObject cannot be called on dynamic types + // EnumBuilder has to be used, but we cannot use EnumBuilder + // because it does not properly support generics + // + return System.Enum.ToObject (type.GetMetaInfo (), Child.GetValue ()); + } +#endif + + public override string GetValueAsLiteral () + { + return Child.GetValueAsLiteral (); + } + + public override long GetValueAsLong () + { + return Child.GetValueAsLong (); + } + + public EnumConstant Increment() + { + return new EnumConstant (((IntegralConstant) Child).Increment (), type); + } + + public override bool IsDefaultValue { + get { + return Child.IsDefaultValue; + } + } + + public override bool IsSideEffectFree { + get { + return Child.IsSideEffectFree; + } + } + + public override bool IsZeroInteger { + get { return Child.IsZeroInteger; } + } + + public override bool IsNegative { + get { + return Child.IsNegative; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + if (Child.Type == target_type) + return Child; + + return Child.ConvertExplicitly (in_checked_context, target_type); + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (this.type == type) { + return this; + } + + if (!Convert.ImplicitStandardConversionExists (this, type)){ + return null; + } + + return Child.ConvertImplicitly (type); + } + } + + /// + /// This kind of cast is used to encapsulate Value Types in objects. + /// + /// The effect of it is to box the value type emitted by the previous + /// operation. + /// + public class BoxedCast : TypeCast { + + public BoxedCast (Expression expr, TypeSpec target_type) + : base (expr, target_type) + { + eclass = ExprClass.Value; + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // Only boxing to object type is supported + if (targetType.BuiltinType != BuiltinTypeSpec.Type.Object) { + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + return; + } + + enc.Encode (child.Type); + child.EncodeAttributeValue (rc, enc, child.Type, parameterType); + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + ec.Emit (OpCodes.Box, child.Type); + } + + public override void EmitSideEffect (EmitContext ec) + { + // boxing is side-effectful, since it involves runtime checks, except when boxing to Object or ValueType + // so, we need to emit the box+pop instructions in most cases + if (child.Type.IsStruct && + (type.BuiltinType == BuiltinTypeSpec.Type.Object || type.BuiltinType == BuiltinTypeSpec.Type.ValueType)) + child.EmitSideEffect (ec); + else + base.EmitSideEffect (ec); + } + } + + public class UnboxCast : TypeCast { + public UnboxCast (Expression expr, TypeSpec return_type) + : base (expr, return_type) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + ec.Emit (OpCodes.Unbox_Any, type); + } + } + + /// + /// This is used to perform explicit numeric conversions. + /// + /// Explicit numeric conversions might trigger exceptions in a checked + /// context, so they should generate the conv.ovf opcodes instead of + /// conv opcodes. + /// + public class ConvCast : TypeCast { + public enum Mode : byte { + I1_U1, I1_U2, I1_U4, I1_U8, I1_CH, + U1_I1, U1_CH, + I2_I1, I2_U1, I2_U2, I2_U4, I2_U8, I2_CH, + U2_I1, U2_U1, U2_I2, U2_CH, + I4_I1, I4_U1, I4_I2, I4_U2, I4_U4, I4_U8, I4_CH, + U4_I1, U4_U1, U4_I2, U4_U2, U4_I4, U4_CH, + I8_I1, I8_U1, I8_I2, I8_U2, I8_I4, I8_U4, I8_U8, I8_CH, I8_I, + U8_I1, U8_U1, U8_I2, U8_U2, U8_I4, U8_U4, U8_I8, U8_CH, U8_I, + CH_I1, CH_U1, CH_I2, + R4_I1, R4_U1, R4_I2, R4_U2, R4_I4, R4_U4, R4_I8, R4_U8, R4_CH, + R8_I1, R8_U1, R8_I2, R8_U2, R8_I4, R8_U4, R8_I8, R8_U8, R8_CH, R8_R4, + I_I8, + } + + Mode mode; + + public ConvCast (Expression child, TypeSpec return_type, Mode m) + : base (child, return_type) + { + mode = m; + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override string ToString () + { + return String.Format ("ConvCast ({0}, {1})", mode, child); + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + Emit (ec, mode); + } + + public static void Emit (EmitContext ec, Mode mode) + { + if (ec.HasSet (EmitContext.Options.CheckedScope)) { + switch (mode){ + case Mode.I1_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I1_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I1_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I1_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I1_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U1_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U1_CH: /* nothing */ break; + + case Mode.I2_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I2_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I2_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I2_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I2_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I2_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U2_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U2_U1: ec.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U2_I2: ec.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U2_CH: /* nothing */ break; + + case Mode.I4_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I4_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I4_I2: ec.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.I4_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I4_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I4_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I4_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U4_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U4_U1: ec.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U4_I2: ec.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U4_U2: ec.Emit (OpCodes.Conv_Ovf_U2_Un); break; + case Mode.U4_I4: ec.Emit (OpCodes.Conv_Ovf_I4_Un); break; + case Mode.U4_CH: ec.Emit (OpCodes.Conv_Ovf_U2_Un); break; + + case Mode.I8_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I8_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I8_I2: ec.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.I8_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I8_I4: ec.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.I8_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I8_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I8_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I8_I: ec.Emit (OpCodes.Conv_Ovf_U); break; + + case Mode.U8_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U8_U1: ec.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U8_I2: ec.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U8_U2: ec.Emit (OpCodes.Conv_Ovf_U2_Un); break; + case Mode.U8_I4: ec.Emit (OpCodes.Conv_Ovf_I4_Un); break; + case Mode.U8_U4: ec.Emit (OpCodes.Conv_Ovf_U4_Un); break; + case Mode.U8_I8: ec.Emit (OpCodes.Conv_Ovf_I8_Un); break; + case Mode.U8_CH: ec.Emit (OpCodes.Conv_Ovf_U2_Un); break; + case Mode.U8_I: ec.Emit (OpCodes.Conv_Ovf_U_Un); break; + + case Mode.CH_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.CH_U1: ec.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.CH_I2: ec.Emit (OpCodes.Conv_Ovf_I2_Un); break; + + case Mode.R4_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.R4_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.R4_I2: ec.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.R4_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R4_I4: ec.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.R4_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.R4_I8: ec.Emit (OpCodes.Conv_Ovf_I8); break; + case Mode.R4_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.R4_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.R8_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.R8_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.R8_I2: ec.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.R8_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R8_I4: ec.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.R8_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.R8_I8: ec.Emit (OpCodes.Conv_Ovf_I8); break; + case Mode.R8_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.R8_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R8_R4: ec.Emit (OpCodes.Conv_R4); break; + + case Mode.I_I8: ec.Emit (OpCodes.Conv_Ovf_I8_Un); break; + } + } else { + switch (mode){ + case Mode.I1_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.I1_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I1_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.I1_U8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.I1_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.U1_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.U1_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.I2_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.I2_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.I2_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I2_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.I2_U8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.I2_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.U2_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.U2_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.U2_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.U2_CH: /* nothing */ break; + + case Mode.I4_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.I4_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.I4_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.I4_U4: /* nothing */ break; + case Mode.I4_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I4_U8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.I4_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.U4_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.U4_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.U4_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.U4_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.U4_I4: /* nothing */ break; + case Mode.U4_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.I8_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.I8_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.I8_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.I8_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I8_I4: ec.Emit (OpCodes.Conv_I4); break; + case Mode.I8_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.I8_U8: /* nothing */ break; + case Mode.I8_CH: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I8_I: ec.Emit (OpCodes.Conv_U); break; + + case Mode.U8_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.U8_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.U8_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.U8_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.U8_I4: ec.Emit (OpCodes.Conv_I4); break; + case Mode.U8_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.U8_I8: /* nothing */ break; + case Mode.U8_CH: ec.Emit (OpCodes.Conv_U2); break; + case Mode.U8_I: ec.Emit (OpCodes.Conv_U); break; + + case Mode.CH_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.CH_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.CH_I2: ec.Emit (OpCodes.Conv_I2); break; + + case Mode.R4_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.R4_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.R4_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.R4_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.R4_I4: ec.Emit (OpCodes.Conv_I4); break; + case Mode.R4_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.R4_I8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.R4_U8: ec.Emit (OpCodes.Conv_U8); break; + case Mode.R4_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.R8_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.R8_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.R8_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.R8_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.R8_I4: ec.Emit (OpCodes.Conv_I4); break; + case Mode.R8_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.R8_I8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.R8_U8: ec.Emit (OpCodes.Conv_U8); break; + case Mode.R8_CH: ec.Emit (OpCodes.Conv_U2); break; + case Mode.R8_R4: ec.Emit (OpCodes.Conv_R4); break; + + case Mode.I_I8: ec.Emit (OpCodes.Conv_U8); break; + } + } + } + } + + class OpcodeCast : TypeCast + { + readonly OpCode op; + + public OpcodeCast (Expression child, TypeSpec return_type, OpCode op) + : base (child, return_type) + { + this.op = op; + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + ec.Emit (op); + } + + public TypeSpec UnderlyingType { + get { return child.Type; } + } + } + + // + // Opcode casts expression with 2 opcodes but only + // single expression tree node + // + class OpcodeCastDuplex : OpcodeCast + { + readonly OpCode second; + + public OpcodeCastDuplex (Expression child, TypeSpec returnType, OpCode first, OpCode second) + : base (child, returnType, first) + { + this.second = second; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + ec.Emit (second); + } + } + + /// + /// This kind of cast is used to encapsulate a child and cast it + /// to the class requested + /// + public sealed class ClassCast : TypeCast { + readonly bool forced; + + public ClassCast (Expression child, TypeSpec return_type) + : base (child, return_type) + { + } + + public ClassCast (Expression child, TypeSpec return_type, bool forced) + : base (child, return_type) + { + this.forced = forced; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + bool gen = TypeManager.IsGenericParameter (child.Type); + if (gen) + ec.Emit (OpCodes.Box, child.Type); + + if (type.IsGenericParameter) { + ec.Emit (OpCodes.Unbox_Any, type); + return; + } + + if (gen && !forced) + return; + + ec.Emit (OpCodes.Castclass, type); + } + } + + // + // Created during resolving pahse when an expression is wrapped or constantified + // and original expression can be used later (e.g. for expression trees) + // + public class ReducedExpression : Expression + { + public sealed class ReducedConstantExpression : EmptyConstantCast + { + readonly Expression orig_expr; + + public ReducedConstantExpression (Constant expr, Expression orig_expr) + : base (expr, expr.Type) + { + this.orig_expr = orig_expr; + } + + public Expression OriginalExpression { + get { + return orig_expr; + } + } + + public override Constant ConvertImplicitly (TypeSpec target_type) + { + Constant c = base.ConvertImplicitly (target_type); + if (c != null) + c = new ReducedConstantExpression (c, orig_expr); + + return c; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return orig_expr.CreateExpressionTree (ec); + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + Constant c = base.ConvertExplicitly (in_checked_context, target_type); + if (c != null) + c = new ReducedConstantExpression (c, orig_expr); + return c; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // + // LAMESPEC: Reduced conditional expression is allowed as an attribute argument + // + if (orig_expr is Conditional) + child.EncodeAttributeValue (rc, enc, targetType,parameterType); + else + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + } + } + + sealed class ReducedExpressionStatement : ExpressionStatement + { + readonly Expression orig_expr; + readonly ExpressionStatement stm; + + public ReducedExpressionStatement (ExpressionStatement stm, Expression orig) + { + this.orig_expr = orig; + this.stm = stm; + this.eclass = stm.eclass; + this.type = stm.Type; + + this.loc = orig.Location; + } + + public override bool ContainsEmitWithAwait () + { + return stm.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return orig_expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + stm.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + stm.EmitStatement (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + stm.FlowAnalysis (fc); + } + } + + readonly Expression expr, orig_expr; + + private ReducedExpression (Expression expr, Expression orig_expr) + { + this.expr = expr; + this.eclass = expr.eclass; + this.type = expr.Type; + this.orig_expr = orig_expr; + this.loc = orig_expr.Location; + } + + #region Properties + + public override bool IsSideEffectFree { + get { + return expr.IsSideEffectFree; + } + } + + public Expression OriginalExpression { + get { + return orig_expr; + } + } + + #endregion + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + // + // Creates fully resolved expression switcher + // + public static Constant Create (Constant expr, Expression original_expr) + { + if (expr.eclass == ExprClass.Unresolved) + throw new ArgumentException ("Unresolved expression"); + + return new ReducedConstantExpression (expr, original_expr); + } + + public static ExpressionStatement Create (ExpressionStatement s, Expression orig) + { + return new ReducedExpressionStatement (s, orig); + } + + public static Expression Create (Expression expr, Expression original_expr) + { + return Create (expr, original_expr, true); + } + + // + // Creates unresolved reduce expression. The original expression has to be + // already resolved. Created expression is constant based based on `expr' + // value unless canBeConstant is used + // + public static Expression Create (Expression expr, Expression original_expr, bool canBeConstant) + { + if (canBeConstant) { + Constant c = expr as Constant; + if (c != null) + return Create (c, original_expr); + } + + ExpressionStatement s = expr as ExpressionStatement; + if (s != null) + return Create (s, original_expr); + + if (expr.eclass == ExprClass.Unresolved) + throw new ArgumentException ("Unresolved expression"); + + return new ReducedExpression (expr, original_expr); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return orig_expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + } + + public override Expression EmitToField (EmitContext ec) + { + return expr.EmitToField(ec); + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + expr.EmitBranchable (ec, target, on_true); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return orig_expr.MakeExpression (ctx); + } + } + + // + // Standard composite pattern + // + public abstract class CompositeExpression : Expression + { + protected Expression expr; + + protected CompositeExpression (Expression expr) + { + this.expr = expr; + this.loc = expr.Location; + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext rc) + { + return expr.CreateExpressionTree (rc); + } + + public Expression Child { + get { return expr; } + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.Resolve (rc); + if (expr == null) + return null; + + type = expr.Type; + eclass = expr.eclass; + return this; + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + } + + public override bool IsNull { + get { return expr.IsNull; } + } + } + + // + // Base of expressions used only to narrow resolve flow + // + public abstract class ShimExpression : Expression + { + protected Expression expr; + + protected ShimExpression (Expression expr) + { + this.expr = expr; + } + + public Expression Expr { + get { + return expr; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + if (expr == null) + return; + + ShimExpression target = (ShimExpression) t; + target.expr = expr.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + public override void Emit (EmitContext ec) + { + throw new InternalErrorException ("Missing Resolve call"); + } + } + + public class UnreachableExpression : Expression + { + public UnreachableExpression (Expression expr) + { + this.loc = expr.Location; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + // TODO: is it ok + throw new NotImplementedException (); + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotSupportedException (); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + fc.Report.Warning (429, 4, loc, "Unreachable expression code detected"); + } + + public override void Emit (EmitContext ec) + { + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + } + } + + // + // Unresolved type name expressions + // + public abstract class ATypeNameExpression : FullNamedExpression + { + string name; + protected TypeArguments targs; + + protected ATypeNameExpression (string name, Location l) + { + this.name = name; + loc = l; + } + + protected ATypeNameExpression (string name, TypeArguments targs, Location l) + { + this.name = name; + this.targs = targs; + loc = l; + } + + protected ATypeNameExpression (string name, int arity, Location l) + : this (name, new UnboundTypeArguments (arity), l) + { + } + + #region Properties + + protected int Arity { + get { + return targs == null ? 0 : targs.Count; + } + } + + public bool HasTypeArguments { + get { + return targs != null && !targs.IsEmpty; + } + } + + public string Name { + get { + return name; + } + set { + name = value; + } + } + + public TypeArguments TypeArguments { + get { + return targs; + } + } + + #endregion + + public override bool Equals (object obj) + { + ATypeNameExpression atne = obj as ATypeNameExpression; + return atne != null && atne.Name == Name && + (targs == null || targs.Equals (atne.targs)); + } + + protected void Error_OpenGenericTypeIsNotAllowed (IMemberContext mc) + { + mc.Module.Compiler.Report.Error (7003, Location, "Unbound generic name is not valid in this context"); + } + + public override int GetHashCode () + { + return Name.GetHashCode (); + } + + // TODO: Move it to MemberCore + public static string GetMemberType (MemberCore mc) + { + if (mc is Property) + return "property"; + if (mc is Indexer) + return "indexer"; + if (mc is FieldBase) + return "field"; + if (mc is MethodCore) + return "method"; + if (mc is EnumMember) + return "enum"; + if (mc is Event) + return "event"; + + return "type"; + } + + public override string GetSignatureForError () + { + if (targs != null) { + return Name + "<" + targs.GetSignatureForError () + ">"; + } + + return Name; + } + + public abstract Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restriction); + } + + /// + /// SimpleName expressions are formed of a single word and only happen at the beginning + /// of a dotted-name. + /// + public class SimpleName : ATypeNameExpression + { + public SimpleName (string name, Location l) + : base (name, l) + { + } + + public SimpleName (string name, TypeArguments args, Location l) + : base (name, args, l) + { + } + + public SimpleName (string name, int arity, Location l) + : base (name, arity, l) + { + } + + public SimpleName GetMethodGroup () + { + return new SimpleName (Name, targs, loc); + } + + protected override Expression DoResolve (ResolveContext rc) + { + return SimpleNameResolve (rc, null); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return SimpleNameResolve (ec, right_side); + } + + public void Error_NameDoesNotExist (ResolveContext rc) + { + rc.Report.Error (103, loc, "The name `{0}' does not exist in the current context", Name); + } + + protected virtual void Error_TypeOrNamespaceNotFound (IMemberContext ctx) + { + if (ctx.CurrentType != null) { + var member = MemberLookup (ctx, false, ctx.CurrentType, Name, 0, MemberLookupRestrictions.ExactArity, loc) as MemberExpr; + if (member != null) { + Error_UnexpectedKind (ctx, member, "type", member.KindName, loc); + return; + } + } + + var report = ctx.Module.Compiler.Report; + + var retval = ctx.LookupNamespaceOrType (Name, Arity, LookupMode.IgnoreAccessibility, loc); + if (retval != null) { + report.SymbolRelatedToPreviousError (retval.Type); + ErrorIsInaccesible (ctx, retval.GetSignatureForError (), loc); + return; + } + + retval = ctx.LookupNamespaceOrType (Name, -System.Math.Max (1, Arity), LookupMode.Probing, loc); + if (retval != null) { + Error_TypeArgumentsCannotBeUsed (ctx, retval.Type, loc); + return; + } + + var ns_candidates = ctx.Module.GlobalRootNamespace.FindTypeNamespaces (ctx, Name, Arity); + if (ns_candidates != null) { + if (ctx is UsingAliasNamespace.AliasContext) { + report.Error (246, loc, + "The type or namespace name `{1}' could not be found. Consider using fully qualified name `{0}.{1}'", + ns_candidates[0], Name); + } else { + string usings = string.Join ("' or `", ns_candidates.ToArray ()); + report.Error (246, loc, + "The type or namespace name `{0}' could not be found. Are you missing `{1}' using directive?", + Name, usings); + } + } else { + report.Error (246, loc, + "The type or namespace name `{0}' could not be found. Are you missing an assembly reference?", + Name); + } + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + FullNamedExpression fne = mc.LookupNamespaceOrType (Name, Arity, LookupMode.Normal, loc); + + if (fne != null) { + if (fne.Type != null && Arity > 0) { + if (HasTypeArguments) { + GenericTypeExpr ct = new GenericTypeExpr (fne.Type, targs, loc); + if (ct.ResolveAsType (mc) == null) + return null; + + return ct; + } + + if (!allowUnboundTypeArguments) + Error_OpenGenericTypeIsNotAllowed (mc); + + return new GenericOpenTypeExpr (fne.Type, loc); + } + + // + // dynamic namespace is ignored when dynamic is allowed (does not apply to types) + // + if (!(fne is NamespaceExpression)) + return fne; + } + + if (Arity == 0 && Name == "dynamic" && mc.Module.Compiler.Settings.Version > LanguageVersion.V_3) { + if (!mc.Module.PredefinedAttributes.Dynamic.IsDefined) { + mc.Module.Compiler.Report.Error (1980, Location, + "Dynamic keyword requires `{0}' to be defined. Are you missing System.Core.dll assembly reference?", + mc.Module.PredefinedAttributes.Dynamic.GetSignatureForError ()); + } + + fne = new DynamicTypeExpr (loc); + fne.ResolveAsType (mc); + } + + if (fne != null) + return fne; + + Error_TypeOrNamespaceNotFound (mc); + return null; + } + + public bool IsPossibleTypeOrNamespace (IMemberContext mc) + { + return mc.LookupNamespaceOrType (Name, Arity, LookupMode.Probing, loc) != null; + } + + public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions) + { + int lookup_arity = Arity; + bool errorMode = false; + Expression e; + Block current_block = rc.CurrentBlock; + INamedBlockVariable variable = null; + bool variable_found = false; + + while (true) { + // + // Stage 1: binding to local variables or parameters + // + // LAMESPEC: It should take invocableOnly into account but that would break csc compatibility + // + if (current_block != null && lookup_arity == 0) { + if (current_block.ParametersBlock.TopBlock.GetLocalName (Name, current_block.Original, ref variable)) { + if (!variable.IsDeclared) { + // We found local name in accessible block but it's not + // initialized yet, maybe the user wanted to bind to something else + errorMode = true; + variable_found = true; + } else { + e = variable.CreateReferenceExpression (rc, loc); + if (e != null) { + if (Arity > 0) + Error_TypeArgumentsCannotBeUsed (rc, "variable", Name, loc); + + return e; + } + } + } + } + + // + // Stage 2: Lookup members if we are inside a type up to top level type for nested types + // + TypeSpec member_type = rc.CurrentType; + for (; member_type != null; member_type = member_type.DeclaringType) { + e = MemberLookup (rc, errorMode, member_type, Name, lookup_arity, restrictions, loc); + if (e == null) + continue; + + var me = e as MemberExpr; + if (me == null) { + // The name matches a type, defer to ResolveAsTypeStep + if (e is TypeExpr) + break; + + continue; + } + + if (errorMode) { + if (variable != null) { + if (me is FieldExpr || me is ConstantExpr || me is EventExpr || me is PropertyExpr) { + rc.Report.Error (844, loc, + "A local variable `{0}' cannot be used before it is declared. Consider renaming the local variable when it hides the member `{1}'", + Name, me.GetSignatureForError ()); + } else { + break; + } + } else if (me is MethodGroupExpr || me is PropertyExpr || me is IndexerExpr) { + // Leave it to overload resolution to report correct error + } else { + // TODO: rc.Report.SymbolRelatedToPreviousError () + ErrorIsInaccesible (rc, me.GetSignatureForError (), loc); + } + } else { + // LAMESPEC: again, ignores InvocableOnly + if (variable != null) { + rc.Report.SymbolRelatedToPreviousError (variable.Location, Name); + rc.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", Name); + } + + // + // MemberLookup does not check accessors availability, this is actually needed for properties only + // + var pe = me as PropertyExpr; + if (pe != null) { + + // Break as there is no other overload available anyway + if ((restrictions & MemberLookupRestrictions.ReadAccess) != 0) { + if (!pe.PropertyInfo.HasGet || !pe.PropertyInfo.Get.IsAccessible (rc)) + break; + + pe.Getter = pe.PropertyInfo.Get; + } else { + if (!pe.PropertyInfo.HasSet || !pe.PropertyInfo.Set.IsAccessible (rc)) + break; + + pe.Setter = pe.PropertyInfo.Set; + } + } + } + + // TODO: It's used by EventExpr -> FieldExpr transformation only + // TODO: Should go to MemberAccess + me = me.ResolveMemberAccess (rc, null, null); + + if (Arity > 0) { + targs.Resolve (rc); + me.SetTypeArguments (rc, targs); + } + + return me; + } + + // + // Stage 3: Lookup nested types, namespaces and type parameters in the context + // + if ((restrictions & MemberLookupRestrictions.InvocableOnly) == 0 && !variable_found) { + if (IsPossibleTypeOrNamespace (rc)) { + if (variable != null) { + rc.Report.SymbolRelatedToPreviousError (variable.Location, Name); + rc.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", Name); + } + + return ResolveAsTypeOrNamespace (rc, false); + } + } + + var mg = NamespaceContainer.LookupStaticUsings (rc, Name, Arity, loc); + if (mg != null) { + if (Arity > 0) { + targs.Resolve (rc); + mg.SetTypeArguments (rc, targs); + } + return mg; + } + + if (Name == "nameof") + return new NameOf (this); + + if (errorMode) { + if (variable_found) { + rc.Report.Error (841, loc, "A local variable `{0}' cannot be used before it is declared", Name); + } else { + if (Arity > 0) { + var tparams = rc.CurrentTypeParameters; + if (tparams != null) { + if (tparams.Find (Name) != null) { + Error_TypeArgumentsCannotBeUsed (rc, "type parameter", Name, loc); + return null; + } + } + + var ct = rc.CurrentType; + do { + if (ct.MemberDefinition.TypeParametersCount > 0) { + foreach (var ctp in ct.MemberDefinition.TypeParameters) { + if (ctp.Name == Name) { + Error_TypeArgumentsCannotBeUsed (rc, "type parameter", Name, loc); + return null; + } + } + } + + ct = ct.DeclaringType; + } while (ct != null); + } + + if ((restrictions & MemberLookupRestrictions.InvocableOnly) == 0) { + e = rc.LookupNamespaceOrType (Name, Arity, LookupMode.IgnoreAccessibility, loc); + if (e != null) { + rc.Report.SymbolRelatedToPreviousError (e.Type); + ErrorIsInaccesible (rc, e.GetSignatureForError (), loc); + return e; + } + } else { + var me = MemberLookup (rc, false, rc.CurrentType, Name, Arity, restrictions & ~MemberLookupRestrictions.InvocableOnly, loc) as MemberExpr; + if (me != null) { + Error_UnexpectedKind (rc, me, "method group", me.KindName, loc); + return ErrorExpression.Instance; + } + } + + e = rc.LookupNamespaceOrType (Name, -System.Math.Max (1, Arity), LookupMode.Probing, loc); + if (e != null) { + if (e.Type.Arity != Arity && (restrictions & MemberLookupRestrictions.IgnoreArity) == 0) { + Error_TypeArgumentsCannotBeUsed (rc, e.Type, loc); + return e; + } + + if (e is TypeExpr) { + // TypeExpression does not have correct location + if (e is TypeExpression) + e = new TypeExpression (e.Type, loc); + + return e; + } + } + + Error_NameDoesNotExist (rc); + } + + return ErrorExpression.Instance; + } + + if (rc.Module.Evaluator != null) { + var fi = rc.Module.Evaluator.LookupField (Name); + if (fi != null) + return new FieldExpr (fi.Item1, loc); + } + + lookup_arity = 0; + errorMode = true; + } + } + + Expression SimpleNameResolve (ResolveContext ec, Expression right_side) + { + Expression e = LookupNameExpression (ec, right_side == null ? MemberLookupRestrictions.ReadAccess : MemberLookupRestrictions.None); + + if (e == null) + return null; + + if (e is FullNamedExpression && e.eclass != ExprClass.Unresolved) { + Error_UnexpectedKind (ec, e, "variable", e.ExprClassName, loc); + return e; + } + + if (right_side != null) { + e = e.ResolveLValue (ec, right_side); + } else { + e = e.Resolve (ec); + } + + return e; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Represents a namespace or a type. The name of the class was inspired by + /// section 10.8.1 (Fully Qualified Names). + /// + public abstract class FullNamedExpression : Expression + { + protected override void CloneTo (CloneContext clonectx, Expression target) + { + // Do nothing, most unresolved type expressions cannot be + // resolved to different type + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + public abstract FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments); + + // + // This is used to resolve the expression as a type, a null + // value will be returned if the expression is not a type + // reference + // + public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + FullNamedExpression fne = ResolveAsTypeOrNamespace (mc, allowUnboundTypeArguments); + + if (fne == null) + return null; + + TypeExpr te = fne as TypeExpr; + if (te == null) { + Error_UnexpectedKind (mc, fne, "type", fne.ExprClassName, loc); + return null; + } + + te.loc = loc; + + type = te.Type; + + var dep = type.GetMissingDependencies (); + if (dep != null) { + ImportedTypeDefinition.Error_MissingDependency (mc, dep, loc); + } + + if (type.Kind == MemberKind.Void) { + mc.Module.Compiler.Report.Error (673, loc, "System.Void cannot be used from C#. Consider using `void'"); + } + + // + // Obsolete checks cannot be done when resolving base context as they + // require type dependencies to be set but we are in process of resolving them + // + if (!(mc is TypeDefinition.BaseContext) && !(mc is UsingAliasNamespace.AliasContext)) { + ObsoleteAttribute obsolete_attr = type.GetAttributeObsolete (); + if (obsolete_attr != null && !mc.IsObsolete) { + AttributeTester.Report_ObsoleteMessage (obsolete_attr, te.GetSignatureForError (), Location, mc.Module.Compiler.Report); + } + } + + return type; + } + + + public override void Emit (EmitContext ec) + { + throw new InternalErrorException ("FullNamedExpression `{0}' found in resolved tree", + GetSignatureForError ()); + } + } + + /// + /// Expression that evaluates to a type + /// + public abstract class TypeExpr : FullNamedExpression + { + public sealed override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + ResolveAsType (mc); + return this; + } + + protected sealed override Expression DoResolve (ResolveContext ec) + { + ResolveAsType (ec); + return this; + } + + public override bool Equals (object obj) + { + TypeExpr tobj = obj as TypeExpr; + if (tobj == null) + return false; + + return Type == tobj.Type; + } + + public override int GetHashCode () + { + return Type.GetHashCode (); + } + } + + /// + /// Fully resolved Expression that already evaluated to a type + /// + public class TypeExpression : TypeExpr + { + public TypeExpression (TypeSpec t, Location l) + { + Type = t; + eclass = ExprClass.Type; + loc = l; + } + + public sealed override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + return type; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class NamespaceExpression : FullNamedExpression + { + readonly Namespace ns; + + public NamespaceExpression (Namespace ns, Location loc) + { + this.ns = ns; + this.Type = InternalType.Namespace; + this.eclass = ExprClass.Namespace; + this.loc = loc; + } + + public Namespace Namespace { + get { + return ns; + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotImplementedException (); + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + return this; + } + + public void Error_NamespaceDoesNotExist (IMemberContext ctx, string name, int arity) + { + var retval = Namespace.LookupType (ctx, name, arity, LookupMode.IgnoreAccessibility, loc); + if (retval != null) { +// ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (retval.MemberDefinition); + ErrorIsInaccesible (ctx, retval.GetSignatureForError (), loc); + return; + } + + retval = Namespace.LookupType (ctx, name, -System.Math.Max (1, arity), LookupMode.Probing, loc); + if (retval != null) { + Error_TypeArgumentsCannotBeUsed (ctx, retval, loc); + return; + } + + Namespace ns; + if (arity > 0 && Namespace.TryGetNamespace (name, out ns)) { + Error_TypeArgumentsCannotBeUsed (ctx, ExprClassName, ns.GetSignatureForError (), loc); + return; + } + + string assembly = null; + string possible_name = Namespace.GetSignatureForError () + "." + name; + + // Only assembly unique name should be added + switch (possible_name) { + case "System.Drawing": + case "System.Web.Services": + case "System.Web": + case "System.Data": + case "System.Configuration": + case "System.Data.Services": + case "System.DirectoryServices": + case "System.Json": + case "System.Net.Http": + case "System.Numerics": + case "System.Runtime.Caching": + case "System.ServiceModel": + case "System.Transactions": + case "System.Web.Routing": + case "System.Xml.Linq": + case "System.Xml": + assembly = possible_name; + break; + + case "System.Linq": + case "System.Linq.Expressions": + assembly = "System.Core"; + break; + + case "System.Windows.Forms": + case "System.Windows.Forms.Layout": + assembly = "System.Windows.Forms"; + break; + } + + assembly = assembly == null ? "an" : "`" + assembly + "'"; + + if (Namespace is GlobalRootNamespace) { + ctx.Module.Compiler.Report.Error (400, loc, + "The type or namespace name `{0}' could not be found in the global namespace. Are you missing {1} assembly reference?", + name, assembly); + } else { + ctx.Module.Compiler.Report.Error (234, loc, + "The type or namespace name `{0}' does not exist in the namespace `{1}'. Are you missing {2} assembly reference?", + name, GetSignatureForError (), assembly); + } + } + + public override string GetSignatureForError () + { + return ns.GetSignatureForError (); + } + + public FullNamedExpression LookupTypeOrNamespace (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc) + { + return ns.LookupTypeOrNamespace (ctx, name, arity, mode, loc); + } + } + + /// + /// This class denotes an expression which evaluates to a member + /// of a struct or a class. + /// + public abstract class MemberExpr : Expression, OverloadResolver.IInstanceQualifier + { + protected bool conditional_access_receiver; + + // + // An instance expression associated with this member, if it's a + // non-static member + // + public Expression InstanceExpression; + + /// + /// The name of this member. + /// + public abstract string Name { + get; + } + + // + // When base.member is used + // + public bool IsBase { + get { return InstanceExpression is BaseThis; } + } + + /// + /// Whether this is an instance member. + /// + public abstract bool IsInstance { + get; + } + + /// + /// Whether this is a static member. + /// + public abstract bool IsStatic { + get; + } + + public abstract string KindName { + get; + } + + public bool ConditionalAccess { get; set; } + + protected abstract TypeSpec DeclaringType { + get; + } + + TypeSpec OverloadResolver.IInstanceQualifier.InstanceType { + get { + return InstanceExpression.Type; + } + } + + // + // Converts best base candidate for virtual method starting from QueriedBaseType + // + protected MethodSpec CandidateToBaseOverride (ResolveContext rc, MethodSpec method) + { + // + // Only when base.member is used and method is virtual + // + if (!IsBase) + return method; + + // + // Overload resulution works on virtual or non-virtual members only (no overrides). That + // means for base.member access we have to find the closest match after we found best candidate + // + if ((method.Modifiers & (Modifiers.ABSTRACT | Modifiers.VIRTUAL | Modifiers.OVERRIDE)) != 0) { + // + // The method could already be what we are looking for + // + TypeSpec[] targs = null; + if (method.DeclaringType != InstanceExpression.Type) { + // + // Candidate can have inflated MVAR parameters and we need to find + // base match for original definition not inflated parameter types + // + var parameters = method.Parameters; + if (method.Arity > 0) { + parameters = ((IParametersMember) method.MemberDefinition).Parameters; + var inflated = method.DeclaringType as InflatedTypeSpec; + if (inflated != null) { + parameters = parameters.Inflate (inflated.CreateLocalInflator (rc)); + } + } + + var filter = new MemberFilter (method.Name, method.Arity, MemberKind.Method, parameters, null); + var base_override = MemberCache.FindMember (InstanceExpression.Type, filter, BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as MethodSpec; + if (base_override != null && base_override.DeclaringType != method.DeclaringType) { + if (base_override.IsGeneric) + targs = method.TypeArguments; + + method = base_override; + } + } + + // + // When base access is used inside anonymous method/iterator/etc we need to + // get back to the context of original type. We do it by emiting proxy + // method in original class and rewriting base call to this compiler + // generated method call which does the actual base invocation. This may + // introduce redundant storey but with `this' only but it's tricky to avoid + // at this stage as we don't know what expressions follow base + // + if (rc.CurrentAnonymousMethod != null) { + if (targs == null && method.IsGeneric) { + targs = method.TypeArguments; + method = method.GetGenericMethodDefinition (); + } + + if (method.Parameters.HasArglist) + throw new NotImplementedException ("__arglist base call proxy"); + + method = rc.CurrentMemberDefinition.Parent.PartialContainer.CreateHoistedBaseCallProxy (rc, method); + + // Ideally this should apply to any proxy rewrite but in the case of unary mutators on + // get/set member expressions second call would fail to proxy because left expression + // would be of 'this' and not 'base' because we share InstanceExpression for get/set + // FIXME: The async check is another hack but will probably fail with mutators + if (rc.CurrentType.IsStruct || rc.CurrentAnonymousMethod.Storey is AsyncTaskStorey) + InstanceExpression = new This (loc).Resolve (rc); + } + + if (targs != null) + method = method.MakeGenericMethod (rc, targs); + } + + // + // Only base will allow this invocation to happen. + // + if (method.IsAbstract) { + rc.Report.SymbolRelatedToPreviousError (method); + Error_CannotCallAbstractBase (rc, method.GetSignatureForError ()); + } + + return method; + } + + protected void CheckProtectedMemberAccess (ResolveContext rc, MemberSpec member) + { + if (InstanceExpression == null) + return; + + if ((member.Modifiers & Modifiers.PROTECTED) != 0 && !(InstanceExpression is This)) { + if (!CheckProtectedMemberAccess (rc, member, InstanceExpression.Type)) { + Error_ProtectedMemberAccess (rc, member, InstanceExpression.Type, loc); + } + } + } + + bool OverloadResolver.IInstanceQualifier.CheckProtectedMemberAccess (ResolveContext rc, MemberSpec member) + { + if (InstanceExpression == null) + return true; + + return InstanceExpression is This || CheckProtectedMemberAccess (rc, member, InstanceExpression.Type); + } + + public static bool CheckProtectedMemberAccess (ResolveContext rc, T member, TypeSpec qualifier) where T : MemberSpec + { + var ct = rc.CurrentType; + if (ct == qualifier) + return true; + + if ((member.Modifiers & Modifiers.INTERNAL) != 0 && member.DeclaringType.MemberDefinition.IsInternalAsPublic (ct.MemberDefinition.DeclaringAssembly)) + return true; + + qualifier = qualifier.GetDefinition (); + if (ct != qualifier && !IsSameOrBaseQualifier (ct, qualifier)) { + return false; + } + + return true; + } + + public override bool ContainsEmitWithAwait () + { + return InstanceExpression != null && InstanceExpression.ContainsEmitWithAwait (); + } + + public override bool HasConditionalAccess () + { + return ConditionalAccess || (InstanceExpression != null && InstanceExpression.HasConditionalAccess ()); + } + + static bool IsSameOrBaseQualifier (TypeSpec type, TypeSpec qtype) + { + do { + type = type.GetDefinition (); + + if (type == qtype || TypeManager.IsFamilyAccessible (qtype, type)) + return true; + + type = type.DeclaringType; + } while (type != null); + + return false; + } + + protected void DoBestMemberChecks (ResolveContext rc, T member) where T : MemberSpec, IInterfaceMemberSpec + { + if (InstanceExpression != null) { + InstanceExpression = InstanceExpression.Resolve (rc); + CheckProtectedMemberAccess (rc, member); + } + + if (member.MemberType.IsPointer && !rc.IsUnsafe) { + UnsafeError (rc, loc); + } + + var dep = member.GetMissingDependencies (); + if (dep != null) { + ImportedTypeDefinition.Error_MissingDependency (rc, dep, loc); + } + + if (!rc.IsObsolete) { + ObsoleteAttribute oa = member.GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, member.GetSignatureForError (), loc, rc.Report); + } + + if (!(member is FieldSpec)) + member.MemberDefinition.SetIsUsed (); + } + + protected virtual void Error_CannotCallAbstractBase (ResolveContext rc, string name) + { + rc.Report.Error (205, loc, "Cannot call an abstract base member `{0}'", name); + } + + public static void Error_ProtectedMemberAccess (ResolveContext rc, MemberSpec member, TypeSpec qualifier, Location loc) + { + rc.Report.SymbolRelatedToPreviousError (member); + rc.Report.Error (1540, loc, + "Cannot access protected member `{0}' via a qualifier of type `{1}'. The qualifier must be of type `{2}' or derived from it", + member.GetSignatureForError (), qualifier.GetSignatureForError (), rc.CurrentType.GetSignatureForError ()); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (InstanceExpression != null) { + InstanceExpression.FlowAnalysis (fc); + + if (ConditionalAccess) { + fc.BranchConditionalAccessDefiniteAssignment (); + } + } + } + + protected void ResolveConditionalAccessReceiver (ResolveContext rc) + { + if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) { + if (HasConditionalAccess ()) { + conditional_access_receiver = true; + rc.Set (ResolveContext.Options.ConditionalAccessReceiver); + } + } + } + + public bool ResolveInstanceExpression (ResolveContext rc, Expression rhs) + { + if (!ResolveInstanceExpressionCore (rc, rhs)) + return false; + + // + // Check intermediate value modification which won't have any effect + // + if (rhs != null && TypeSpec.IsValueType (InstanceExpression.Type)) { + var fexpr = InstanceExpression as FieldExpr; + if (fexpr != null) { + if (!fexpr.Spec.IsReadOnly || rc.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.ConstructorScope)) + return true; + + if (fexpr.IsStatic) { + rc.Report.Error (1650, loc, "Fields of static readonly field `{0}' cannot be assigned to (except in a static constructor or a variable initializer)", + fexpr.GetSignatureForError ()); + } else { + rc.Report.Error (1648, loc, "Members of readonly field `{0}' cannot be modified (except in a constructor or a variable initializer)", + fexpr.GetSignatureForError ()); + } + + return true; + } + + if (InstanceExpression is PropertyExpr || InstanceExpression is IndexerExpr || InstanceExpression is Invocation) { + if (rc.CurrentInitializerVariable != null) { + rc.Report.Error (1918, loc, "Members of value type `{0}' cannot be assigned using a property `{1}' object initializer", + InstanceExpression.Type.GetSignatureForError (), InstanceExpression.GetSignatureForError ()); + } else { + rc.Report.Error (1612, loc, + "Cannot modify a value type return value of `{0}'. Consider storing the value in a temporary variable", + InstanceExpression.GetSignatureForError ()); + } + + return true; + } + + var lvr = InstanceExpression as LocalVariableReference; + if (lvr != null) { + + if (!lvr.local_info.IsReadonly) + return true; + + rc.Report.Error (1654, loc, "Cannot assign to members of `{0}' because it is a `{1}'", + InstanceExpression.GetSignatureForError (), lvr.local_info.GetReadOnlyContext ()); + } + } + + return true; + } + + bool ResolveInstanceExpressionCore (ResolveContext rc, Expression rhs) + { + if (IsStatic) { + if (InstanceExpression != null) { + if (InstanceExpression is TypeExpr) { + var t = InstanceExpression.Type; + do { + ObsoleteAttribute oa = t.GetAttributeObsolete (); + if (oa != null && !rc.IsObsolete) { + AttributeTester.Report_ObsoleteMessage (oa, t.GetSignatureForError (), loc, rc.Report); + } + + t = t.DeclaringType; + } while (t != null); + } else { + var runtime_expr = InstanceExpression as RuntimeValueExpression; + if (runtime_expr == null || !runtime_expr.IsSuggestionOnly) { + rc.Report.Error (176, loc, + "Static member `{0}' cannot be accessed with an instance reference, qualify it with a type name instead", + GetSignatureForError ()); + } + } + + InstanceExpression = null; + } + + return false; + } + + if (InstanceExpression == null || InstanceExpression is TypeExpr) { + if (InstanceExpression != null || !This.IsThisAvailable (rc, true)) { + if (rc.HasSet (ResolveContext.Options.FieldInitializerScope)) { + rc.Report.Error (236, loc, + "A field initializer cannot reference the nonstatic field, method, or property `{0}'", + GetSignatureForError ()); + } else { + var fe = this as FieldExpr; + if (fe != null && fe.Spec.MemberDefinition is PrimaryConstructorField) { + if (rc.HasSet (ResolveContext.Options.BaseInitializer)) { + rc.Report.Error (9005, loc, "Constructor initializer cannot access primary constructor parameters"); + } else { + rc.Report.Error (9006, loc, "An object reference is required to access primary constructor parameter `{0}'", + fe.Name); + } + } else { + rc.Report.Error (120, loc, + "An object reference is required to access non-static member `{0}'", + GetSignatureForError ()); + } + } + + InstanceExpression = new CompilerGeneratedThis (rc.CurrentType, loc).Resolve (rc); + return false; + } + + if (!TypeManager.IsFamilyAccessible (rc.CurrentType, DeclaringType)) { + rc.Report.Error (38, loc, + "Cannot access a nonstatic member of outer type `{0}' via nested type `{1}'", + DeclaringType.GetSignatureForError (), rc.CurrentType.GetSignatureForError ()); + } + + InstanceExpression = new This (loc).Resolve (rc); + return false; + } + + var me = InstanceExpression as MemberExpr; + if (me != null) { + me.ResolveInstanceExpressionCore (rc, rhs); + + var fe = me as FieldExpr; + if (fe != null && fe.IsMarshalByRefAccess (rc)) { + rc.Report.SymbolRelatedToPreviousError (me.DeclaringType); + rc.Report.Warning (1690, 1, loc, + "Cannot call methods, properties, or indexers on `{0}' because it is a value type member of a marshal-by-reference class", + me.GetSignatureForError ()); + } + + return true; + } + + // + // Additional checks for l-value member access + // + if (rhs != null) { + if (InstanceExpression is UnboxCast) { + rc.Report.Error (445, InstanceExpression.Location, "Cannot modify the result of an unboxing conversion"); + } + } + + return true; + } + + public virtual MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original) + { + if (left != null && !ConditionalAccess && left.IsNull && TypeSpec.IsReferenceType (left.Type)) { + ec.Report.Warning (1720, 1, left.Location, + "Expression will always cause a `{0}'", "System.NullReferenceException"); + } + + InstanceExpression = left; + return this; + } + + protected void EmitInstance (EmitContext ec, bool prepare_for_load) + { + var inst = new InstanceEmitter (InstanceExpression, TypeSpec.IsValueType (InstanceExpression.Type)); + inst.Emit (ec, ConditionalAccess); + + if (prepare_for_load) + ec.Emit (OpCodes.Dup); + } + + public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta); + } + + public class ExtensionMethodCandidates + { + readonly NamespaceContainer container; + readonly IList methods; + readonly int index; + readonly IMemberContext context; + + public ExtensionMethodCandidates (IMemberContext context, IList methods, NamespaceContainer nsContainer, int lookupIndex) + { + this.context = context; + this.methods = methods; + this.container = nsContainer; + this.index = lookupIndex; + } + + public NamespaceContainer Container { + get { + return container; + } + } + + public IMemberContext Context { + get { + return context; + } + } + + public int LookupIndex { + get { + return index; + } + } + + public IList Methods { + get { + return methods; + } + } + } + + // + // Represents a group of extension method candidates for whole namespace + // + class ExtensionMethodGroupExpr : MethodGroupExpr, OverloadResolver.IErrorHandler + { + ExtensionMethodCandidates candidates; + public Expression ExtensionExpression; + + public ExtensionMethodGroupExpr (ExtensionMethodCandidates candidates, Expression extensionExpr, Location loc) + : base (candidates.Methods.Cast().ToList (), extensionExpr.Type, loc) + { + this.candidates = candidates; + this.ExtensionExpression = extensionExpr; + } + + public override bool IsStatic { + get { return true; } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (ConditionalAccess) { + fc.BranchConditionalAccessDefiniteAssignment (); + } + } + + // + // For extension methodgroup we are not looking for base members but parent + // namespace extension methods + // + public override IList GetBaseMembers (TypeSpec baseType) + { + // TODO: candidates are null only when doing error reporting, that's + // incorrect. We have to discover same extension methods in error mode + if (candidates == null) + return null; + + int arity = type_arguments == null ? 0 : type_arguments.Count; + + candidates = candidates.Container.LookupExtensionMethod (candidates.Context, ExtensionExpression.Type, Name, arity, candidates.LookupIndex); + if (candidates == null) + return null; + + return candidates.Methods.Cast ().ToList (); + } + + public override MethodGroupExpr LookupExtensionMethod (ResolveContext rc) + { + // We are already here + return null; + } + + public override MethodGroupExpr OverloadResolve (ResolveContext ec, ref Arguments arguments, OverloadResolver.IErrorHandler ehandler, OverloadResolver.Restrictions restr) + { + if (arguments == null) + arguments = new Arguments (1); + + ExtensionExpression = ExtensionExpression.Resolve (ec); + if (ExtensionExpression == null) + return null; + + var cand = candidates; + var atype = ConditionalAccess ? Argument.AType.ExtensionTypeConditionalAccess : Argument.AType.ExtensionType; + arguments.Insert (0, new Argument (ExtensionExpression, atype)); + var res = base.OverloadResolve (ec, ref arguments, ehandler ?? this, restr); + + // Restore candidates in case we are running in probing mode + candidates = cand; + + // Store resolved argument and restore original arguments + if (res == null) { + // Clean-up modified arguments for error reporting + arguments.RemoveAt (0); + return null; + } + + var me = ExtensionExpression as MemberExpr; + if (me != null) { + me.ResolveInstanceExpression (ec, null); + var fe = me as FieldExpr; + if (fe != null) + fe.Spec.MemberDefinition.SetIsUsed (); + } + + InstanceExpression = null; + return this; + } + + #region IErrorHandler Members + + bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext rc, MemberSpec best, MemberSpec ambiguous) + { + return false; + } + + bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index) + { + rc.Report.SymbolRelatedToPreviousError (best); + rc.Report.Error (1928, loc, + "Type `{0}' does not contain a member `{1}' and the best extension method overload `{2}' has some invalid arguments", + queried_type.GetSignatureForError (), Name, best.GetSignatureForError ()); + + if (index == 0) { + rc.Report.Error (1929, loc, + "Extension method instance type `{0}' cannot be converted to `{1}'", + arg.Type.GetSignatureForError (), ((MethodSpec)best).Parameters.ExtensionMethodType.GetSignatureForError ()); + } + + return true; + } + + bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best) + { + return false; + } + + bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best) + { + return false; + } + + #endregion + } + + /// + /// MethodGroupExpr represents a group of method candidates which + /// can be resolved to the best method overload + /// + public class MethodGroupExpr : MemberExpr, OverloadResolver.IBaseMembersProvider + { + static readonly MemberSpec[] Excluded = new MemberSpec[0]; + + protected IList Methods; + MethodSpec best_candidate; + TypeSpec best_candidate_return; + protected TypeArguments type_arguments; + + SimpleName simple_name; + protected TypeSpec queried_type; + + public MethodGroupExpr (IList mi, TypeSpec type, Location loc) + { + Methods = mi; + this.loc = loc; + this.type = InternalType.MethodGroup; + + eclass = ExprClass.MethodGroup; + queried_type = type; + } + + public MethodGroupExpr (MethodSpec m, TypeSpec type, Location loc) + : this (new MemberSpec[] { m }, type, loc) + { + } + + #region Properties + + public MethodSpec BestCandidate { + get { + return best_candidate; + } + } + + public TypeSpec BestCandidateReturnType { + get { + return best_candidate_return; + } + } + + public IList Candidates { + get { + return Methods; + } + } + + protected override TypeSpec DeclaringType { + get { + return queried_type; + } + } + + public bool IsConditionallyExcluded { + get { + return Methods == Excluded; + } + } + + public override bool IsInstance { + get { + if (best_candidate != null) + return !best_candidate.IsStatic; + + return false; + } + } + + public override bool IsSideEffectFree { + get { + return InstanceExpression == null || InstanceExpression.IsSideEffectFree; + } + } + + public override bool IsStatic { + get { + if (best_candidate != null) + return best_candidate.IsStatic; + + return false; + } + } + + public override string KindName { + get { return "method"; } + } + + public override string Name { + get { + if (best_candidate != null) + return best_candidate.Name; + + // TODO: throw ? + return Methods.First ().Name; + } + } + + #endregion + + // + // When best candidate is already know this factory can be used + // to avoid expensive overload resolution to be called + // + // NOTE: InstanceExpression has to be set manually + // + public static MethodGroupExpr CreatePredefined (MethodSpec best, TypeSpec queriedType, Location loc) + { + return new MethodGroupExpr (best, queriedType, loc) { + best_candidate = best, + best_candidate_return = best.ReturnType + }; + } + + public override string GetSignatureForError () + { + if (best_candidate != null) + return best_candidate.GetSignatureForError (); + + return Methods.First ().GetSignatureForError (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (best_candidate == null) { + ec.Report.Error (1953, loc, "An expression tree cannot contain an expression with method group"); + return null; + } + + if (IsConditionallyExcluded) + ec.Report.Error (765, loc, + "Partial methods with only a defining declaration or removed conditional methods cannot be used in an expression tree"); + + if (ConditionalAccess) + Error_NullShortCircuitInsideExpressionTree (ec); + + return new TypeOfMethod (best_candidate, loc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + this.eclass = ExprClass.MethodGroup; + + if (InstanceExpression != null) { + InstanceExpression = InstanceExpression.Resolve (ec); + if (InstanceExpression == null) + return null; + } + + return this; + } + + public override void Emit (EmitContext ec) + { + throw new NotSupportedException (); + } + + public void EmitCall (EmitContext ec, Arguments arguments, bool statement) + { + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpression; + call.ConditionalAccess = ConditionalAccess; + + if (statement) + call.EmitStatement (ec, best_candidate, arguments, loc); + else + call.Emit (ec, best_candidate, arguments, loc); + } + + public void EmitCall (EmitContext ec, Arguments arguments, TypeSpec conditionalAccessReceiver, bool statement) + { + ec.ConditionalAccess = new ConditionalAccessContext (conditionalAccessReceiver, ec.DefineLabel ()) { + Statement = statement + }; + + EmitCall (ec, arguments, statement); + + ec.CloseConditionalAccess (!statement && best_candidate_return != conditionalAccessReceiver && conditionalAccessReceiver.IsNullableType ? conditionalAccessReceiver : null); + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + ec.Report.Error (428, loc, "Cannot convert method group `{0}' to non-delegate type `{1}'. Consider using parentheses to invoke the method", + Name, target.GetSignatureForError ()); + } + + public static bool IsExtensionMethodArgument (Expression expr) + { + // + // LAMESPEC: No details about which expressions are not allowed + // + return !(expr is TypeExpr) && !(expr is BaseThis); + } + + /// + /// Find the Applicable Function Members (7.4.2.1) + /// + /// me: Method Group expression with the members to select. + /// it might contain constructors or methods (or anything + /// that maps to a method). + /// + /// Arguments: ArrayList containing resolved Argument objects. + /// + /// loc: The location if we want an error to be reported, or a Null + /// location for "probing" purposes. + /// + /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo) + /// that is the best match of me on Arguments. + /// + /// + public virtual MethodGroupExpr OverloadResolve (ResolveContext ec, ref Arguments args, OverloadResolver.IErrorHandler cerrors, OverloadResolver.Restrictions restr) + { + // TODO: causes issues with probing mode, remove explicit Kind check + if (best_candidate != null && best_candidate.Kind == MemberKind.Destructor) + return this; + + var r = new OverloadResolver (Methods, type_arguments, restr, loc); + if ((restr & OverloadResolver.Restrictions.NoBaseMembers) == 0) { + r.BaseMembersProvider = this; + r.InstanceQualifier = this; + } + + if (cerrors != null) + r.CustomErrors = cerrors; + + // TODO: When in probing mode do IsApplicable only and when called again do VerifyArguments for full error reporting + best_candidate = r.ResolveMember (ec, ref args); + if (best_candidate == null) { + if (!r.BestCandidateIsDynamic) + return null; + + if (simple_name != null && ec.IsStatic) + InstanceExpression = ProbeIdenticalTypeName (ec, InstanceExpression, simple_name); + + return this; + } + + // Overload resolver had to create a new method group, all checks bellow have already been executed + if (r.BestCandidateNewMethodGroup != null) + return r.BestCandidateNewMethodGroup; + + if (best_candidate.Kind == MemberKind.Method && (restr & OverloadResolver.Restrictions.ProbingOnly) == 0) { + if (InstanceExpression != null) { + if (best_candidate.IsExtensionMethod && args[0].Expr == InstanceExpression) { + InstanceExpression = null; + } else { + if (simple_name != null && best_candidate.IsStatic) { + InstanceExpression = ProbeIdenticalTypeName (ec, InstanceExpression, simple_name); + } + + InstanceExpression.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup | ResolveFlags.Type); + } + } + + ResolveInstanceExpression (ec, null); + } + + var base_override = CandidateToBaseOverride (ec, best_candidate); + if (base_override == best_candidate) { + best_candidate_return = r.BestCandidateReturnType; + } else { + best_candidate = base_override; + best_candidate_return = best_candidate.ReturnType; + } + + if (best_candidate.IsGeneric && (restr & OverloadResolver.Restrictions.ProbingOnly) == 0 && TypeParameterSpec.HasAnyTypeParameterConstrained (best_candidate.GenericDefinition)) { + ConstraintChecker cc = new ConstraintChecker (ec); + cc.CheckAll (best_candidate.GetGenericMethodDefinition (), best_candidate.TypeArguments, best_candidate.Constraints, loc); + } + + // + // Additional check for possible imported base override method which + // could not be done during IsOverrideMethodBaseTypeAccessible + // + if (best_candidate.IsVirtual && (best_candidate.DeclaringType.Modifiers & Modifiers.PROTECTED) != 0 && + best_candidate.MemberDefinition.IsImported && !best_candidate.DeclaringType.IsAccessible (ec)) { + ec.Report.SymbolRelatedToPreviousError (best_candidate); + ErrorIsInaccesible (ec, best_candidate.GetSignatureForError (), loc); + } + + // Speed up the check by not doing it on disallowed targets + if (best_candidate_return.Kind == MemberKind.Void && best_candidate.IsConditionallyExcluded (ec)) + Methods = Excluded; + + return this; + } + + public override MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original) + { + var fe = left as FieldExpr; + if (fe != null) { + // + // Using method-group on struct fields makes the struct assigned. I am not sure + // why but that's what .net does + // + fe.Spec.MemberDefinition.SetIsAssigned (); + } + + simple_name = original; + return base.ResolveMemberAccess (ec, left, original); + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + type_arguments = ta; + } + + #region IBaseMembersProvider Members + + public virtual IList GetBaseMembers (TypeSpec baseType) + { + return baseType == null ? null : MemberCache.FindMembers (baseType, Methods [0].Name, false); + } + + public IParametersMember GetOverrideMemberParameters (MemberSpec member) + { + if (queried_type == member.DeclaringType) + return null; + + return MemberCache.FindMember (queried_type, new MemberFilter ((MethodSpec) member), + BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as IParametersMember; + } + + // + // Extension methods lookup after ordinary methods candidates failed to apply + // + public virtual MethodGroupExpr LookupExtensionMethod (ResolveContext rc) + { + if (InstanceExpression == null || InstanceExpression.eclass == ExprClass.Type) + return null; + + if (!IsExtensionMethodArgument (InstanceExpression)) + return null; + + int arity = type_arguments == null ? 0 : type_arguments.Count; + var methods = rc.LookupExtensionMethod (InstanceExpression.Type, Methods[0].Name, arity); + if (methods == null) + return null; + + var emg = new ExtensionMethodGroupExpr (methods, InstanceExpression, loc); + emg.SetTypeArguments (rc, type_arguments); + return emg; + } + + #endregion + } + + struct ConstructorInstanceQualifier : OverloadResolver.IInstanceQualifier + { + public ConstructorInstanceQualifier (TypeSpec type) + : this () + { + InstanceType = type; + } + + public TypeSpec InstanceType { get; private set; } + + public bool CheckProtectedMemberAccess (ResolveContext rc, MemberSpec member) + { + return MemberExpr.CheckProtectedMemberAccess (rc, member, InstanceType); + } + } + + public struct OverloadResolver + { + [Flags] + public enum Restrictions + { + None = 0, + DelegateInvoke = 1, + ProbingOnly = 1 << 1, + CovariantDelegate = 1 << 2, + NoBaseMembers = 1 << 3, + BaseMembersIncluded = 1 << 4, + GetEnumeratorLookup = 1 << 5 + } + + public interface IBaseMembersProvider + { + IList GetBaseMembers (TypeSpec baseType); + IParametersMember GetOverrideMemberParameters (MemberSpec member); + MethodGroupExpr LookupExtensionMethod (ResolveContext rc); + } + + public interface IErrorHandler + { + bool AmbiguousCandidates (ResolveContext rc, MemberSpec best, MemberSpec ambiguous); + bool ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument a, int index); + bool NoArgumentMatch (ResolveContext rc, MemberSpec best); + bool TypeInferenceFailed (ResolveContext rc, MemberSpec best); + } + + public interface IInstanceQualifier + { + TypeSpec InstanceType { get; } + bool CheckProtectedMemberAccess (ResolveContext rc, MemberSpec member); + } + + sealed class NoBaseMembers : IBaseMembersProvider + { + public static readonly IBaseMembersProvider Instance = new NoBaseMembers (); + + public IList GetBaseMembers (TypeSpec baseType) + { + return null; + } + + public IParametersMember GetOverrideMemberParameters (MemberSpec member) + { + return null; + } + + public MethodGroupExpr LookupExtensionMethod (ResolveContext rc) + { + return null; + } + } + + struct AmbiguousCandidate + { + public readonly MemberSpec Member; + public readonly bool Expanded; + public readonly AParametersCollection Parameters; + + public AmbiguousCandidate (MemberSpec member, AParametersCollection parameters, bool expanded) + { + Member = member; + Parameters = parameters; + Expanded = expanded; + } + } + + Location loc; + IList members; + TypeArguments type_arguments; + IBaseMembersProvider base_provider; + IErrorHandler custom_errors; + IInstanceQualifier instance_qualifier; + Restrictions restrictions; + MethodGroupExpr best_candidate_extension_group; + TypeSpec best_candidate_return_type; + + SessionReportPrinter lambda_conv_msgs; + + public OverloadResolver (IList members, Restrictions restrictions, Location loc) + : this (members, null, restrictions, loc) + { + } + + public OverloadResolver (IList members, TypeArguments targs, Restrictions restrictions, Location loc) + : this () + { + if (members == null || members.Count == 0) + throw new ArgumentException ("empty members set"); + + this.members = members; + this.loc = loc; + type_arguments = targs; + this.restrictions = restrictions; + if (IsDelegateInvoke) + this.restrictions |= Restrictions.NoBaseMembers; + + base_provider = NoBaseMembers.Instance; + } + + #region Properties + + public IBaseMembersProvider BaseMembersProvider { + get { + return base_provider; + } + set { + base_provider = value; + } + } + + public bool BestCandidateIsDynamic { get; set; } + + // + // Best candidate was found in newly created MethodGroupExpr, used by extension methods + // + public MethodGroupExpr BestCandidateNewMethodGroup { + get { + return best_candidate_extension_group; + } + } + + // + // Return type can be different between best candidate and closest override + // + public TypeSpec BestCandidateReturnType { + get { + return best_candidate_return_type; + } + } + + public IErrorHandler CustomErrors { + get { + return custom_errors; + } + set { + custom_errors = value; + } + } + + TypeSpec DelegateType { + get { + if ((restrictions & Restrictions.DelegateInvoke) == 0) + throw new InternalErrorException ("Not running in delegate mode", loc); + + return members [0].DeclaringType; + } + } + + public IInstanceQualifier InstanceQualifier { + get { + return instance_qualifier; + } + set { + instance_qualifier = value; + } + } + + bool IsProbingOnly { + get { + return (restrictions & Restrictions.ProbingOnly) != 0; + } + } + + bool IsDelegateInvoke { + get { + return (restrictions & Restrictions.DelegateInvoke) != 0; + } + } + + #endregion + + // + // 7.4.3.3 Better conversion from expression + // Returns : 1 if a->p is better, + // 2 if a->q is better, + // 0 if neither is better + // + static int BetterExpressionConversion (ResolveContext ec, Argument a, TypeSpec p, TypeSpec q) + { + TypeSpec argument_type = a.Type; + + // + // If argument is an anonymous function + // + if (argument_type == InternalType.AnonymousMethod && ec.Module.Compiler.Settings.Version > LanguageVersion.ISO_2) { + // + // p and q are delegate types or expression tree types + // + if (p.IsExpressionTreeType || q.IsExpressionTreeType) { + if (q.MemberDefinition != p.MemberDefinition) { + return 0; + } + + // + // Uwrap delegate from Expression + // + q = TypeManager.GetTypeArguments (q)[0]; + p = TypeManager.GetTypeArguments (p)[0]; + } + + var p_m = Delegate.GetInvokeMethod (p); + var q_m = Delegate.GetInvokeMethod (q); + + // + // With identical parameter lists + // + if (!TypeSpecComparer.Equals (p_m.Parameters.Types, q_m.Parameters.Types)) + return 0; + + p = p_m.ReturnType; + var orig_q = q; + q = q_m.ReturnType; + + // + // if p is void returning, and q has a return type Y, then C2 is the better conversion. + // + if (p.Kind == MemberKind.Void) { + return q.Kind != MemberKind.Void ? 2 : 0; + } + + // + // if p has a return type Y, and q is void returning, then C1 is the better conversion. + // + if (q.Kind == MemberKind.Void) { + return p.Kind != MemberKind.Void ? 1: 0; + } + + var am = (AnonymousMethodExpression) a.Expr; + + // + // When anonymous method is an asynchronous, and P has a return type Task, and Q has a return type Task + // better conversion is performed between underlying types Y1 and Y2 + // + if (p.IsGenericTask || q.IsGenericTask) { + if (am.Block.IsAsync && p.IsGenericTask && q.IsGenericTask) { + q = q.TypeArguments[0]; + p = p.TypeArguments[0]; + } + } + + if (q != p) { + // + // An inferred return type X exists for E in the context of that parameter list, and + // the conversion from X to Y1 is better than the conversion from X to Y2 + // + argument_type = am.InferReturnType (ec, null, orig_q); + if (argument_type == null) { + // TODO: Can this be hit? + return 1; + } + + if (argument_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + argument_type = ec.BuiltinTypes.Object; + } + } + + if (argument_type == p) + return 1; + + if (argument_type == q) + return 2; + + // + // The parameters are identicial and return type is not void, use better type conversion + // on return type to determine better one + // + return BetterTypeConversion (ec, p, q); + } + + // + // 7.4.3.4 Better conversion from type + // + public static int BetterTypeConversion (ResolveContext ec, TypeSpec p, TypeSpec q) + { + if (p == null || q == null) + throw new InternalErrorException ("BetterTypeConversion got a null conversion"); + + switch (p.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + if (q.BuiltinType == BuiltinTypeSpec.Type.UInt || q.BuiltinType == BuiltinTypeSpec.Type.ULong) + return 1; + break; + case BuiltinTypeSpec.Type.Long: + if (q.BuiltinType == BuiltinTypeSpec.Type.ULong) + return 1; + break; + case BuiltinTypeSpec.Type.SByte: + switch (q.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + return 1; + } + break; + case BuiltinTypeSpec.Type.Short: + switch (q.BuiltinType) { + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + return 1; + } + break; + case BuiltinTypeSpec.Type.Dynamic: + // Dynamic is never better + return 2; + } + + switch (q.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + if (p.BuiltinType == BuiltinTypeSpec.Type.UInt || p.BuiltinType == BuiltinTypeSpec.Type.ULong) + return 2; + break; + case BuiltinTypeSpec.Type.Long: + if (p.BuiltinType == BuiltinTypeSpec.Type.ULong) + return 2; + break; + case BuiltinTypeSpec.Type.SByte: + switch (p.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + return 2; + } + break; + case BuiltinTypeSpec.Type.Short: + switch (p.BuiltinType) { + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + return 2; + } + break; + case BuiltinTypeSpec.Type.Dynamic: + // Dynamic is never better + return 1; + } + + // FIXME: handle lifted operators + + // TODO: this is expensive + Expression p_tmp = new EmptyExpression (p); + Expression q_tmp = new EmptyExpression (q); + + bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q); + bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p); + + if (p_to_q && !q_to_p) + return 1; + + if (q_to_p && !p_to_q) + return 2; + + return 0; + } + + /// + /// Determines "Better function" between candidate + /// and the current best match + /// + /// + /// Returns a boolean indicating : + /// false if candidate ain't better + /// true if candidate is better than the current best match + /// + static bool BetterFunction (ResolveContext ec, Arguments args, MemberSpec candidate, AParametersCollection cparam, bool candidate_params, + MemberSpec best, AParametersCollection bparam, bool best_params) + { + AParametersCollection candidate_pd = ((IParametersMember) candidate).Parameters; + AParametersCollection best_pd = ((IParametersMember) best).Parameters; + + bool better_at_least_one = false; + bool are_equivalent = true; + int args_count = args == null ? 0 : args.Count; + int j = 0; + Argument a = null; + TypeSpec ct, bt; + for (int c_idx = 0, b_idx = 0; j < args_count; ++j, ++c_idx, ++b_idx) { + a = args[j]; + + // Default arguments are ignored for better decision + if (a.IsDefaultArgument) + break; + + // + // When comparing named argument the parameter type index has to be looked up + // in original parameter set (override version for virtual members) + // + NamedArgument na = a as NamedArgument; + if (na != null) { + int idx = cparam.GetParameterIndexByName (na.Name); + ct = candidate_pd.Types[idx]; + if (candidate_params && candidate_pd.FixedParameters[idx].ModFlags == Parameter.Modifier.PARAMS) + ct = TypeManager.GetElementType (ct); + + idx = bparam.GetParameterIndexByName (na.Name); + bt = best_pd.Types[idx]; + if (best_params && best_pd.FixedParameters[idx].ModFlags == Parameter.Modifier.PARAMS) + bt = TypeManager.GetElementType (bt); + } else { + ct = candidate_pd.Types[c_idx]; + bt = best_pd.Types[b_idx]; + + if (candidate_params && candidate_pd.FixedParameters[c_idx].ModFlags == Parameter.Modifier.PARAMS) { + ct = TypeManager.GetElementType (ct); + --c_idx; + } + + if (best_params && best_pd.FixedParameters[b_idx].ModFlags == Parameter.Modifier.PARAMS) { + bt = TypeManager.GetElementType (bt); + --b_idx; + } + } + + if (TypeSpecComparer.IsEqual (ct, bt)) + continue; + + are_equivalent = false; + int result = BetterExpressionConversion (ec, a, ct, bt); + + // for each argument, the conversion to 'ct' should be no worse than + // the conversion to 'bt'. + if (result == 2) + return false; + + // for at least one argument, the conversion to 'ct' should be better than + // the conversion to 'bt'. + if (result != 0) + better_at_least_one = true; + } + + if (better_at_least_one) + return true; + + // + // Tie-breaking rules are applied only for equivalent parameter types + // + if (!are_equivalent) + return false; + + // + // If candidate is applicable in its normal form and best has a params array and is applicable + // only in its expanded form, then candidate is better + // + if (candidate_params != best_params) + return !candidate_params; + + // + // We have not reached end of parameters list due to params or used default parameters + // + while (j < candidate_pd.Count && j < best_pd.Count) { + var cand_param = candidate_pd.FixedParameters [j]; + var best_param = best_pd.FixedParameters [j]; + + if (candidate_pd.Count == best_pd.Count) { + // + // LAMESPEC: + // + // void Foo (int i = 0) is better than void Foo (params int[]) for Foo () + // void Foo (string[] s, string value = null) is better than Foo (string s, params string[]) for Foo (null) or Foo () + // + if (cand_param.HasDefaultValue != best_param.HasDefaultValue) + return cand_param.HasDefaultValue; + + if (cand_param.HasDefaultValue) { + ++j; + continue; + } + } else { + // + // Neither is better when not all arguments are provided + // + // void Foo (string s, int i = 0) <-> Foo (string s, int i = 0, int i2 = 0) + // void Foo (string s, int i = 0) <-> Foo (string s, byte i = 0) + // void Foo (string s, params int[]) <-> Foo (string s, params byte[]) + // + if (cand_param.HasDefaultValue && best_param.HasDefaultValue) + return false; + } + + break; + } + + if (candidate_pd.Count != best_pd.Count) + return candidate_pd.Count < best_pd.Count; + + // + // One is a non-generic method and second is a generic method, then non-generic is better + // + if (best.IsGeneric != candidate.IsGeneric) + return best.IsGeneric; + + // + // Both methods have the same number of parameters, and the parameters have equal types + // Pick the "more specific" signature using rules over original (non-inflated) types + // + var candidate_def_pd = ((IParametersMember) candidate.MemberDefinition).Parameters; + var best_def_pd = ((IParametersMember) best.MemberDefinition).Parameters; + + bool specific_at_least_once = false; + for (j = 0; j < args_count; ++j) { + NamedArgument na = args_count == 0 ? null : args [j] as NamedArgument; + if (na != null) { + ct = candidate_def_pd.Types[cparam.GetParameterIndexByName (na.Name)]; + bt = best_def_pd.Types[bparam.GetParameterIndexByName (na.Name)]; + } else { + ct = candidate_def_pd.Types[j]; + bt = best_def_pd.Types[j]; + } + + if (ct == bt) + continue; + TypeSpec specific = MoreSpecific (ct, bt); + if (specific == bt) + return false; + if (specific == ct) + specific_at_least_once = true; + } + + if (specific_at_least_once) + return true; + + return false; + } + + static bool CheckInflatedArguments (MethodSpec ms) + { + if (!TypeParameterSpec.HasAnyTypeParameterTypeConstrained (ms.GenericDefinition)) + return true; + + // Setup constraint checker for probing only + ConstraintChecker cc = new ConstraintChecker (null); + + var mp = ms.Parameters.Types; + for (int i = 0; i < mp.Length; ++i) { + var type = mp[i] as InflatedTypeSpec; + if (type == null) + continue; + + var targs = type.TypeArguments; + if (targs.Length == 0) + continue; + + // TODO: Checking inflated MVAR arguments should be enough + if (!cc.CheckAll (type.GetDefinition (), targs, type.Constraints, Location.Null)) + return false; + } + + return true; + } + + public static void Error_ConstructorMismatch (ResolveContext rc, TypeSpec type, int argCount, Location loc) + { + rc.Report.Error (1729, loc, + "The type `{0}' does not contain a constructor that takes `{1}' arguments", + type.GetSignatureForError (), argCount.ToString ()); + } + + // + // Determines if the candidate method is applicable to the given set of arguments + // There could be two different set of parameters for same candidate where one + // is the closest override for default values and named arguments checks and second + // one being the virtual base for the parameter types and modifiers. + // + // A return value rates candidate method compatibility, + // 0 = the best, int.MaxValue = the worst + // -1 = fatal error + // + int IsApplicable (ResolveContext ec, ref Arguments arguments, int arg_count, ref MemberSpec candidate, IParametersMember pm, ref bool params_expanded_form, ref bool dynamicArgument, ref TypeSpec returnType, bool errorMode) + { + // Parameters of most-derived type used mainly for named and optional parameters + var pd = pm.Parameters; + + // Used for params modifier only, that's legacy of C# 1.0 which uses base type for + // params modifier instead of most-derived type + var cpd = ((IParametersMember) candidate).Parameters; + int param_count = pd.Count; + int optional_count = 0; + int score; + Arguments orig_args = arguments; + + if (arg_count != param_count) { + // + // No arguments expansion when doing exact match for delegates + // + if ((restrictions & Restrictions.CovariantDelegate) == 0) { + for (int i = 0; i < pd.Count; ++i) { + if (pd.FixedParameters[i].HasDefaultValue) { + optional_count = pd.Count - i; + break; + } + } + } + + if (optional_count != 0) { + // Readjust expected number when params used + if (cpd.HasParams) { + optional_count--; + if (arg_count < param_count) + param_count--; + } else if (arg_count > param_count) { + int args_gap = System.Math.Abs (arg_count - param_count); + return int.MaxValue - 10000 + args_gap; + } else if (arg_count < param_count - optional_count) { + int args_gap = System.Math.Abs (param_count - optional_count - arg_count); + return int.MaxValue - 10000 + args_gap; + } + } else if (arg_count != param_count) { + int args_gap = System.Math.Abs (arg_count - param_count); + if (!cpd.HasParams) + return int.MaxValue - 10000 + args_gap; + if (arg_count < param_count - 1) + return int.MaxValue - 10000 + args_gap; + } + + // Resize to fit optional arguments + if (optional_count != 0) { + if (arguments == null) { + arguments = new Arguments (optional_count); + } else { + // Have to create a new container, so the next run can do same + var resized = new Arguments (param_count); + resized.AddRange (arguments); + arguments = resized; + } + + for (int i = arg_count; i < param_count; ++i) + arguments.Add (null); + } + } + + if (arg_count > 0) { + // + // Shuffle named arguments to the right positions if there are any + // + if (arguments[arg_count - 1] is NamedArgument) { + arg_count = arguments.Count; + + for (int i = 0; i < arg_count; ++i) { + bool arg_moved = false; + while (true) { + NamedArgument na = arguments[i] as NamedArgument; + if (na == null) + break; + + int index = pd.GetParameterIndexByName (na.Name); + + // Named parameter not found + if (index < 0) + return (i + 1) * 3; + + // already reordered + if (index == i) + break; + + Argument temp; + if (index >= param_count) { + // When using parameters which should not be available to the user + if ((cpd.FixedParameters[index].ModFlags & Parameter.Modifier.PARAMS) == 0) + break; + + arguments.Add (null); + ++arg_count; + temp = null; + } else { + if (index == arg_count) + return (i + 1) * 3; + + temp = arguments [index]; + + // The slot has been taken by positional argument + if (temp != null && !(temp is NamedArgument)) + break; + } + + if (!arg_moved) { + arguments = arguments.MarkOrderedArgument (na); + arg_moved = true; + } + + if (arguments == orig_args) { + arguments = new Arguments (orig_args.Count); + arguments.AddRange (orig_args); + } + + arguments[index] = arguments[i]; + arguments[i] = temp; + + if (temp == null) + break; + } + } + } else { + arg_count = arguments.Count; + } + } else if (arguments != null) { + arg_count = arguments.Count; + } + + // + // Don't do any expensive checks when the candidate cannot succeed + // + if (arg_count != param_count && !cpd.HasParams) + return (param_count - arg_count) * 2 + 1; + + var dep = candidate.GetMissingDependencies (); + if (dep != null) { + ImportedTypeDefinition.Error_MissingDependency (ec, dep, loc); + return -1; + } + + // + // 1. Handle generic method using type arguments when specified or type inference + // + TypeSpec[] ptypes; + var ms = candidate as MethodSpec; + if (ms != null && ms.IsGeneric) { + if (type_arguments != null) { + var g_args_count = ms.Arity; + if (g_args_count != type_arguments.Count) + return int.MaxValue - 20000 + System.Math.Abs (type_arguments.Count - g_args_count); + + if (type_arguments.Arguments != null) + ms = ms.MakeGenericMethod (ec, type_arguments.Arguments); + } else { + // + // Deploy custom error reporting for infered anonymous expression or lambda methods. When + // probing lambda methods keep all errors reported in separate set and once we are done and no best + // candidate was found use the set to report more details about what was wrong with lambda body. + // The general idea is to distinguish between code errors and errors caused by + // trial-and-error type inference + // + if (lambda_conv_msgs == null) { + for (int i = 0; i < arg_count; i++) { + Argument a = arguments[i]; + if (a == null) + continue; + + var am = a.Expr as AnonymousMethodExpression; + if (am != null) { + if (lambda_conv_msgs == null) + lambda_conv_msgs = new SessionReportPrinter (); + + am.TypeInferenceReportPrinter = lambda_conv_msgs; + } + } + } + + var ti = new TypeInference (arguments); + TypeSpec[] i_args = ti.InferMethodArguments (ec, ms); + + if (i_args == null) + return ti.InferenceScore - 20000; + + // + // Clear any error messages when the result was success + // + if (lambda_conv_msgs != null) + lambda_conv_msgs.ClearSession (); + + if (i_args.Length != 0) { + if (!errorMode) { + foreach (var ta in i_args) { + if (!ta.IsAccessible (ec)) + return ti.InferenceScore - 10000; + } + } + + ms = ms.MakeGenericMethod (ec, i_args); + } + } + + // + // Type arguments constraints have to match for the method to be applicable + // + if (!CheckInflatedArguments (ms)) { + candidate = ms; + return int.MaxValue - 25000; + } + + // + // We have a generic return type and at same time the method is override which + // means we have to also inflate override return type in case the candidate is + // best candidate and override return type is different to base return type. + // + // virtual Foo with override Foo + // + if (candidate != pm) { + MethodSpec override_ms = (MethodSpec) pm; + var inflator = new TypeParameterInflator (ec, ms.DeclaringType, override_ms.GenericDefinition.TypeParameters, ms.TypeArguments); + returnType = inflator.Inflate (returnType); + } else { + returnType = ms.ReturnType; + } + + candidate = ms; + pd = ms.Parameters; + ptypes = pd.Types; + } else { + if (type_arguments != null) + return int.MaxValue - 15000; + + ptypes = cpd.Types; + } + + // + // 2. Each argument has to be implicitly convertible to method parameter + // + Parameter.Modifier p_mod = 0; + TypeSpec pt = null; + + for (int i = 0; i < arg_count; i++) { + Argument a = arguments[i]; + if (a == null) { + var fp = pd.FixedParameters[i]; + if (!fp.HasDefaultValue) { + arguments = orig_args; + return arg_count * 2 + 2; + } + + // + // Get the default value expression, we can use the same expression + // if the type matches + // + Expression e = fp.DefaultValue; + if (e != null) { + e = ResolveDefaultValueArgument (ec, ptypes[i], e, loc); + if (e == null) { + // Restore for possible error reporting + for (int ii = i; ii < arg_count; ++ii) + arguments.RemoveAt (i); + + return (arg_count - i) * 2 + 1; + } + } + + if ((fp.ModFlags & Parameter.Modifier.CallerMask) != 0) { + // + // LAMESPEC: Attributes can be mixed together with build-in priority + // + if ((fp.ModFlags & Parameter.Modifier.CallerLineNumber) != 0) { + e = new IntLiteral (ec.BuiltinTypes, loc.Row, loc); + } else if ((fp.ModFlags & Parameter.Modifier.CallerFilePath) != 0) { + e = new StringLiteral (ec.BuiltinTypes, loc.NameFullPath, loc); + } else if (ec.MemberContext.CurrentMemberDefinition != null) { + e = new StringLiteral (ec.BuiltinTypes, ec.MemberContext.CurrentMemberDefinition.GetCallerMemberName (), loc); + } + } + + arguments[i] = new Argument (e, Argument.AType.Default); + continue; + } + + if (p_mod != Parameter.Modifier.PARAMS) { + p_mod = (pd.FixedParameters[i].ModFlags & ~Parameter.Modifier.PARAMS) | (cpd.FixedParameters[i].ModFlags & Parameter.Modifier.PARAMS); + pt = ptypes [i]; + } else if (!params_expanded_form) { + params_expanded_form = true; + pt = ((ElementTypeSpec) pt).Element; + i -= 2; + continue; + } + + score = 1; + if (!params_expanded_form) { + if (a.IsExtensionType) { + // + // Indentity, implicit reference or boxing conversion must exist for the extension parameter + // + // LAMESPEC: or implicit type parameter conversion + // + var at = a.Type; + if (at == pt || TypeSpecComparer.IsEqual (at, pt) || + Convert.ImplicitReferenceConversionExists (at, pt, false) || + Convert.ImplicitBoxingConversion (null, at, pt) != null) { + score = 0; + continue; + } + } else { + score = IsArgumentCompatible (ec, a, p_mod, pt); + + if (score < 0) + dynamicArgument = true; + } + } + + // + // It can be applicable in expanded form (when not doing exact match like for delegates) + // + if (score != 0 && (p_mod & Parameter.Modifier.PARAMS) != 0 && (restrictions & Restrictions.CovariantDelegate) == 0) { + if (!params_expanded_form) { + pt = ((ElementTypeSpec) pt).Element; + } + + if (score > 0) + score = IsArgumentCompatible (ec, a, Parameter.Modifier.NONE, pt); + + if (score < 0) { + params_expanded_form = true; + dynamicArgument = true; + } else if (score == 0 || arg_count > pd.Count) { + params_expanded_form = true; + } + } + + if (score > 0) { + if (params_expanded_form) + ++score; + return (arg_count - i) * 2 + score; + } + } + + // + // Restore original arguments for dynamic binder to keep the intention of original source code + // + if (dynamicArgument) + arguments = orig_args; + + return 0; + } + + public static Expression ResolveDefaultValueArgument (ResolveContext ec, TypeSpec ptype, Expression e, Location loc) + { + if (e is Constant && e.Type == ptype) + return e; + + // + // LAMESPEC: No idea what the exact rules are for System.Reflection.Missing.Value instead of null + // + if (e == EmptyExpression.MissingValue && ptype.BuiltinType == BuiltinTypeSpec.Type.Object || ptype.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + e = new MemberAccess (new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Reflection", loc), "Missing", loc), "Value", loc); + } else if (e is Constant) { + // + // Handles int to int? conversions, DefaultParameterValue check + // + e = Convert.ImplicitConversionStandard (ec, e, ptype, loc); + if (e == null) + return null; + } else { + e = new DefaultValueExpression (new TypeExpression (ptype, loc), loc); + } + + return e.Resolve (ec); + } + + // + // Tests argument compatibility with the parameter + // The possible return values are + // 0 - success + // 1 - modifier mismatch + // 2 - type mismatch + // -1 - dynamic binding required + // + int IsArgumentCompatible (ResolveContext ec, Argument argument, Parameter.Modifier param_mod, TypeSpec parameter) + { + // + // Types have to be identical when ref or out modifer + // is used and argument is not of dynamic type + // + if (((argument.Modifier | param_mod) & Parameter.Modifier.RefOutMask) != 0) { + if (argument.Type != parameter) { + // + // Do full equality check after quick path + // + if (!TypeSpecComparer.IsEqual (argument.Type, parameter)) { + // + // Using dynamic for ref/out parameter can still succeed at runtime + // + if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (argument.Modifier & Parameter.Modifier.RefOutMask) == 0 && (restrictions & Restrictions.CovariantDelegate) == 0) + return -1; + + return 2; + } + } + + if ((argument.Modifier & Parameter.Modifier.RefOutMask) != (param_mod & Parameter.Modifier.RefOutMask)) { + // + // Using dynamic for ref/out parameter can still succeed at runtime + // + if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (argument.Modifier & Parameter.Modifier.RefOutMask) == 0 && (restrictions & Restrictions.CovariantDelegate) == 0) + return -1; + + return 1; + } + + } else { + if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (restrictions & Restrictions.CovariantDelegate) == 0) + return -1; + + // + // Use implicit conversion in all modes to return same candidates when the expression + // is used as argument or delegate conversion + // + if (!Convert.ImplicitConversionExists (ec, argument.Expr, parameter)) { + return parameter.IsDelegate && argument.Expr is AnonymousMethodExpression ? 2 : 3; + } + } + + return 0; + } + + static TypeSpec MoreSpecific (TypeSpec p, TypeSpec q) + { + if (TypeManager.IsGenericParameter (p) && !TypeManager.IsGenericParameter (q)) + return q; + if (!TypeManager.IsGenericParameter (p) && TypeManager.IsGenericParameter (q)) + return p; + + var ac_p = p as ArrayContainer; + if (ac_p != null) { + var ac_q = q as ArrayContainer; + if (ac_q == null) + return null; + + TypeSpec specific = MoreSpecific (ac_p.Element, ac_q.Element); + if (specific == ac_p.Element) + return p; + if (specific == ac_q.Element) + return q; + } else if (p.IsGeneric && q.IsGeneric) { + var pargs = TypeManager.GetTypeArguments (p); + var qargs = TypeManager.GetTypeArguments (q); + + bool p_specific_at_least_once = false; + bool q_specific_at_least_once = false; + + for (int i = 0; i < pargs.Length; i++) { + TypeSpec specific = MoreSpecific (pargs[i], qargs[i]); + if (specific == pargs[i]) + p_specific_at_least_once = true; + if (specific == qargs[i]) + q_specific_at_least_once = true; + } + + if (p_specific_at_least_once && !q_specific_at_least_once) + return p; + if (!p_specific_at_least_once && q_specific_at_least_once) + return q; + } + + return null; + } + + // + // Find the best method from candidate list + // + public T ResolveMember (ResolveContext rc, ref Arguments args) where T : MemberSpec, IParametersMember + { + List ambiguous_candidates = null; + + MemberSpec best_candidate; + Arguments best_candidate_args = null; + bool best_candidate_params = false; + bool best_candidate_dynamic = false; + int best_candidate_rate; + IParametersMember best_parameter_member = null; + + int args_count = args != null ? args.Count : 0; + + Arguments candidate_args = args; + bool error_mode = false; + MemberSpec invocable_member = null; + + while (true) { + best_candidate = null; + best_candidate_rate = int.MaxValue; + + var type_members = members; + do { + for (int i = 0; i < type_members.Count; ++i) { + var member = type_members[i]; + + // + // Methods in a base class are not candidates if any method in a derived + // class is applicable + // + if ((member.Modifiers & Modifiers.OVERRIDE) != 0) + continue; + + if (!error_mode) { + if (!member.IsAccessible (rc)) + continue; + + if (rc.IsRuntimeBinder && !member.DeclaringType.IsAccessible (rc)) + continue; + + if ((member.Modifiers & (Modifiers.PROTECTED | Modifiers.STATIC)) == Modifiers.PROTECTED && + instance_qualifier != null && !instance_qualifier.CheckProtectedMemberAccess (rc, member)) { + continue; + } + } + + IParametersMember pm = member as IParametersMember; + if (pm == null) { + // + // Will use it later to report ambiguity between best method and invocable member + // + if (Invocation.IsMemberInvocable (member)) + invocable_member = member; + + continue; + } + + // + // Overload resolution is looking for base member but using parameter names + // and default values from the closest member. That means to do expensive lookup + // for the closest override for virtual or abstract members + // + if ((member.Modifiers & (Modifiers.VIRTUAL | Modifiers.ABSTRACT)) != 0) { + var override_params = base_provider.GetOverrideMemberParameters (member); + if (override_params != null) + pm = override_params; + } + + // + // Check if the member candidate is applicable + // + bool params_expanded_form = false; + bool dynamic_argument = false; + TypeSpec rt = pm.MemberType; + int candidate_rate = IsApplicable (rc, ref candidate_args, args_count, ref member, pm, ref params_expanded_form, ref dynamic_argument, ref rt, error_mode); + + if (lambda_conv_msgs != null) + lambda_conv_msgs.EndSession (); + + // + // How does it score compare to others + // + if (candidate_rate < best_candidate_rate) { + + // Fatal error (missing dependency), cannot continue + if (candidate_rate < 0) + return null; + + if ((restrictions & Restrictions.GetEnumeratorLookup) != 0 && candidate_args.Count != 0) { + // Only parameterless methods are considered + } else { + best_candidate_rate = candidate_rate; + best_candidate = member; + best_candidate_args = candidate_args; + best_candidate_params = params_expanded_form; + best_candidate_dynamic = dynamic_argument; + best_parameter_member = pm; + best_candidate_return_type = rt; + } + } else if (candidate_rate == 0) { + // + // The member look is done per type for most operations but sometimes + // it's not possible like for binary operators overload because they + // are unioned between 2 sides + // + if ((restrictions & Restrictions.BaseMembersIncluded) != 0) { + if (TypeSpec.IsBaseClass (best_candidate.DeclaringType, member.DeclaringType, true)) + continue; + } + + bool is_better; + if (best_candidate.DeclaringType.IsInterface && member.DeclaringType.ImplementsInterface (best_candidate.DeclaringType, false)) { + // + // We pack all interface members into top level type which makes the overload resolution + // more complicated for interfaces. We compensate it by removing methods with same + // signature when building the cache hence this path should not really be hit often + // + // Example: + // interface IA { void Foo (int arg); } + // interface IB : IA { void Foo (params int[] args); } + // + // IB::Foo is the best overload when calling IB.Foo (1) + // + is_better = true; + if (ambiguous_candidates != null) { + foreach (var amb_cand in ambiguous_candidates) { + if (member.DeclaringType.ImplementsInterface (best_candidate.DeclaringType, false)) { + continue; + } + + is_better = false; + break; + } + + if (is_better) + ambiguous_candidates = null; + } + } else { + // Is the new candidate better + is_better = BetterFunction (rc, candidate_args, member, pm.Parameters, params_expanded_form, best_candidate, best_parameter_member.Parameters, best_candidate_params); + } + + if (is_better) { + best_candidate = member; + best_candidate_args = candidate_args; + best_candidate_params = params_expanded_form; + best_candidate_dynamic = dynamic_argument; + best_parameter_member = pm; + best_candidate_return_type = rt; + } else { + // It's not better but any other found later could be but we are not sure yet + if (ambiguous_candidates == null) + ambiguous_candidates = new List (); + + ambiguous_candidates.Add (new AmbiguousCandidate (member, pm.Parameters, params_expanded_form)); + } + } + + // Restore expanded arguments + candidate_args = args; + } + } while (best_candidate_rate != 0 && (type_members = base_provider.GetBaseMembers (type_members[0].DeclaringType.BaseType)) != null); + + // + // We've found exact match + // + if (best_candidate_rate == 0) + break; + + // + // Try extension methods lookup when no ordinary method match was found and provider enables it + // + if (!error_mode) { + var emg = base_provider.LookupExtensionMethod (rc); + if (emg != null) { + emg = emg.OverloadResolve (rc, ref args, null, restrictions); + if (emg != null) { + best_candidate_extension_group = emg; + return (T) (MemberSpec) emg.BestCandidate; + } + } + } + + // Don't run expensive error reporting mode for probing + if (IsProbingOnly) + return null; + + if (error_mode) + break; + + if (lambda_conv_msgs != null && !lambda_conv_msgs.IsEmpty) + break; + + lambda_conv_msgs = null; + error_mode = true; + } + + // + // No best member match found, report an error + // + if (best_candidate_rate != 0 || error_mode) { + ReportOverloadError (rc, best_candidate, best_parameter_member, best_candidate_args, best_candidate_params); + return null; + } + + if (best_candidate_dynamic) { + if (args[0].IsExtensionType) { + rc.Report.Error (1973, loc, + "Type `{0}' does not contain a member `{1}' and the best extension method overload `{2}' cannot be dynamically dispatched. Consider calling the method without the extension method syntax", + args [0].Type.GetSignatureForError (), best_candidate.Name, best_candidate.GetSignatureForError ()); + } + + // + // Check type constraints only when explicit type arguments are used + // + if (best_candidate.IsGeneric && type_arguments != null) { + MethodSpec bc = best_candidate as MethodSpec; + if (bc != null && TypeParameterSpec.HasAnyTypeParameterConstrained (bc.GenericDefinition)) { + ConstraintChecker cc = new ConstraintChecker (rc); + cc.CheckAll (bc.GetGenericMethodDefinition (), bc.TypeArguments, bc.Constraints, loc); + } + } + + BestCandidateIsDynamic = true; + return null; + } + + // + // These flags indicates we are running delegate probing conversion. No need to + // do more expensive checks + // + if ((restrictions & (Restrictions.ProbingOnly | Restrictions.CovariantDelegate)) == (Restrictions.CovariantDelegate | Restrictions.ProbingOnly)) + return (T) best_candidate; + + if (ambiguous_candidates != null) { + // + // Now check that there are no ambiguities i.e the selected method + // should be better than all the others + // + for (int ix = 0; ix < ambiguous_candidates.Count; ix++) { + var candidate = ambiguous_candidates [ix]; + + if (!BetterFunction (rc, best_candidate_args, best_candidate, best_parameter_member.Parameters, best_candidate_params, candidate.Member, candidate.Parameters, candidate.Expanded)) { + var ambiguous = candidate.Member; + if (custom_errors == null || !custom_errors.AmbiguousCandidates (rc, best_candidate, ambiguous)) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + rc.Report.SymbolRelatedToPreviousError (ambiguous); + rc.Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'", + best_candidate.GetSignatureForError (), ambiguous.GetSignatureForError ()); + } + + return (T) best_candidate; + } + } + } + + if (invocable_member != null && !IsProbingOnly) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + rc.Report.SymbolRelatedToPreviousError (invocable_member); + rc.Report.Warning (467, 2, loc, "Ambiguity between method `{0}' and invocable non-method `{1}'. Using method group", + best_candidate.GetSignatureForError (), invocable_member.GetSignatureForError ()); + } + + // + // And now check if the arguments are all + // compatible, perform conversions if + // necessary etc. and return if everything is + // all right + // + if (!VerifyArguments (rc, ref best_candidate_args, best_candidate, best_parameter_member, best_candidate_params)) + return null; + + if (best_candidate == null) + return null; + + // + // Don't run possibly expensive checks in probing mode + // + if (!IsProbingOnly && !rc.IsInProbingMode) { + // + // Check ObsoleteAttribute on the best method + // + ObsoleteAttribute oa = best_candidate.GetAttributeObsolete (); + if (oa != null && !rc.IsObsolete) + AttributeTester.Report_ObsoleteMessage (oa, best_candidate.GetSignatureForError (), loc, rc.Report); + + best_candidate.MemberDefinition.SetIsUsed (); + } + + args = best_candidate_args; + return (T) best_candidate; + } + + public MethodSpec ResolveOperator (ResolveContext rc, ref Arguments args) + { + return ResolveMember (rc, ref args); + } + + void ReportArgumentMismatch (ResolveContext ec, int idx, MemberSpec method, + Argument a, AParametersCollection expected_par, TypeSpec paramType) + { + if (custom_errors != null && custom_errors.ArgumentMismatch (ec, method, a, idx)) + return; + + if (a.Type == InternalType.ErrorType) + return; + + if (a is CollectionElementInitializer.ElementInitializerArgument) { + ec.Report.SymbolRelatedToPreviousError (method); + if ((expected_par.FixedParameters[idx].ModFlags & Parameter.Modifier.RefOutMask) != 0) { + ec.Report.Error (1954, loc, "The best overloaded collection initalizer method `{0}' cannot have `ref' or `out' modifier", + TypeManager.CSharpSignature (method)); + return; + } + ec.Report.Error (1950, loc, "The best overloaded collection initalizer method `{0}' has some invalid arguments", + TypeManager.CSharpSignature (method)); + } else if (IsDelegateInvoke) { + ec.Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments", + DelegateType.GetSignatureForError ()); + } else { + ec.Report.SymbolRelatedToPreviousError (method); + ec.Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments", + method.GetSignatureForError ()); + } + + Parameter.Modifier mod = idx >= expected_par.Count ? 0 : expected_par.FixedParameters[idx].ModFlags; + + string index = (idx + 1).ToString (); + if (((mod & Parameter.Modifier.RefOutMask) ^ (a.Modifier & Parameter.Modifier.RefOutMask)) != 0) { + if ((mod & Parameter.Modifier.RefOutMask) == 0) + ec.Report.Error (1615, a.Expr.Location, "Argument `#{0}' does not require `{1}' modifier. Consider removing `{1}' modifier", + index, Parameter.GetModifierSignature (a.Modifier)); + else + ec.Report.Error (1620, a.Expr.Location, "Argument `#{0}' is missing `{1}' modifier", + index, Parameter.GetModifierSignature (mod)); + } else { + string p1 = a.GetSignatureForError (); + string p2 = paramType.GetSignatureForError (); + + if (p1 == p2) { + p1 = a.Type.GetSignatureForErrorIncludingAssemblyName (); + p2 = paramType.GetSignatureForErrorIncludingAssemblyName (); + } + + if ((mod & Parameter.Modifier.RefOutMask) != 0) { + p1 = Parameter.GetModifierSignature (a.Modifier) + " " + p1; + p2 = Parameter.GetModifierSignature (a.Modifier) + " " + p2; + } + + ec.Report.Error (1503, a.Expr.Location, + "Argument `#{0}' cannot convert `{1}' expression to type `{2}'", index, p1, p2); + } + } + + // + // We have failed to find exact match so we return error info about the closest match + // + void ReportOverloadError (ResolveContext rc, MemberSpec best_candidate, IParametersMember pm, Arguments args, bool params_expanded) + { + int ta_count = type_arguments == null ? 0 : type_arguments.Count; + int arg_count = args == null ? 0 : args.Count; + + if (ta_count != best_candidate.Arity && (ta_count > 0 || ((IParametersMember) best_candidate).Parameters.IsEmpty)) { + var mg = new MethodGroupExpr (new [] { best_candidate }, best_candidate.DeclaringType, loc); + mg.Error_TypeArgumentsCannotBeUsed (rc, best_candidate, loc); + return; + } + + if (lambda_conv_msgs != null && lambda_conv_msgs.Merge (rc.Report.Printer)) { + return; + } + + + if ((best_candidate.Modifiers & (Modifiers.PROTECTED | Modifiers.STATIC)) == Modifiers.PROTECTED && + InstanceQualifier != null && !InstanceQualifier.CheckProtectedMemberAccess (rc, best_candidate)) { + MemberExpr.Error_ProtectedMemberAccess (rc, best_candidate, InstanceQualifier.InstanceType, loc); + } + + // + // For candidates which match on parameters count report more details about incorrect arguments + // + if (pm != null) { + if (pm.Parameters.Count == arg_count || params_expanded || HasUnfilledParams (best_candidate, pm, args)) { + // Reject any inaccessible member + if (!best_candidate.IsAccessible (rc) || !best_candidate.DeclaringType.IsAccessible (rc)) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + Expression.ErrorIsInaccesible (rc, best_candidate.GetSignatureForError (), loc); + return; + } + + var ms = best_candidate as MethodSpec; + if (ms != null && ms.IsGeneric) { + bool constr_ok = true; + if (ms.TypeArguments != null) + constr_ok = new ConstraintChecker (rc.MemberContext).CheckAll (ms.GetGenericMethodDefinition (), ms.TypeArguments, ms.Constraints, loc); + + if (ta_count == 0 && ms.TypeArguments == null) { + if (custom_errors != null && custom_errors.TypeInferenceFailed (rc, best_candidate)) + return; + + if (constr_ok) { + rc.Report.Error (411, loc, + "The type arguments for method `{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly", + ms.GetGenericMethodDefinition ().GetSignatureForError ()); + } + + return; + } + } + + VerifyArguments (rc, ref args, best_candidate, pm, params_expanded); + return; + } + } + + // + // We failed to find any method with correct argument count, report best candidate + // + if (custom_errors != null && custom_errors.NoArgumentMatch (rc, best_candidate)) + return; + + if (best_candidate.Kind == MemberKind.Constructor) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + Error_ConstructorMismatch (rc, best_candidate.DeclaringType, arg_count, loc); + } else if (IsDelegateInvoke) { + rc.Report.SymbolRelatedToPreviousError (DelegateType); + rc.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments", + DelegateType.GetSignatureForError (), arg_count.ToString ()); + } else { + string name = best_candidate.Kind == MemberKind.Indexer ? "this" : best_candidate.Name; + rc.Report.SymbolRelatedToPreviousError (best_candidate); + rc.Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments", + name, arg_count.ToString ()); + } + } + + static bool HasUnfilledParams (MemberSpec best_candidate, IParametersMember pm, Arguments args) + { + var p = ((IParametersMember)best_candidate).Parameters; + if (!p.HasParams) + return false; + + string name = null; + for (int i = p.Count - 1; i != 0; --i) { + var fp = p.FixedParameters [i]; + if ((fp.ModFlags & Parameter.Modifier.PARAMS) == 0) + continue; + + name = fp.Name; + break; + } + + if (args == null) + return false; + + foreach (var arg in args) { + var na = arg as NamedArgument; + if (na == null) + continue; + + if (na.Name == name) { + name = null; + break; + } + } + + if (name == null) + return false; + + return args.Count + 1 == pm.Parameters.Count; + } + + bool VerifyArguments (ResolveContext ec, ref Arguments args, MemberSpec member, IParametersMember pm, bool chose_params_expanded) + { + var pd = pm.Parameters; + var cpd = ((IParametersMember) member).Parameters; + var ptypes = cpd.Types; + + Parameter.Modifier p_mod = 0; + TypeSpec pt = null; + int a_idx = 0, a_pos = 0; + Argument a = null; + ArrayInitializer params_initializers = null; + bool has_unsafe_arg = pm.MemberType.IsPointer; + int arg_count = args == null ? 0 : args.Count; + + for (; a_idx < arg_count; a_idx++, ++a_pos) { + a = args[a_idx]; + if (a == null) + continue; + + if (p_mod != Parameter.Modifier.PARAMS) { + p_mod = cpd.FixedParameters [a_idx].ModFlags; + pt = ptypes[a_idx]; + has_unsafe_arg |= pt.IsPointer; + + if (p_mod == Parameter.Modifier.PARAMS) { + if (chose_params_expanded) { + params_initializers = new ArrayInitializer (arg_count - a_idx, a.Expr.Location); + pt = TypeManager.GetElementType (pt); + } + } + } + + // + // Types have to be identical when ref or out modifer is used + // + if (((a.Modifier | p_mod) & Parameter.Modifier.RefOutMask) != 0) { + if ((a.Modifier & Parameter.Modifier.RefOutMask) != (p_mod & Parameter.Modifier.RefOutMask)) + break; + + if (a.Expr.Type == pt || TypeSpecComparer.IsEqual (a.Expr.Type, pt)) + continue; + + break; + } + + NamedArgument na = a as NamedArgument; + if (na != null) { + int name_index = pd.GetParameterIndexByName (na.Name); + if (name_index < 0 || name_index >= pd.Count) { + if (IsDelegateInvoke) { + ec.Report.SymbolRelatedToPreviousError (DelegateType); + ec.Report.Error (1746, na.Location, + "The delegate `{0}' does not contain a parameter named `{1}'", + DelegateType.GetSignatureForError (), na.Name); + } else { + ec.Report.SymbolRelatedToPreviousError (member); + ec.Report.Error (1739, na.Location, + "The best overloaded method match for `{0}' does not contain a parameter named `{1}'", + TypeManager.CSharpSignature (member), na.Name); + } + } else if (args[name_index] != a && args[name_index] != null) { + if (IsDelegateInvoke) + ec.Report.SymbolRelatedToPreviousError (DelegateType); + else + ec.Report.SymbolRelatedToPreviousError (member); + + ec.Report.Error (1744, na.Location, + "Named argument `{0}' cannot be used for a parameter which has positional argument specified", + na.Name); + } + } + + if (a.Expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + continue; + + if ((restrictions & Restrictions.CovariantDelegate) != 0 && !Delegate.IsTypeCovariant (ec, a.Expr.Type, pt)) { + custom_errors.NoArgumentMatch (ec, member); + return false; + } + + Expression conv; + if (a.IsExtensionType) { + if (a.Expr.Type == pt || TypeSpecComparer.IsEqual (a.Expr.Type, pt)) { + conv = a.Expr; + } else { + conv = Convert.ImplicitReferenceConversion (a.Expr, pt, false); + if (conv == null) + conv = Convert.ImplicitBoxingConversion (a.Expr, a.Expr.Type, pt); + } + } else { + conv = Convert.ImplicitConversion (ec, a.Expr, pt, loc); + } + + if (conv == null) + break; + + // + // Convert params arguments to an array initializer + // + if (params_initializers != null) { + // we choose to use 'a.Expr' rather than 'conv' so that + // we don't hide the kind of expression we have (esp. CompoundAssign.Helper) + params_initializers.Add (a.Expr); + args.RemoveAt (a_idx--); + --arg_count; + a.Expr = conv; + continue; + } + + // Update the argument with the implicit conversion + a.Expr = conv; + } + + if (a_idx != arg_count) { + ReportArgumentMismatch (ec, a_pos, member, a, pd, pt); + return false; + } + + // + // Fill not provided arguments required by params modifier + // + if (params_initializers == null && arg_count + 1 == pd.Count) { + if (args == null) + args = new Arguments (1); + + pt = ptypes[pd.Count - 1]; + pt = TypeManager.GetElementType (pt); + has_unsafe_arg |= pt.IsPointer; + params_initializers = new ArrayInitializer (0, loc); + } + + // + // Append an array argument with all params arguments + // + if (params_initializers != null) { + args.Add (new Argument ( + new ArrayCreation (new TypeExpression (pt, loc), params_initializers, loc).Resolve (ec))); + arg_count++; + } + + if (has_unsafe_arg && !ec.IsUnsafe) { + Expression.UnsafeError (ec, loc); + } + + // + // We could infer inaccesible type arguments + // + if (type_arguments == null && member.IsGeneric) { + var ms = (MethodSpec) member; + foreach (var ta in ms.TypeArguments) { + if (!ta.IsAccessible (ec)) { + ec.Report.SymbolRelatedToPreviousError (ta); + Expression.ErrorIsInaccesible (ec, member.GetSignatureForError (), loc); + break; + } + } + } + + return true; + } + } + + public class ConstantExpr : MemberExpr + { + readonly ConstSpec constant; + + public ConstantExpr (ConstSpec constant, Location loc) + { + this.constant = constant; + this.loc = loc; + } + + public override string Name { + get { throw new NotImplementedException (); } + } + + public override string KindName { + get { return "constant"; } + } + + public override bool IsInstance { + get { return !IsStatic; } + } + + public override bool IsStatic { + get { return true; } + } + + protected override TypeSpec DeclaringType { + get { return constant.DeclaringType; } + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext rc) + { + ResolveInstanceExpression (rc, null); + DoBestMemberChecks (rc, constant); + + var c = constant.GetConstant (rc); + + // Creates reference expression to the constant value + return Constant.CreateConstantFromValue (constant.MemberType, c.GetValue (), loc); + } + + public override void Emit (EmitContext ec) + { + throw new NotSupportedException (); + } + + public override string GetSignatureForError () + { + return constant.GetSignatureForError (); + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "constant", GetSignatureForError (), loc); + } + } + + // + // Fully resolved expression that references a Field + // + public class FieldExpr : MemberExpr, IDynamicAssign, IMemoryLocation, IVariableReference + { + protected FieldSpec spec; + VariableInfo variable_info; + + LocalTemporary temp; + bool prepared; + + protected FieldExpr (Location l) + { + loc = l; + } + + public FieldExpr (FieldSpec spec, Location loc) + { + this.spec = spec; + this.loc = loc; + + type = spec.MemberType; + } + + public FieldExpr (FieldBase fi, Location l) + : this (fi.Spec, l) + { + } + + #region Properties + + public override string Name { + get { + return spec.Name; + } + } + + public bool IsHoisted { + get { + IVariableReference hv = InstanceExpression as IVariableReference; + return hv != null && hv.IsHoisted; + } + } + + public override bool IsInstance { + get { + return !spec.IsStatic; + } + } + + public override bool IsStatic { + get { + return spec.IsStatic; + } + } + + public override string KindName { + get { return "field"; } + } + + public FieldSpec Spec { + get { + return spec; + } + } + + protected override TypeSpec DeclaringType { + get { + return spec.DeclaringType; + } + } + + public VariableInfo VariableInfo { + get { + return variable_info; + } + } + +#endregion + + public override string GetSignatureForError () + { + return spec.GetSignatureForError (); + } + + public bool IsMarshalByRefAccess (ResolveContext rc) + { + // Checks possible ldflda of field access expression + return !spec.IsStatic && TypeSpec.IsValueType (spec.MemberType) && !(InstanceExpression is This) && + rc.Module.PredefinedTypes.MarshalByRefObject.Define () && + TypeSpec.IsBaseClass (spec.DeclaringType, rc.Module.PredefinedTypes.MarshalByRefObject.TypeSpec, false); + } + + public void SetHasAddressTaken () + { + IVariableReference vr = InstanceExpression as IVariableReference; + if (vr != null) { + vr.SetHasAddressTaken (); + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + var t = (FieldExpr) target; + + if (InstanceExpression != null) + t.InstanceExpression = InstanceExpression.Clone (clonectx); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (ConditionalAccess) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + + return CreateExpressionTree (ec, true); + } + + public Expression CreateExpressionTree (ResolveContext ec, bool convertInstance) + { + Arguments args; + Expression instance; + + if (InstanceExpression == null) { + instance = new NullLiteral (loc); + } else if (convertInstance) { + instance = InstanceExpression.CreateExpressionTree (ec); + } else { + args = new Arguments (1); + args.Add (new Argument (InstanceExpression)); + instance = CreateExpressionFactoryCall (ec, "Constant", args); + } + + args = Arguments.CreateForExpressionTree (ec, null, + instance, + CreateTypeOfExpression ()); + + return CreateExpressionFactoryCall (ec, "Field", args); + } + + public Expression CreateTypeOfExpression () + { + return new TypeOfField (spec, loc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + spec.MemberDefinition.SetIsUsed (); + + return DoResolve (ec, null); + } + + Expression DoResolve (ResolveContext ec, Expression rhs) + { + bool lvalue_instance = rhs != null && IsInstance && spec.DeclaringType.IsStruct; + + if (rhs != this) { + ResolveConditionalAccessReceiver (ec); + + if (ResolveInstanceExpression (ec, rhs)) { + // Resolve the field's instance expression while flow analysis is turned + // off: when accessing a field "a.b", we must check whether the field + // "a.b" is initialized, not whether the whole struct "a" is initialized. + + if (lvalue_instance) { + bool out_access = rhs == EmptyExpression.OutAccess || rhs == EmptyExpression.LValueMemberOutAccess; + + Expression right_side = + out_access ? EmptyExpression.LValueMemberOutAccess : EmptyExpression.LValueMemberAccess; + + InstanceExpression = InstanceExpression.ResolveLValue (ec, right_side); + } else { + InstanceExpression = InstanceExpression.Resolve (ec, ResolveFlags.VariableOrValue); + } + + if (InstanceExpression == null) + return null; + } + + DoBestMemberChecks (ec, spec); + + if (conditional_access_receiver) + ec.With (ResolveContext.Options.ConditionalAccessReceiver, false); + } + + var fb = spec as FixedFieldSpec; + IVariableReference var = InstanceExpression as IVariableReference; + + if (fb != null) { + IFixedExpression fe = InstanceExpression as IFixedExpression; + if (!ec.HasSet (ResolveContext.Options.FixedInitializerScope) && (fe == null || !fe.IsFixed)) { + ec.Report.Error (1666, loc, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement"); + } + + if (InstanceExpression.eclass != ExprClass.Variable) { + ec.Report.SymbolRelatedToPreviousError (spec); + ec.Report.Error (1708, loc, "`{0}': Fixed size buffers can only be accessed through locals or fields", + TypeManager.GetFullNameSignature (spec)); + } else if (var != null && var.IsHoisted) { + AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, var, loc); + } + + return new FixedBufferPtr (this, fb.ElementType, loc).Resolve (ec); + } + + // + // Set flow-analysis variable info for struct member access. It will be check later + // for precise error reporting + // + if (var != null && var.VariableInfo != null && InstanceExpression.Type.IsStruct) { + variable_info = var.VariableInfo.GetStructFieldInfo (Name); + } + + if (ConditionalAccess) { + if (conditional_access_receiver) + type = LiftMemberType (ec, type); + + if (InstanceExpression.IsNull) + return Constant.CreateConstantFromValue (type, null, loc); + } + + eclass = ExprClass.Variable; + return this; + } + + public void SetFieldAssigned (FlowAnalysisContext fc) + { + if (!IsInstance) + return; + + bool lvalue_instance = spec.DeclaringType.IsStruct; + if (lvalue_instance) { + var var = InstanceExpression as IVariableReference; + if (var != null && var.VariableInfo != null) { + fc.SetStructFieldAssigned (var.VariableInfo, Name); + } + } + + var fe = InstanceExpression as FieldExpr; + if (fe != null) { + Expression instance; + + do { + instance = fe.InstanceExpression; + var fe_instance = instance as FieldExpr; + if ((fe_instance != null && !fe_instance.IsStatic) || instance is LocalVariableReference) { + if (TypeSpec.IsReferenceType (fe.Type) && instance.Type.IsStruct) { + var var = InstanceExpression as IVariableReference; + if (var != null && var.VariableInfo == null) { + var var_inst = instance as IVariableReference; + if (var_inst == null || (var_inst.VariableInfo != null && !fc.IsDefinitelyAssigned (var_inst.VariableInfo))) + fc.Report.Warning (1060, 1, fe.loc, "Use of possibly unassigned field `{0}'", fe.Name); + } + } + + if (fe_instance != null) { + fe = fe_instance; + continue; + } + } + + break; + } while (true); + + if (instance != null && TypeSpec.IsReferenceType (instance.Type)) + instance.FlowAnalysis (fc); + } else { + if (TypeSpec.IsReferenceType (InstanceExpression.Type)) + InstanceExpression.FlowAnalysis (fc); + } + } + + Expression Error_AssignToReadonly (ResolveContext rc, Expression right_side) + { + // The return value is always null. Returning a value simplifies calling code. + + if (right_side == EmptyExpression.OutAccess) { + if (IsStatic) { + rc.Report.Error (199, loc, "A static readonly field `{0}' cannot be passed ref or out (except in a static constructor)", + GetSignatureForError ()); + } else { + rc.Report.Error (192, loc, "A readonly field `{0}' cannot be passed ref or out (except in a constructor)", + GetSignatureForError ()); + } + + return null; + } + + if (right_side == EmptyExpression.LValueMemberAccess) { + // Already reported as CS1648/CS1650 + return null; + } + + if (right_side == EmptyExpression.LValueMemberOutAccess) { + if (IsStatic) { + rc.Report.Error (1651, loc, "Fields of static readonly field `{0}' cannot be passed ref or out (except in a static constructor)", + GetSignatureForError ()); + } else { + rc.Report.Error (1649, loc, "Members of readonly field `{0}' cannot be passed ref or out (except in a constructor)", + GetSignatureForError ()); + } + return null; + } + + if (IsStatic) { + rc.Report.Error (198, loc, "A static readonly field `{0}' cannot be assigned to (except in a static constructor or a variable initializer)", + GetSignatureForError ()); + } else { + rc.Report.Error (191, loc, "A readonly field `{0}' cannot be assigned to (except in a constructor or a variable initializer)", + GetSignatureForError ()); + } + + return null; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (ConditionalAccess) + throw new NotSupportedException ("null propagating operator assignment"); + + if (spec is FixedFieldSpec) { + // It could be much better error message but we want to be error compatible + Error_ValueAssignment (ec, right_side); + } + + Expression e = DoResolve (ec, right_side); + + if (e == null) + return null; + + spec.MemberDefinition.SetIsAssigned (); + + if ((right_side == EmptyExpression.UnaryAddress || right_side == EmptyExpression.OutAccess) && + (spec.Modifiers & Modifiers.VOLATILE) != 0) { + ec.Report.Warning (420, 1, loc, + "`{0}': A volatile field references will not be treated as volatile", + spec.GetSignatureForError ()); + } + + if (spec.IsReadOnly) { + // InitOnly fields can only be assigned in constructors or initializers + if (!ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.ConstructorScope)) + return Error_AssignToReadonly (ec, right_side); + + if (ec.HasSet (ResolveContext.Options.ConstructorScope)) { + + // InitOnly fields cannot be assigned-to in a different constructor from their declaring type + if (ec.CurrentMemberDefinition.Parent.PartialContainer.Definition != spec.DeclaringType.GetDefinition ()) + return Error_AssignToReadonly (ec, right_side); + // static InitOnly fields cannot be assigned-to in an instance constructor + if (IsStatic && !ec.IsStatic) + return Error_AssignToReadonly (ec, right_side); + // instance constructors can't modify InitOnly fields of other instances of the same type + if (!IsStatic && !(InstanceExpression is This)) + return Error_AssignToReadonly (ec, right_side); + } + } + + if (right_side == EmptyExpression.OutAccess && IsMarshalByRefAccess (ec)) { + ec.Report.SymbolRelatedToPreviousError (spec.DeclaringType); + ec.Report.Warning (197, 1, loc, + "Passing `{0}' as ref or out or taking its address may cause a runtime exception because it is a field of a marshal-by-reference class", + GetSignatureForError ()); + } + + eclass = ExprClass.Variable; + return this; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + var var = InstanceExpression as IVariableReference; + if (var != null) { + var vi = var.VariableInfo; + if (vi != null && !fc.IsStructFieldDefinitelyAssigned (vi, Name)) { + fc.Report.Error (170, loc, "Use of possibly unassigned field `{0}'", Name); + return; + } + + if (TypeSpec.IsValueType (InstanceExpression.Type) && InstanceExpression is VariableReference) + return; + } + + base.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + public override int GetHashCode () + { + return spec.GetHashCode (); + } + + public bool IsFixed { + get { + // + // A variable of the form V.I is fixed when V is a fixed variable of a struct type + // + IVariableReference variable = InstanceExpression as IVariableReference; + if (variable != null) + return InstanceExpression.Type.IsStruct && variable.IsFixed; + + IFixedExpression fe = InstanceExpression as IFixedExpression; + return fe != null && fe.IsFixed; + } + } + + public override bool Equals (object obj) + { + FieldExpr fe = obj as FieldExpr; + if (fe == null) + return false; + + if (spec != fe.spec) + return false; + + if (InstanceExpression == null || fe.InstanceExpression == null) + return true; + + return InstanceExpression.Equals (fe.InstanceExpression); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + bool is_volatile = (spec.Modifiers & Modifiers.VOLATILE) != 0; + + if (IsStatic){ + if (is_volatile) + ec.Emit (OpCodes.Volatile); + + ec.Emit (OpCodes.Ldsfld, spec); + } else { + if (!prepared) { + if (conditional_access_receiver) + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + + EmitInstance (ec, false); + } + + // Optimization for build-in types + if (type.IsStruct && type == ec.CurrentType && InstanceExpression.Type == type) { + ec.EmitLoadFromPtr (type); + } else { + var ff = spec as FixedFieldSpec; + if (ff != null) { + ec.Emit (OpCodes.Ldflda, spec); + ec.Emit (OpCodes.Ldflda, ff.Element); + } else { + if (is_volatile) + ec.Emit (OpCodes.Volatile); + + ec.Emit (OpCodes.Ldfld, spec); + } + } + + if (conditional_access_receiver) { + ec.CloseConditionalAccess (type.IsNullableType && type != spec.MemberType ? type : null); + } + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + if (!IsStatic) { + temp = new LocalTemporary (this.Type); + temp.Store (ec); + } + } + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + bool has_await_source = ec.HasSet (BuilderContext.Options.AsyncBody) && source.ContainsEmitWithAwait (); + if (isCompound && !(source is DynamicExpressionStatement) && !has_await_source) { + prepared = true; + } + + if (IsInstance) { + if (ConditionalAccess) + throw new NotImplementedException ("null operator assignment"); + + if (has_await_source) + source = source.EmitToField (ec); + + EmitInstance (ec, prepared); + } + + source.Emit (ec); + + if (leave_copy || ec.NotifyEvaluatorOnStore) { + ec.Emit (OpCodes.Dup); + if (!IsStatic) { + temp = new LocalTemporary (this.Type); + temp.Store (ec); + } + } + + if ((spec.Modifiers & Modifiers.VOLATILE) != 0) + ec.Emit (OpCodes.Volatile); + + spec.MemberDefinition.SetIsAssigned (); + + if (IsStatic) + ec.Emit (OpCodes.Stsfld, spec); + else + ec.Emit (OpCodes.Stfld, spec); + + if (ec.NotifyEvaluatorOnStore) { + if (!IsStatic) + throw new NotImplementedException ("instance field write"); + + if (leave_copy) + ec.Emit (OpCodes.Dup); + + ec.Module.Evaluator.EmitValueChangedCallback (ec, Name, type, loc); + } + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + temp = null; + } + } + + // + // Emits store to field with prepared values on stack + // + public void EmitAssignFromStack (EmitContext ec) + { + if (IsStatic) { + ec.Emit (OpCodes.Stsfld, spec); + } else { + ec.Emit (OpCodes.Stfld, spec); + } + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public override void EmitSideEffect (EmitContext ec) + { + bool is_volatile = (spec.Modifiers & Modifiers.VOLATILE) != 0; + + if (is_volatile) // || is_marshal_by_ref ()) + base.EmitSideEffect (ec); + } + + public virtual void AddressOf (EmitContext ec, AddressOp mode) + { + if ((mode & AddressOp.Store) != 0) + spec.MemberDefinition.SetIsAssigned (); + if ((mode & AddressOp.Load) != 0) + spec.MemberDefinition.SetIsUsed (); + + // + // Handle initonly fields specially: make a copy and then + // get the address of the copy. + // + bool need_copy; + if (spec.IsReadOnly){ + need_copy = true; + if (ec.HasSet (EmitContext.Options.ConstructorScope) && spec.DeclaringType == ec.CurrentType) { + if (IsStatic){ + if (ec.IsStatic) + need_copy = false; + } else + need_copy = false; + } + } else + need_copy = false; + + if (need_copy) { + Emit (ec); + var temp = ec.GetTemporaryLocal (type); + ec.Emit (OpCodes.Stloc, temp); + ec.Emit (OpCodes.Ldloca, temp); + return; + } + + + if (IsStatic){ + ec.Emit (OpCodes.Ldsflda, spec); + } else { + if (!prepared) + EmitInstance (ec, false); + ec.Emit (OpCodes.Ldflda, spec); + } + } + + public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { + return MakeExpression (ctx); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Field ( + IsStatic ? null : InstanceExpression.MakeExpression (ctx), + spec.GetMetaInfo ()); +#endif + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "field", GetSignatureForError (), loc); + } + } + + + // + // Expression that evaluates to a Property. + // + // This is not an LValue because we need to re-write the expression. We + // can not take data from the stack and store it. + // + sealed class PropertyExpr : PropertyOrIndexerExpr + { + Arguments arguments; + + public PropertyExpr (PropertySpec spec, Location l) + : base (l) + { + best_candidate = spec; + type = spec.MemberType; + } + + #region Properties + + protected override Arguments Arguments { + get { + return arguments; + } + set { + arguments = value; + } + } + + protected override TypeSpec DeclaringType { + get { + return best_candidate.DeclaringType; + } + } + + public override string Name { + get { + return best_candidate.Name; + } + } + + public override bool IsInstance { + get { + return !IsStatic; + } + } + + public override bool IsStatic { + get { + return best_candidate.IsStatic; + } + } + + public override string KindName { + get { return "property"; } + } + + public PropertySpec PropertyInfo { + get { + return best_candidate; + } + } + + #endregion + + public override MethodGroupExpr CanReduceLambda (AnonymousMethodBody body) + { + if (best_candidate == null || !(best_candidate.IsStatic || InstanceExpression is This)) + return null; + + var args_count = arguments == null ? 0 : arguments.Count; + if (args_count != body.Parameters.Count && args_count == 0) + return null; + + var mg = MethodGroupExpr.CreatePredefined (best_candidate.Get, DeclaringType, loc); + mg.InstanceExpression = InstanceExpression; + + return mg; + } + + public static PropertyExpr CreatePredefined (PropertySpec spec, Location loc) + { + return new PropertyExpr (spec, loc) { + Getter = spec.Get, + Setter = spec.Set + }; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (ConditionalAccess) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + + Arguments args; + if (IsSingleDimensionalArrayLength ()) { + args = new Arguments (1); + args.Add (new Argument (InstanceExpression.CreateExpressionTree (ec))); + return CreateExpressionFactoryCall (ec, "ArrayLength", args); + } + + args = new Arguments (2); + if (InstanceExpression == null) + args.Add (new Argument (new NullLiteral (loc))); + else + args.Add (new Argument (InstanceExpression.CreateExpressionTree (ec))); + args.Add (new Argument (new TypeOfMethod (Getter, loc))); + return CreateExpressionFactoryCall (ec, "Property", args); + } + + public Expression CreateSetterTypeOfExpression (ResolveContext rc) + { + DoResolveLValue (rc, null); + return new TypeOfMethod (Setter, loc); + } + + public override string GetSignatureForError () + { + return best_candidate.GetSignatureForError (); + } + + public override SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Property (InstanceExpression.MakeExpression (ctx), (MethodInfo) Setter.GetMetaInfo ()); +#endif + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Property (InstanceExpression.MakeExpression (ctx), (MethodInfo) Getter.GetMetaInfo ()); +#endif + } + + void Error_PropertyNotValid (ResolveContext ec) + { + ec.Report.SymbolRelatedToPreviousError (best_candidate); + ec.Report.Error (1546, loc, "Property or event `{0}' is not supported by the C# language", + GetSignatureForError ()); + } + + bool IsSingleDimensionalArrayLength () + { + if (best_candidate.DeclaringType.BuiltinType != BuiltinTypeSpec.Type.Array || !best_candidate.HasGet || Name != "Length") + return false; + + ArrayContainer ac = InstanceExpression.Type as ArrayContainer; + return ac != null && ac.Rank == 1; + } + + public override void Emit (EmitContext ec, bool leave_copy) + { + // + // Special case: length of single dimension array property is turned into ldlen + // + if (IsSingleDimensionalArrayLength ()) { + if (conditional_access_receiver) { + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + } + + EmitInstance (ec, false); + + ec.Emit (OpCodes.Ldlen); + ec.Emit (OpCodes.Conv_I4); + + if (conditional_access_receiver) { + ec.CloseConditionalAccess (type); + } + + return; + } + + base.Emit (ec, leave_copy); + } + + public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + Arguments args; + LocalTemporary await_source_arg = null; + + if (isCompound && !(source is DynamicExpressionStatement)) { + emitting_compound_assignment = true; + source.Emit (ec); + + if (has_await_arguments) { + await_source_arg = new LocalTemporary (Type); + await_source_arg.Store (ec); + + args = new Arguments (1); + args.Add (new Argument (await_source_arg)); + + if (leave_copy) { + temp = await_source_arg; + } + + has_await_arguments = false; + } else { + args = null; + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (this.Type); + temp.Store (ec); + } + } + } else { + args = arguments ?? new Arguments (1); + + if (leave_copy) { + source.Emit (ec); + temp = new LocalTemporary (this.Type); + temp.Store (ec); + args.Add (new Argument (temp)); + } else { + args.Add (new Argument (source)); + } + } + + emitting_compound_assignment = false; + + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpression; + if (args == null) + call.InstanceExpressionOnStack = true; + + if (ConditionalAccess) { + call.ConditionalAccess = true; + } + + if (leave_copy) + call.Emit (ec, Setter, args, loc); + else + call.EmitStatement (ec, Setter, args, loc); + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } + + if (await_source_arg != null) { + await_source_arg.Release (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + protected override Expression OverloadResolve (ResolveContext rc, Expression right_side) + { + eclass = ExprClass.PropertyAccess; + + if (best_candidate.IsNotCSharpCompatible) { + Error_PropertyNotValid (rc); + } + + ResolveInstanceExpression (rc, right_side); + + if ((best_candidate.Modifiers & (Modifiers.ABSTRACT | Modifiers.VIRTUAL)) != 0 && best_candidate.DeclaringType != InstanceExpression.Type) { + var filter = new MemberFilter (best_candidate.Name, 0, MemberKind.Property, null, null); + var p = MemberCache.FindMember (InstanceExpression.Type, filter, BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as PropertySpec; + if (p != null) { + type = p.MemberType; + } + } + + DoBestMemberChecks (rc, best_candidate); + + // Handling of com-imported properties with any number of default property parameters + if (best_candidate.HasGet && !best_candidate.Get.Parameters.IsEmpty) { + var p = best_candidate.Get.Parameters; + arguments = new Arguments (p.Count); + for (int i = 0; i < p.Count; ++i) { + arguments.Add (new Argument (OverloadResolver.ResolveDefaultValueArgument (rc, p.Types [i], p.FixedParameters [i].DefaultValue, loc))); + } + } else if (best_candidate.HasSet && best_candidate.Set.Parameters.Count > 1) { + var p = best_candidate.Set.Parameters; + arguments = new Arguments (p.Count - 1); + for (int i = 0; i < p.Count - 1; ++i) { + arguments.Add (new Argument (OverloadResolver.ResolveDefaultValueArgument (rc, p.Types [i], p.FixedParameters [i].DefaultValue, loc))); + } + } + + return this; + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "property", GetSignatureForError (), loc); + } + } + + abstract class PropertyOrIndexerExpr : MemberExpr, IDynamicAssign where T : PropertySpec + { + // getter and setter can be different for base calls + MethodSpec getter, setter; + protected T best_candidate; + + protected LocalTemporary temp; + protected bool emitting_compound_assignment; + protected bool has_await_arguments; + + protected PropertyOrIndexerExpr (Location l) + { + loc = l; + } + + #region Properties + + protected abstract Arguments Arguments { get; set; } + + public MethodSpec Getter { + get { + return getter; + } + set { + getter = value; + } + } + + public MethodSpec Setter { + get { + return setter; + } + set { + setter = value; + } + } + + #endregion + + protected override Expression DoResolve (ResolveContext ec) + { + if (eclass == ExprClass.Unresolved) { + ResolveConditionalAccessReceiver (ec); + + var expr = OverloadResolve (ec, null); + if (expr == null) + return null; + + if (expr != this) + return expr.Resolve (ec); + + if (conditional_access_receiver) { + type = LiftMemberType (ec, type); + ec.With (ResolveContext.Options.ConditionalAccessReceiver, false); + } + } + + if (!ResolveGetter (ec)) + return null; + + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (ConditionalAccess) + throw new NotSupportedException ("null propagating operator assignment"); + + if (right_side == EmptyExpression.OutAccess) { + // TODO: best_candidate can be null at this point + INamedBlockVariable variable = null; + if (best_candidate != null && ec.CurrentBlock.ParametersBlock.TopBlock.GetLocalName (best_candidate.Name, ec.CurrentBlock, ref variable) && variable is Linq.RangeVariable) { + ec.Report.Error (1939, loc, "A range variable `{0}' may not be passes as `ref' or `out' parameter", + best_candidate.Name); + } else { + right_side.DoResolveLValue (ec, this); + } + return null; + } + + if (eclass == ExprClass.Unresolved) { + var expr = OverloadResolve (ec, right_side); + if (expr == null) + return null; + + if (expr != this) + return expr.ResolveLValue (ec, right_side); + } else { + ResolveInstanceExpression (ec, right_side); + } + + if (!ResolveSetter (ec)) + return null; + + return this; + } + + void EmitConditionalAccess (EmitContext ec, ref CallEmitter call, MethodSpec method, Arguments arguments) + { + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + + call.Emit (ec, method, arguments, loc); + + ec.CloseConditionalAccess (method.ReturnType != type && type.IsNullableType ? type : null); + } + + // + // Implements the IAssignMethod interface for assignments + // + public virtual void Emit (EmitContext ec, bool leave_copy) + { + var call = new CallEmitter (); + call.ConditionalAccess = ConditionalAccess; + call.InstanceExpression = InstanceExpression; + if (has_await_arguments) + call.HasAwaitArguments = true; + else + call.DuplicateArguments = emitting_compound_assignment; + + if (conditional_access_receiver) + EmitConditionalAccess (ec, ref call, Getter, Arguments); + else + call.Emit (ec, Getter, Arguments, loc); + + if (call.HasAwaitArguments) { + InstanceExpression = call.InstanceExpression; + Arguments = call.EmittedArguments; + has_await_arguments = true; + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (Type); + temp.Store (ec); + } + } + + public abstract void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound); + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + protected override FieldExpr EmitToFieldSource (EmitContext ec) + { + has_await_arguments = true; + Emit (ec, false); + return null; + } + + public abstract SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source); + + protected abstract Expression OverloadResolve (ResolveContext rc, Expression right_side); + + bool ResolveGetter (ResolveContext rc) + { + if (!best_candidate.HasGet) { + if (InstanceExpression != EmptyExpression.Null) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + rc.Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor", + best_candidate.GetSignatureForError ()); + return false; + } + } else if (!best_candidate.Get.IsAccessible (rc) || !best_candidate.Get.DeclaringType.IsAccessible (rc)) { + if (best_candidate.HasDifferentAccessibility) { + rc.Report.SymbolRelatedToPreviousError (best_candidate.Get); + rc.Report.Error (271, loc, "The property or indexer `{0}' cannot be used in this context because the get accessor is inaccessible", + TypeManager.CSharpSignature (best_candidate)); + } else { + rc.Report.SymbolRelatedToPreviousError (best_candidate.Get); + ErrorIsInaccesible (rc, best_candidate.Get.GetSignatureForError (), loc); + } + } + + if (best_candidate.HasDifferentAccessibility) { + CheckProtectedMemberAccess (rc, best_candidate.Get); + } + + getter = CandidateToBaseOverride (rc, best_candidate.Get); + return true; + } + + bool ResolveSetter (ResolveContext rc) + { + if (!best_candidate.HasSet) { + rc.Report.Error (200, loc, "Property or indexer `{0}' cannot be assigned to (it is read-only)", + GetSignatureForError ()); + return false; + } + + if (!best_candidate.Set.IsAccessible (rc) || !best_candidate.Set.DeclaringType.IsAccessible (rc)) { + if (best_candidate.HasDifferentAccessibility) { + rc.Report.SymbolRelatedToPreviousError (best_candidate.Set); + rc.Report.Error (272, loc, "The property or indexer `{0}' cannot be used in this context because the set accessor is inaccessible", + GetSignatureForError ()); + } else { + rc.Report.SymbolRelatedToPreviousError (best_candidate.Set); + ErrorIsInaccesible (rc, best_candidate.GetSignatureForError (), loc); + } + } + + if (best_candidate.HasDifferentAccessibility) + CheckProtectedMemberAccess (rc, best_candidate.Set); + + setter = CandidateToBaseOverride (rc, best_candidate.Set); + return true; + } + } + + /// + /// Fully resolved expression that evaluates to an Event + /// + public class EventExpr : MemberExpr, IAssignMethod + { + readonly EventSpec spec; + MethodSpec op; + + public EventExpr (EventSpec spec, Location loc) + { + this.spec = spec; + this.loc = loc; + } + + #region Properties + + protected override TypeSpec DeclaringType { + get { + return spec.DeclaringType; + } + } + + public override string Name { + get { + return spec.Name; + } + } + + public override bool IsInstance { + get { + return !spec.IsStatic; + } + } + + public override bool IsStatic { + get { + return spec.IsStatic; + } + } + + public override string KindName { + get { return "event"; } + } + + public MethodSpec Operator { + get { + return op; + } + } + + #endregion + + public override MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original) + { + // + // If the event is local to this class and we are not lhs of +=/-= we transform ourselves into a FieldExpr + // + if (!ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) { + if (spec.BackingField != null && + (spec.DeclaringType == ec.CurrentType || TypeManager.IsNestedChildOf (ec.CurrentType, spec.DeclaringType.MemberDefinition))) { + + spec.MemberDefinition.SetIsUsed (); + + if (!ec.IsObsolete) { + ObsoleteAttribute oa = spec.GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, spec.GetSignatureForError (), loc, ec.Report); + } + + if ((spec.Modifiers & (Modifiers.ABSTRACT | Modifiers.EXTERN)) != 0) + Error_AssignmentEventOnly (ec); + + FieldExpr ml = new FieldExpr (spec.BackingField, loc); + + InstanceExpression = null; + + return ml.ResolveMemberAccess (ec, left, original); + } + } + + return base.ResolveMemberAccess (ec, left, original); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (right_side == EmptyExpression.EventAddition) { + op = spec.AccessorAdd; + } else if (right_side == EmptyExpression.EventSubtraction) { + op = spec.AccessorRemove; + } + + if (op == null) { + Error_AssignmentEventOnly (ec); + return null; + } + + op = CandidateToBaseOverride (ec, op); + return this; + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.EventAccess; + type = spec.MemberType; + + ResolveInstanceExpression (ec, null); + + if (!ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) { + Error_AssignmentEventOnly (ec); + } + + DoBestMemberChecks (ec, spec); + return this; + } + + public override void Emit (EmitContext ec) + { + throw new NotSupportedException (); + //Error_CannotAssign (); + } + + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + if (leave_copy || !isCompound) + throw new NotImplementedException ("EventExpr::EmitAssign"); + + Arguments args = new Arguments (1); + args.Add (new Argument (source)); + + // TODO: Wrong, needs receiver +// if (NullShortCircuit) { +// ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); +// } + + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpression; + call.ConditionalAccess = ConditionalAccess; + call.EmitStatement (ec, op, args, loc); + +// if (NullShortCircuit) +// ec.CloseConditionalAccess (null); + } + + #endregion + + void Error_AssignmentEventOnly (ResolveContext ec) + { + if (spec.DeclaringType == ec.CurrentType || TypeManager.IsNestedChildOf (ec.CurrentType, spec.DeclaringType.MemberDefinition)) { + ec.Report.Error (79, loc, + "The event `{0}' can only appear on the left hand side of `+=' or `-=' operator", + GetSignatureForError ()); + } else { + ec.Report.Error (70, loc, + "The event `{0}' can only appear on the left hand side of += or -= when used outside of the type `{1}'", + GetSignatureForError (), spec.DeclaringType.GetSignatureForError ()); + } + } + + protected override void Error_CannotCallAbstractBase (ResolveContext rc, string name) + { + name = name.Substring (0, name.LastIndexOf ('.')); + base.Error_CannotCallAbstractBase (rc, name); + } + + public override string GetSignatureForError () + { + return TypeManager.CSharpSignature (spec); + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "event", GetSignatureForError (), loc); + } + } + + public class TemporaryVariableReference : VariableReference + { + public class Declarator : Statement + { + TemporaryVariableReference variable; + + public Declarator (TemporaryVariableReference variable) + { + this.variable = variable; + loc = variable.loc; + } + + protected override void DoEmit (EmitContext ec) + { + variable.li.CreateBuilder (ec); + } + + public override void Emit (EmitContext ec) + { + // Don't create sequence point + DoEmit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // Nothing + } + } + + LocalVariable li; + + public TemporaryVariableReference (LocalVariable li, Location loc) + { + this.li = li; + this.type = li.Type; + this.loc = loc; + } + + public override bool IsLockedByStatement { + get { + return false; + } + set { + } + } + + public LocalVariable LocalInfo { + get { + return li; + } + } + + public static TemporaryVariableReference Create (TypeSpec type, Block block, Location loc) + { + var li = LocalVariable.CreateCompilerGenerated (type, block, loc); + return new TemporaryVariableReference (li, loc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Variable; + + // + // Don't capture temporary variables except when using + // state machine redirection and block yields + // + if (ec.CurrentAnonymousMethod is StateMachineInitializer && + (ec.CurrentBlock.Explicit.HasYield || ec.CurrentBlock.Explicit.HasAwait) && + ec.IsVariableCapturingRequired) { + AnonymousMethodStorey storey = li.Block.Explicit.CreateAnonymousMethodStorey (ec); + storey.CaptureLocalVariable (ec, li); + } + + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return Resolve (ec); + } + + public override void Emit (EmitContext ec) + { + li.CreateBuilder (ec); + + Emit (ec, false); + } + + public void EmitAssign (EmitContext ec, Expression source) + { + li.CreateBuilder (ec); + + EmitAssign (ec, source, false, false); + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + return li.HoistedVariant; + } + + public override bool IsFixed { + get { return true; } + } + + public override bool IsRef { + get { return false; } + } + + public override string Name { + get { throw new NotImplementedException (); } + } + + public override void SetHasAddressTaken () + { + throw new NotImplementedException (); + } + + protected override ILocalVariable Variable { + get { return li; } + } + + public override VariableInfo VariableInfo { + get { return null; } + } + } + + /// + /// Handles `var' contextual keyword; var becomes a keyword only + /// if no type called var exists in a variable scope + /// + class VarExpr : SimpleName + { + public VarExpr (Location loc) + : base ("var", loc) + { + } + + public bool InferType (ResolveContext ec, Expression right_side) + { + if (type != null) + throw new InternalErrorException ("An implicitly typed local variable could not be redefined"); + + type = right_side.Type; + if (type == InternalType.NullLiteral || type.Kind == MemberKind.Void || type == InternalType.AnonymousMethod || type == InternalType.MethodGroup) { + ec.Report.Error (815, loc, + "An implicitly typed local variable declaration cannot be initialized with `{0}'", + type.GetSignatureForError ()); + return false; + } + + eclass = ExprClass.Variable; + return true; + } + + protected override void Error_TypeOrNamespaceNotFound (IMemberContext ec) + { + if (ec.Module.Compiler.Settings.Version < LanguageVersion.V_3) + base.Error_TypeOrNamespaceNotFound (ec); + else + ec.Module.Compiler.Report.Error (825, loc, "The contextual keyword `var' may only appear within a local variable declaration"); + } + } + + public class InvalidStatementExpression : Statement + { + public Expression Expression { + get; + private set; + } + + public InvalidStatementExpression (Expression expr) + { + this.Expression = expr; + } + + public override void Emit (EmitContext ec) + { + // nothing + } + + protected override void DoEmit (EmitContext ec) + { + // nothing + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // nothing + } + + public override Mono.CSharp.Expression CreateExpressionTree (ResolveContext ec) + { + return null; + } + + public override object Accept (Mono.CSharp.StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + protected override bool DoFlowAnalysis(FlowAnalysisContext fc) + { + return false; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/enum.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/enum.cs new file mode 100644 index 000000000..96385f622 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/enum.cs @@ -0,0 +1,339 @@ +// +// enum.cs: Enum handling. +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2003 Novell, Inc (http://www.novell.com) +// Copyright 2011 Xamarin Inc +// + +using System; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +#else +using MetaType = System.Type; +using System.Reflection; +#endif + +namespace Mono.CSharp { + + public class EnumMember : Const + { + class EnumTypeExpr : TypeExpr + { + public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments) + { + type = ec.CurrentType; + eclass = ExprClass.Type; + return type; + } + } + + public EnumMember (Enum parent, MemberName name, Attributes attrs) + : base (parent, new EnumTypeExpr (), Modifiers.PUBLIC, name, attrs) + { + } + + static bool IsValidEnumType (TypeSpec t) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Char: + return true; + default: + return t.IsEnum; + } + } + + public override Constant ConvertInitializer (ResolveContext rc, Constant expr) + { + if (expr is EnumConstant) + expr = ((EnumConstant) expr).Child; + + var en = (Enum)Parent; + var underlying = en.UnderlyingType; + if (expr != null) { + expr = expr.ImplicitConversionRequired (rc, underlying); + if (expr != null && !IsValidEnumType (expr.Type)) { + en.Error_UnderlyingType (Location); + expr = null; + } + } + + if (expr == null) + expr = New.Constantify (underlying, Location); + + return new EnumConstant (expr, MemberType); + } + + public override bool Define () + { + if (!ResolveMemberType ()) + return false; + + const FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal; + FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), attr); + spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer); + + Parent.MemberCache.AddMember (spec); + return true; + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + } + + /// + /// Enumeration container + /// + public class Enum : TypeDefinition + { + // + // Implicit enum member initializer, used when no constant value is provided + // + sealed class ImplicitInitializer : Expression + { + readonly EnumMember prev; + readonly EnumMember current; + + public ImplicitInitializer (EnumMember current, EnumMember prev) + { + this.current = current; + this.prev = prev; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("Missing Resolve call"); + } + + protected override Expression DoResolve (ResolveContext rc) + { + // We are the first member + if (prev == null) { + return New.Constantify (current.Parent.Definition, Location); + } + + var c = ((ConstSpec) prev.Spec).GetConstant (rc) as EnumConstant; + try { + return c.Increment (); + } catch (OverflowException) { + rc.Report.Error (543, current.Location, + "The enumerator value `{0}' is outside the range of enumerator underlying type `{1}'", + current.GetSignatureForError (), ((Enum) current.Parent).UnderlyingType.GetSignatureForError ()); + + return New.Constantify (current.Parent.Definition, current.Location); + } + } + + public override void Emit (EmitContext ec) + { + throw new NotSupportedException ("Missing Resolve call"); + } + } + + public static readonly string UnderlyingValueField = "value__"; + + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE; + + readonly FullNamedExpression underlying_type_expr; + + public Enum (TypeContainer parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, name, attrs, MemberKind.Enum) + { + underlying_type_expr = type; + var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE; + ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod_flags, accmods, Location, Report); + spec = new EnumSpec (null, this, null, null, ModFlags); + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Enum; + } + } + + public FullNamedExpression BaseTypeExpression { + get { + return underlying_type_expr; + } + } + + protected override TypeAttributes TypeAttr { + get { + return base.TypeAttr | TypeAttributes.Class | TypeAttributes.Sealed; + } + } + + public TypeSpec UnderlyingType { + get { + return ((EnumSpec) spec).UnderlyingType; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public void AddEnumMember (EnumMember em) + { + if (em.Name == UnderlyingValueField) { + Report.Error (76, em.Location, "An item in an enumeration cannot have an identifier `{0}'", + UnderlyingValueField); + return; + } + + AddMember (em); + } + + public void Error_UnderlyingType (Location loc) + { + Report.Error (1008, loc, + "Type byte, sbyte, short, ushort, int, uint, long or ulong expected"); + } + + protected override void DoDefineContainer () + { + TypeSpec ut; + if (underlying_type_expr != null) { + ut = underlying_type_expr.ResolveAsType (this); + if (!EnumSpec.IsValidUnderlyingType (ut)) { + Error_UnderlyingType (underlying_type_expr.Location); + ut = null; + } + } else { + ut = null; + } + + if (ut == null) + ut = Compiler.BuiltinTypes.Int; + + ((EnumSpec) spec).UnderlyingType = ut; + + TypeBuilder.DefineField (UnderlyingValueField, UnderlyingType.GetMetaInfo (), + FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName); + + DefineBaseTypes (); + } + + protected override bool DoDefineMembers () + { + for (int i = 0; i < Members.Count; ++i) { + EnumMember em = (EnumMember) Members[i]; + if (em.Initializer == null) { + em.Initializer = new ImplicitInitializer (em, i == 0 ? null : (EnumMember) Members[i - 1]); + } + + em.Define (); + } + + return true; + } + + public override bool IsUnmanagedType () + { + return true; + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_type = Compiler.BuiltinTypes.Enum; + base_class = null; + return null; + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + switch (UnderlyingType.BuiltinType) { + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.UShort: + Report.Warning (3009, 1, Location, "`{0}': base type `{1}' is not CLS-compliant", + GetSignatureForError (), UnderlyingType.GetSignatureForError ()); + break; + } + + return true; + } + } + + class EnumSpec : TypeSpec + { + TypeSpec underlying; + + public EnumSpec (TypeSpec declaringType, ITypeDefinition definition, TypeSpec underlyingType, MetaType info, Modifiers modifiers) + : base (MemberKind.Enum, declaringType, definition, info, modifiers | Modifiers.SEALED) + { + this.underlying = underlyingType; + } + + public TypeSpec UnderlyingType { + get { + return underlying; + } + set { + if (underlying != null) + throw new InternalErrorException ("UnderlyingType reset"); + + underlying = value; + } + } + + public static TypeSpec GetUnderlyingType (TypeSpec t) + { + return ((EnumSpec) t.GetDefinition ()).UnderlyingType; + } + + public static bool IsValidUnderlyingType (TypeSpec type) + { + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.ULong: + return true; + } + + return false; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/eval.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/eval.cs new file mode 100644 index 000000000..aac77eee6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/eval.cs @@ -0,0 +1,1333 @@ +// +// eval.cs: Evaluation and Hosting API for the C# compiler +// +// Authors: +// Miguel de Icaza (miguel@gnome.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2011 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Threading; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.IO; +using System.Text; +using System.Linq; + +namespace Mono.CSharp +{ + + /// + /// Experimental! + /// + public delegate void ValueModificationHandler (string variableName, int row, int column, object value); + + /// + /// Evaluator: provides an API to evaluate C# statements and + /// expressions dynamically. + /// + /// + /// This class exposes static methods to evaluate expressions in the + /// current program. + /// + /// To initialize the evaluator with a number of compiler + /// options call the Init(string[]args) method with a set of + /// command line options that the compiler recognizes. + /// + /// To interrupt execution of a statement, you can invoke the + /// Evaluator.Interrupt method. + /// + public class Evaluator { + + enum ParseMode { + // Parse silently, do not output any error messages + Silent, + + // Report errors during parse + ReportErrors, + + // Auto-complete, means that the tokenizer will start producing + // GETCOMPLETIONS tokens when it reaches a certain point. + GetCompletions + } + + static object evaluator_lock = new object (); + static volatile bool invoking; + +#if !STATIC + static int count; +#endif + static Thread invoke_thread; + + readonly Dictionary> fields; + + Type base_class; + bool inited; + int startup_files; + + readonly CompilerContext ctx; + readonly ModuleContainer module; + readonly ReflectionImporter importer; + readonly CompilationSourceFile source_file; + + int? listener_id; + + public Evaluator (CompilerContext ctx) + { + this.ctx = ctx; + + module = new ModuleContainer (ctx); + module.Evaluator = this; + + source_file = new CompilationSourceFile (module, null); + module.AddTypeContainer (source_file); + + startup_files = ctx.SourceFiles.Count; + + // FIXME: Importer needs this assembly for internalsvisibleto + module.SetDeclaringAssembly (new AssemblyDefinitionDynamic (module, "evaluator")); + importer = new ReflectionImporter (module, ctx.BuiltinTypes); + + InteractiveBaseClass = typeof (InteractiveBase); + fields = new Dictionary> (); + } + + void Init () + { + var loader = new DynamicLoader (importer, ctx); + + RootContext.ToplevelTypes = module; + + //var startup_files = new List (); + //foreach (CompilationUnit file in Location.SourceFiles) + // startup_files.Add (file.Path); + + loader.LoadReferences (module); + ctx.BuiltinTypes.CheckDefinitions (module); + module.InitializePredefinedTypes (); + + inited = true; + } + + void ParseStartupFiles () + { + Driver d = new Driver (ctx); + + Location.Initialize (ctx.SourceFiles); + + var parser_session = new ParserSession (); + for (int i = 0; i < startup_files; ++i) { + var sf = ctx.SourceFiles [i]; + d.Parse (sf, module, parser_session, ctx.Report); + } + } + + void Reset () + { + Location.Reset (); + Location.Initialize (ctx.SourceFiles); + } + + /// + /// When set evaluator will automatically wait on Task of async methods. When not + /// set it's called responsibility to handle Task execution + /// + public bool WaitOnTask { get; set; } + + /// + /// If true, turns type expressions into valid expressions + /// and calls the describe method on it + /// + public bool DescribeTypeExpressions; + + /// + /// Whether the evaluator will use terse syntax, and the semicolons at the end are optional + /// + public bool Terse = true; + + /// + /// The base class for the classes that host the user generated code + /// + /// + /// + /// This is the base class that will host the code + /// executed by the Evaluator. By default + /// this is the Mono.CSharp.InteractiveBase class + /// which is useful for interactive use. + /// + /// By changing this property you can control the + /// base class and the static members that are + /// available to your evaluated code. + /// + public Type InteractiveBaseClass { + get { + return base_class; + } + set { + base_class = value; + + if (value != null && typeof (InteractiveBase).IsAssignableFrom (value)) + InteractiveBase.Evaluator = this; + } + } + + /// + /// Interrupts the evaluation of an expression executing in Evaluate. + /// + /// + /// Use this method to interrupt long-running invocations. + /// + public void Interrupt () + { + if (!inited || !invoking) + return; + + if (invoke_thread != null) + invoke_thread.Abort (); + } + + /// + /// Compiles the input string and returns a delegate that represents the compiled code. + /// + /// + /// + /// Compiles the input string as a C# expression or + /// statement, unlike the Evaluate method, the + /// resulting delegate can be invoked multiple times + /// without incurring in the compilation overhead. + /// + /// If the return value of this function is null, + /// this indicates that the parsing was complete. + /// If the return value is a string it indicates + /// that the input string was partial and that the + /// invoking code should provide more code before + /// the code can be successfully compiled. + /// + /// If you know that you will always get full expressions or + /// statements and do not care about partial input, you can use + /// the other Compile overload. + /// + /// On success, in addition to returning null, the + /// compiled parameter will be set to the delegate + /// that can be invoked to execute the code. + /// + /// + public string Compile (string input, out CompiledMethod compiled) + { + if (input == null || input.Length == 0){ + compiled = null; + return null; + } + + lock (evaluator_lock){ + if (!inited) { + Init (); + ParseStartupFiles (); + } else { + ctx.Report.Printer.Reset (); + } + + bool partial_input; + CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input); + + // Terse mode, try to provide the trailing semicolon automatically. + if (parser == null && Terse && partial_input){ + bool ignore; + + // check if the source would compile with a block, if so, we should not + // add the semicolon. + var needs_block = ParseString (ParseMode.Silent, input + "{}", out ignore) != null; + if (!needs_block) + parser = ParseString (ParseMode.Silent, input + ";", out ignore); + } + if (parser == null){ + compiled = null; + if (partial_input) + return input; + + ParseString (ParseMode.ReportErrors, input, out partial_input); + return null; + } + + Class parser_result = parser.InteractiveResult; + compiled = CompileBlock (parser_result, parser.undo, ctx.Report); + return null; + } + } + + /// + /// Compiles the input string and returns a delegate that represents the compiled code. + /// + /// + /// + /// Compiles the input string as a C# expression or + /// statement, unlike the Evaluate method, the + /// resulting delegate can be invoked multiple times + /// without incurring in the compilation overhead. + /// + /// This method can only deal with fully formed input + /// strings and does not provide a completion mechanism. + /// If you must deal with partial input (for example for + /// interactive use) use the other overload. + /// + /// On success, a delegate is returned that can be used + /// to invoke the method. + /// + /// + public CompiledMethod Compile (string input) + { + CompiledMethod compiled; + + // Ignore partial inputs + if (Compile (input, out compiled) != null){ + // Error, the input was partial. + return null; + } + + // Either null (on error) or the compiled method. + return compiled; + } + + static MethodInfo listener_proxy_value; + internal void EmitValueChangedCallback (EmitContext ec, string name, TypeSpec type, Location loc) + { + if (listener_id == null) + listener_id = ListenerProxy.Register (ModificationListener); + + if (listener_proxy_value == null) + listener_proxy_value = typeof (ListenerProxy).GetMethod ("ValueChanged"); + +#if STATIC + throw new NotSupportedException (); +#else + // object value, int row, int col, string name, int listenerId + if (type.IsStructOrEnum) + ec.Emit (OpCodes.Box, type); + + ec.EmitInt (loc.Row); + ec.EmitInt (loc.Column); + ec.Emit (OpCodes.Ldstr, name); + ec.EmitInt (listener_id.Value); + ec.Emit (OpCodes.Call, listener_proxy_value); +#endif + } + + /// + /// Evaluates and expression or statement and returns any result values. + /// + /// + /// Evaluates the input string as a C# expression or + /// statement. If the input string is an expression + /// the result will be stored in the result variable + /// and the result_set variable will be set to true. + /// + /// It is necessary to use the result/result_set + /// pair to identify when a result was set (for + /// example, execution of user-provided input can be + /// an expression, a statement or others, and + /// result_set would only be set if the input was an + /// expression. + /// + /// If the return value of this function is null, + /// this indicates that the parsing was complete. + /// If the return value is a string, it indicates + /// that the input is partial and that the user + /// should provide an updated string. + /// + public string Evaluate (string input, out object result, out bool result_set) + { + CompiledMethod compiled; + + result_set = false; + result = null; + + input = Compile (input, out compiled); + if (input != null) + return input; + + if (compiled == null) + return null; + + // + // The code execution does not need to keep the compiler lock + // + object retval = typeof (QuitValue); + + try { + invoke_thread = System.Threading.Thread.CurrentThread; + invoking = true; + compiled (ref retval); + } catch (ThreadAbortException e){ + Thread.ResetAbort (); + Console.WriteLine ("Interrupted!\n{0}", e); + } finally { + invoking = false; + + if (listener_id != null) { + ListenerProxy.Unregister (listener_id.Value); + listener_id = null; + } + } + + // + // We use a reference to a compiler type, in this case + // Driver as a flag to indicate that this was a statement + // + if (!ReferenceEquals (retval, typeof (QuitValue))) { + result_set = true; + result = retval; + } + + return null; + } + + public string [] GetCompletions (string input, out string prefix) + { + prefix = ""; + if (input == null || input.Length == 0) + return null; + + lock (evaluator_lock){ + if (!inited) + Init (); + + bool partial_input; + CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input); + if (parser == null){ + return null; + } + + Class host = parser.InteractiveResult; + + var base_class_imported = importer.ImportType (base_class); + var baseclass_list = new List (1) { + new TypeExpression (base_class_imported, host.Location) + }; + host.SetBaseTypes (baseclass_list); + +#if NET_4_0 + var access = AssemblyBuilderAccess.RunAndCollect; +#else + var access = AssemblyBuilderAccess.Run; +#endif + var a = new AssemblyDefinitionDynamic (module, "completions"); + a.Create (AppDomain.CurrentDomain, access); + module.SetDeclaringAssembly (a); + + // Need to setup MemberCache + host.CreateContainer (); + // Need to setup base type + host.DefineContainer (); + + var method = host.Members[0] as Method; + BlockContext bc = new BlockContext (method, method.Block, ctx.BuiltinTypes.Void); + + try { + method.Block.Resolve (bc, method); + } catch (CompletionResult cr) { + prefix = cr.BaseText; + return cr.Result; + } + } + return null; + } + + /// + /// Executes the given expression or statement. + /// + /// + /// Executes the provided statement, returns true + /// on success, false on parsing errors. Exceptions + /// might be thrown by the called code. + /// + public bool Run (string statement) + { + object result; + bool result_set; + + return Evaluate (statement, out result, out result_set) == null; + } + + /// + /// Evaluates and expression or statement and returns the result. + /// + /// + /// Evaluates the input string as a C# expression or + /// statement and returns the value. + /// + /// This method will throw an exception if there is a syntax error, + /// of if the provided input is not an expression but a statement. + /// + public object Evaluate (string input) + { + object result; + bool result_set; + + string r = Evaluate (input, out result, out result_set); + + if (r != null) + throw new ArgumentException ("Syntax error on input: partial input"); + + if (result_set == false) + throw new ArgumentException ("The expression failed to resolve"); + + return result; + } + + /// + /// Experimental! + /// + public ValueModificationHandler ModificationListener { get; set; } + + enum InputKind { + EOF, + StatementOrExpression, + CompilationUnit, + Error + } + + // + // Deambiguates the input string to determine if we + // want to process a statement or if we want to + // process a compilation unit. + // + // This is done using a top-down predictive parser, + // since the yacc/jay parser can not deambiguage this + // without more than one lookahead token. There are very + // few ambiguities. + // + InputKind ToplevelOrStatement (SeekableStreamReader seekable) + { + Tokenizer tokenizer = new Tokenizer (seekable, source_file, new ParserSession (), ctx.Report); + + // Prefer contextual block keywords over identifiers + tokenizer.parsing_block++; + + int t = tokenizer.token (); + switch (t){ + case Token.EOF: + return InputKind.EOF; + + // These are toplevels + case Token.EXTERN: + case Token.OPEN_BRACKET: + case Token.ABSTRACT: + case Token.CLASS: + case Token.ENUM: + case Token.INTERFACE: + case Token.INTERNAL: + case Token.NAMESPACE: + case Token.PRIVATE: + case Token.PROTECTED: + case Token.PUBLIC: + case Token.SEALED: + case Token.STATIC: + case Token.STRUCT: + return InputKind.CompilationUnit; + + // Definitely expression + case Token.FIXED: + case Token.BOOL: + case Token.BYTE: + case Token.CHAR: + case Token.DECIMAL: + case Token.DOUBLE: + case Token.FLOAT: + case Token.INT: + case Token.LONG: + case Token.NEW: + case Token.OBJECT: + case Token.SBYTE: + case Token.SHORT: + case Token.STRING: + case Token.UINT: + case Token.ULONG: + return InputKind.StatementOrExpression; + + // These need deambiguation help + case Token.USING: + t = tokenizer.token (); + if (t == Token.EOF) + return InputKind.EOF; + + if (t == Token.IDENTIFIER) + return InputKind.CompilationUnit; + return InputKind.StatementOrExpression; + + + // Distinguish between: + // delegate opt_anonymous_method_signature block + // delegate type + case Token.DELEGATE: + t = tokenizer.token (); + if (t == Token.EOF) + return InputKind.EOF; + if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE) + return InputKind.StatementOrExpression; + return InputKind.CompilationUnit; + + // Distinguih between: + // unsafe block + // unsafe as modifier of a type declaration + case Token.UNSAFE: + t = tokenizer.token (); + if (t == Token.EOF) + return InputKind.EOF; + if (t == Token.OPEN_PARENS) + return InputKind.StatementOrExpression; + return InputKind.CompilationUnit; + + // These are errors: we list explicitly what we had + // from the grammar, ERROR and then everything else + + case Token.READONLY: + case Token.OVERRIDE: + case Token.ERROR: + return InputKind.Error; + + // This catches everything else allowed by + // expressions. We could add one-by-one use cases + // if needed. + default: + return InputKind.StatementOrExpression; + } + } + + // + // Parses the string @input and returns a CSharpParser if succeeful. + // + // if @silent is set to true then no errors are + // reported to the user. This is used to do various calls to the + // parser and check if the expression is parsable. + // + // @partial_input: if @silent is true, then it returns whether the + // parsed expression was partial, and more data is needed + // + CSharpParser ParseString (ParseMode mode, string input, out bool partial_input) + { + partial_input = false; + Reset (); + + var enc = ctx.Settings.Encoding; + var s = new MemoryStream (enc.GetBytes (input)); + SeekableStreamReader seekable = new SeekableStreamReader (s, enc); + + InputKind kind = ToplevelOrStatement (seekable); + if (kind == InputKind.Error){ + if (mode == ParseMode.ReportErrors) + ctx.Report.Error (-25, "Detection Parsing Error"); + partial_input = false; + return null; + } + + if (kind == InputKind.EOF){ + if (mode == ParseMode.ReportErrors) + Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true"); + partial_input = true; + return null; + + } + seekable.Position = 0; + + source_file.DeclarationFound = false; + CSharpParser parser = new CSharpParser (seekable, source_file, new ParserSession ()); + + if (kind == InputKind.StatementOrExpression){ + parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter; + parser.Lexer.parsing_block++; + ctx.Settings.StatementMode = true; + } else { + parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter; + ctx.Settings.StatementMode = false; + } + + if (mode == ParseMode.GetCompletions) + parser.Lexer.CompleteOnEOF = true; + + ReportPrinter old_printer = null; + if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions)) + old_printer = ctx.Report.SetPrinter (new StreamReportPrinter (TextWriter.Null)); + + try { + parser.parse (); + } finally { + if (ctx.Report.Errors != 0){ + if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF) + partial_input = true; + + if (parser.undo != null) + parser.undo.ExecuteUndo (); + + parser = null; + } + + if (old_printer != null) + ctx.Report.SetPrinter (old_printer); + } + return parser; + } + + CompiledMethod CompileBlock (Class host, Undo undo, Report Report) + { +#if STATIC + throw new NotSupportedException (); +#else + string current_debug_name = "eval-" + count + ".dll"; + ++count; + + AssemblyDefinitionDynamic assembly; + AssemblyBuilderAccess access; + + if (Environment.GetEnvironmentVariable ("SAVE") != null) { + access = AssemblyBuilderAccess.RunAndSave; + assembly = new AssemblyDefinitionDynamic (module, current_debug_name, current_debug_name); + assembly.Importer = importer; + } else { +#if NET_4_0 + access = AssemblyBuilderAccess.RunAndCollect; +#else + access = AssemblyBuilderAccess.Run; +#endif + assembly = new AssemblyDefinitionDynamic (module, current_debug_name); + } + + assembly.Create (AppDomain.CurrentDomain, access); + + Method expression_method; + if (host != null) { + var base_class_imported = importer.ImportType (base_class); + var baseclass_list = new List (1) { + new TypeExpression (base_class_imported, host.Location) + }; + + host.SetBaseTypes (baseclass_list); + + expression_method = (Method) host.Members[0]; + + if ((expression_method.ModFlags & Modifiers.ASYNC) != 0) { + // + // Host method is async. When WaitOnTask is set we wrap it with wait + // + // void AsyncWait (ref object $retval) { + // $retval = Host(); + // ((Task)$retval).Wait(); // When WaitOnTask is set + // } + // + var p = new ParametersCompiled ( + new Parameter (new TypeExpression (module.Compiler.BuiltinTypes.Object, Location.Null), "$retval", Parameter.Modifier.REF, null, Location.Null) + ); + + var method = new Method(host, new TypeExpression(module.Compiler.BuiltinTypes.Void, Location.Null), + Modifiers.PUBLIC | Modifiers.STATIC, new MemberName("AsyncWait"), p, null); + + method.Block = new ToplevelBlock(method.Compiler, p, Location.Null); + method.Block.AddStatement(new StatementExpression (new SimpleAssign( + new SimpleName(p [0].Name, Location.Null), + new Invocation(new SimpleName(expression_method.MemberName.Name, Location.Null), new Arguments(0)), + Location.Null), Location.Null)); + + if (WaitOnTask) { + var task = new Cast (expression_method.TypeExpression, new SimpleName (p [0].Name, Location.Null), Location.Null); + + method.Block.AddStatement (new StatementExpression (new Invocation ( + new MemberAccess (task, "Wait", Location.Null), + new Arguments (0)), Location.Null)); + } + + host.AddMember(method); + + expression_method = method; + } + + host.CreateContainer(); + host.DefineContainer(); + host.Define(); + + } else { + expression_method = null; + } + + module.CreateContainer (); + + // Disable module and source file re-definition checks + module.EnableRedefinition (); + source_file.EnableRedefinition (); + + module.Define (); + + if (Report.Errors != 0){ + if (undo != null) + undo.ExecuteUndo (); + + return null; + } + + if (host != null){ + host.PrepareEmit (); + host.EmitContainer (); + } + + module.EmitContainer (); + + if (Report.Errors != 0){ + if (undo != null) + undo.ExecuteUndo (); + return null; + } + + module.CloseContainer (); + if (host != null) + host.CloseContainer (); + + if (access == AssemblyBuilderAccess.RunAndSave) + assembly.Save (); + + if (host == null) + return null; + + // + // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant + // work from MethodBuilders. Retarded, I know. + // + var tt = assembly.Builder.GetType (host.TypeBuilder.Name); + var mi = tt.GetMethod (expression_method.MemberName.Name); + + // + // We need to then go from FieldBuilder to FieldInfo + // or reflection gets confused (it basically gets confused, and variables override each + // other). + // + foreach (var member in host.Members) { + var field = member as Field; + if (field == null) + continue; + + var fi = tt.GetField (field.Name); + + Tuple old; + + // If a previous value was set, nullify it, so that we do + // not leak memory + if (fields.TryGetValue (field.Name, out old)) { + if (old.Item1.MemberType.IsStruct) { + // + // TODO: Clear fields for structs + // + } else { + try { + old.Item2.SetValue (null, null); + } catch { + } + } + } + + fields[field.Name] = Tuple.Create (field.Spec, fi); + } + + return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi); +#endif + } + + /// + /// A sentinel value used to indicate that no value was + /// was set by the compiled function. This is used to + /// differentiate between a function not returning a + /// value and null. + /// + internal static class QuitValue { } + + internal Tuple LookupField (string name) + { + Tuple fi; + fields.TryGetValue (name, out fi); + return fi; + } + + static string Quote (string s) + { + if (s.IndexOf ('"') != -1) + s = s.Replace ("\"", "\\\""); + + return "\"" + s + "\""; + } + + public string GetUsing () + { + StringBuilder sb = new StringBuilder (); + // TODO: + //foreach (object x in ns.using_alias_list) + // sb.AppendFormat ("using {0};\n", x); + + foreach (var ue in source_file.Usings) { + sb.AppendFormat ("using {0};", ue.ToString ()); + sb.Append (Environment.NewLine); + } + + return sb.ToString (); + } + + internal List GetUsingList () + { + var res = new List (); + + foreach (var ue in source_file.Usings) { + if (ue.Alias != null || ue.ResolvedExpression == null) + continue; + + res.Add (ue.NamespaceExpression.Name); + } + + return res; + } + + internal string [] GetVarNames () + { + lock (evaluator_lock){ + return new List (fields.Keys).ToArray (); + } + } + + public string GetVars () + { + lock (evaluator_lock){ + StringBuilder sb = new StringBuilder (); + + foreach (var de in fields){ + var fi = LookupField (de.Key); + object value; + try { + value = fi.Item2.GetValue (null); + if (value is string) + value = Quote ((string)value); + } catch { + value = ""; + } + + sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value); + sb.AppendLine (); + } + + return sb.ToString (); + } + } + + /// + /// Loads the given assembly and exposes the API to the user. + /// + public void LoadAssembly (string file) + { + var loader = new DynamicLoader (importer, ctx); + var assembly = loader.LoadAssemblyFile (file, false); + if (assembly == null) + return; + + lock (evaluator_lock){ + importer.ImportAssembly (assembly, module.GlobalRootNamespace); + } + } + + /// + /// Exposes the API of the given assembly to the Evaluator + /// + public void ReferenceAssembly (Assembly a) + { + lock (evaluator_lock){ + importer.ImportAssembly (a, module.GlobalRootNamespace); + } + } + } + + + /// + /// A delegate that can be used to invoke the + /// compiled expression or statement. + /// + /// + /// Since the Compile methods will compile + /// statements and expressions into the same + /// delegate, you can tell if a value was returned + /// by checking whether the returned value is of type + /// NoValueSet. + /// + + public delegate void CompiledMethod (ref object retvalue); + + /// + /// The default base class for every interaction line + /// + /// + /// The expressions and statements behave as if they were + /// a static method of this class. The InteractiveBase class + /// contains a number of useful methods, but can be overwritten + /// by setting the InteractiveBaseType property in the Evaluator + /// + public class InteractiveBase { + /// + /// Determines where the standard output of methods in this class will go. + /// + public static TextWriter Output = Console.Out; + + /// + /// Determines where the standard error of methods in this class will go. + /// + public static TextWriter Error = Console.Error; + + /// + /// The primary prompt used for interactive use. + /// + public static string Prompt = "csharp> "; + + /// + /// The secondary prompt used for interactive use (used when + /// an expression is incomplete). + /// + public static string ContinuationPrompt = " > "; + + /// + /// Used to signal that the user has invoked the `quit' statement. + /// + public static bool QuitRequested; + + public static Evaluator Evaluator; + + /// + /// Shows all the variables defined so far. + /// + static public void ShowVars () + { + Output.Write (Evaluator.GetVars ()); + Output.Flush (); + } + + /// + /// Displays the using statements in effect at this point. + /// + static public void ShowUsing () + { + Output.Write (Evaluator.GetUsing ()); + Output.Flush (); + } + + /// + /// Times the execution of the given delegate + /// + static public TimeSpan Time (Action a) + { + DateTime start = DateTime.Now; + a (); + return DateTime.Now - start; + } + + /// + /// Loads the assemblies from a package + /// + /// + /// Loads the assemblies from a package. This is equivalent + /// to passing the -pkg: command line flag to the C# compiler + /// on the command line. + /// + static public void LoadPackage (string pkg) + { + if (pkg == null){ + Error.WriteLine ("Invalid package specified"); + return; + } + + string pkgout = Driver.GetPackageFlags (pkg, null); + + string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}). + Split (new Char [] { ' ', '\t'}); + + foreach (string s in xargs){ + if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){ + string lib = s.Substring (s.IndexOf (':')+1); + + Evaluator.LoadAssembly (lib); + continue; + } + } + } + + /// + /// Loads the assembly + /// + /// + /// Loads the specified assembly and makes its types + /// available to the evaluator. This is equivalent + /// to passing the -pkg: command line flag to the C# + /// compiler on the command line. + /// + static public void LoadAssembly (string assembly) + { + Evaluator.LoadAssembly (assembly); + } + + static public void print (object obj) + { + Output.WriteLine (obj); + } + + static public void print (string fmt, params object [] args) + { + Output.WriteLine (fmt, args); + } + + /// + /// Returns a list of available static methods. + /// + static public string help { + get { + return "Static methods:\n" + + " Describe (object); - Describes the object's type\n" + + " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" + + " LoadAssembly (assembly); - Loads the given assembly (like -r:ASSEMBLY)\n" + + " ShowVars (); - Shows defined local variables.\n" + + " ShowUsing (); - Show active using declarations.\n" + + " Prompt - The prompt used by the C# shell\n" + + " ContinuationPrompt - The prompt for partial input\n" + + " Time (() => { }); - Times the specified code\n" + + " print (obj); - Shorthand for Console.WriteLine\n" + + " quit; - You'll never believe it - this quits the repl!\n" + + " help; - This help text\n"; + } + } + + /// + /// Indicates to the read-eval-print-loop that the interaction should be finished. + /// + static public object quit { + get { + QuitRequested = true; + + // To avoid print null at the exit + return typeof (Evaluator.QuitValue); + } + } + + /// + /// Same as quit - useful in script scenerios + /// + static public void Quit () { + QuitRequested = true; + } + +#if !NET_2_1 + /// + /// Describes an object or a type. + /// + /// + /// This method will show a textual representation + /// of the object's type. If the object is a + /// System.Type it renders the type directly, + /// otherwise it renders the type returned by + /// invoking GetType on the object. + /// + static public string Describe (object x) + { + if (x == null) + return ""; + + var type = x as Type ?? x.GetType (); + + StringWriter sw = new StringWriter (); + new Outline (type, sw, true, false, false).OutlineType (); + return sw.ToString (); + } +#endif + } + + class InteractiveMethod : Method + { + public InteractiveMethod(TypeDefinition parent, FullNamedExpression returnType, Modifiers mod, ParametersCompiled parameters) + : base(parent, returnType, mod, new MemberName("Host"), parameters, null) + { + } + + public void ChangeToAsync () + { + ModFlags |= Modifiers.ASYNC; + ModFlags &= ~Modifiers.UNSAFE; + type_expr = new TypeExpression(Module.PredefinedTypes.Task.TypeSpec, Location); + parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + + public override string GetSignatureForError() + { + return "InteractiveHost"; + } + } + + class HoistedEvaluatorVariable : HoistedVariable + { + public HoistedEvaluatorVariable (Field field) + : base (null, field) + { + } + + protected override FieldExpr GetFieldExpression (EmitContext ec) + { + return new FieldExpr (field, field.Location); + } + } + + /// + /// A class used to assign values if the source expression is not void + /// + /// Used by the interactive shell to allow it to call this code to set + /// the return value for an invocation. + /// + class OptionalAssign : SimpleAssign { + public OptionalAssign (Expression s, Location loc) + : base (null, s, loc) + { + } + + public override Location StartLocation { + get { + return Location.Null; + } + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression clone = source.Clone (new CloneContext ()); + + clone = clone.Resolve (ec); + if (clone == null) + return null; + + // + // A useful feature for the REPL: if we can resolve the expression + // as a type, Describe the type; + // + if (ec.Module.Evaluator.DescribeTypeExpressions && !(ec.CurrentAnonymousMethod is AsyncInitializer)) { + var old_printer = ec.Report.SetPrinter (new SessionReportPrinter ()); + Expression tclone; + try { + // Note: clone context cannot be shared otherwise block mapping would leak + tclone = source.Clone (new CloneContext ()); + tclone = tclone.Resolve (ec, ResolveFlags.Type); + if (ec.Report.Errors > 0) + tclone = null; + } finally { + ec.Report.SetPrinter (old_printer); + } + + if (tclone is TypeExpr) { + Arguments args = new Arguments (1); + args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location))); + return new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec); + } + } + + // This means its really a statement. + if (clone.Type.Kind == MemberKind.Void || clone is DynamicInvocation || clone is Assign) { + return clone; + } + + source = clone; + + var host = (Method) ec.MemberContext.CurrentMemberDefinition; + + if (host.ParameterInfo.IsEmpty) { + eclass = ExprClass.Value; + type = InternalType.FakeInternalType; + return this; + } + + target = new SimpleName (host.ParameterInfo[0].Name, Location); + + return base.DoResolve (ec); + } + + public override void EmitStatement(EmitContext ec) + { + if (target == null) { + source.Emit (ec); + return; + } + + base.EmitStatement(ec); + } + } + + public class Undo + { + List undo_actions; + + public void AddTypeContainer (TypeContainer current_container, TypeDefinition tc) + { + if (current_container == tc){ + Console.Error.WriteLine ("Internal error: inserting container into itself"); + return; + } + + if (undo_actions == null) + undo_actions = new List (); + + if (current_container.Containers != null) + { + var existing = current_container.Containers.FirstOrDefault (l => l.Basename == tc.Basename); + if (existing != null) { + current_container.RemoveContainer (existing); + undo_actions.Add (() => current_container.AddTypeContainer (existing)); + } + } + + undo_actions.Add (() => current_container.RemoveContainer (tc)); + } + + public void ExecuteUndo () + { + if (undo_actions == null) + return; + + foreach (var p in undo_actions){ + p (); + } + + undo_actions = null; + } + } + + static class ListenerProxy + { + static readonly Dictionary listeners = new Dictionary (); + + static int counter; + + public static int Register (ValueModificationHandler listener) + { + lock (listeners) { + var id = counter++; + listeners.Add (id, listener); + return id; + } + } + + public static void Unregister (int listenerId) + { + lock (listeners) { + listeners.Remove (listenerId); + } + } + + public static void ValueChanged (object value, int row, int col, string name, int listenerId) + { + ValueModificationHandler action; + lock (listeners) { + if (!listeners.TryGetValue (listenerId, out action)) + return; + } + + action (name, row, col, value); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/expression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/expression.cs new file mode 100644 index 000000000..3a076077d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/expression.cs @@ -0,0 +1,11921 @@ +// +// expression.cs: Expression representation for the IL tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using SLE = System.Linq.Expressions; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // + // This is an user operator expression, automatically created during + // resolve phase + // + public class UserOperatorCall : Expression { + protected readonly Arguments arguments; + protected readonly MethodSpec oper; + readonly Func expr_tree; + + public UserOperatorCall (MethodSpec oper, Arguments args, Func expr_tree, Location loc) + { + this.oper = oper; + this.arguments = args; + this.expr_tree = expr_tree; + + type = oper.ReturnType; + eclass = ExprClass.Value; + this.loc = loc; + } + + public override bool ContainsEmitWithAwait () + { + return arguments.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (expr_tree != null) + return expr_tree (ec, new TypeOfMethod (oper, loc)); + + Arguments args = Arguments.CreateForExpressionTree (ec, arguments, + new NullLiteral (loc), + new TypeOfMethod (oper, loc)); + + return CreateExpressionFactoryCall (ec, "Call", args); + } + + protected override void CloneTo (CloneContext context, Expression target) + { + // Nothing to clone + } + + protected override Expression DoResolve (ResolveContext ec) + { + // + // We are born fully resolved + // + return this; + } + + public override void Emit (EmitContext ec) + { + var call = new CallEmitter (); + call.Emit (ec, oper, arguments, loc); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + arguments.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Call ((MethodInfo) oper.GetMetaInfo (), Arguments.MakeExpression (arguments, ctx)); +#endif + } + } + + public class ParenthesizedExpression : ShimExpression + { + public ParenthesizedExpression (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + + protected override Expression DoResolve (ResolveContext ec) + { + var res = expr.Resolve (ec); + var constant = res as Constant; + if (constant != null && constant.IsLiteral) + return Constant.CreateConstantFromValue (res.Type, constant.GetValue (), expr.Location); + + return res; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return expr.DoResolveLValue (ec, right_side); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Unary implements unary expressions. + // + public class Unary : Expression + { + public enum Operator : byte { + UnaryPlus, UnaryNegation, LogicalNot, OnesComplement, + AddressOf, TOP + } + + public readonly Operator Oper; + public Expression Expr; + ConvCast.Mode enum_conversion; + + public Unary (Operator op, Expression expr, Location loc) + { + Oper = op; + Expr = expr; + this.loc = loc; + } + + // + // This routine will attempt to simplify the unary expression when the + // argument is a constant. + // + Constant TryReduceConstant (ResolveContext ec, Constant constant) + { + var e = constant; + + while (e is EmptyConstantCast) + e = ((EmptyConstantCast) e).child; + + if (e is SideEffectConstant) { + Constant r = TryReduceConstant (ec, ((SideEffectConstant) e).value); + return r == null ? null : new SideEffectConstant (r, e, r.Location); + } + + TypeSpec expr_type = e.Type; + + switch (Oper){ + case Operator.UnaryPlus: + // Unary numeric promotions + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + return new IntConstant (ec.BuiltinTypes, ((ByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.SByte: + return new IntConstant (ec.BuiltinTypes, ((SByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Short: + return new IntConstant (ec.BuiltinTypes, ((ShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.UShort: + return new IntConstant (ec.BuiltinTypes, ((UShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Char: + return new IntConstant (ec.BuiltinTypes, ((CharConstant) e).Value, e.Location); + + // Predefined operators + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.Double: + case BuiltinTypeSpec.Type.Decimal: + return e; + } + + return null; + + case Operator.UnaryNegation: + // Unary numeric promotions + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + return new IntConstant (ec.BuiltinTypes, -((ByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.SByte: + return new IntConstant (ec.BuiltinTypes, -((SByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Short: + return new IntConstant (ec.BuiltinTypes, -((ShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.UShort: + return new IntConstant (ec.BuiltinTypes, -((UShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Char: + return new IntConstant (ec.BuiltinTypes, -((CharConstant) e).Value, e.Location); + + // Predefined operators + case BuiltinTypeSpec.Type.Int: + int ivalue = ((IntConstant) e).Value; + if (ivalue == int.MinValue) { + if (ec.ConstantCheckState) { + ConstantFold.Error_CompileTimeOverflow (ec, loc); + return null; + } + return e; + } + return new IntConstant (ec.BuiltinTypes, -ivalue, e.Location); + + case BuiltinTypeSpec.Type.Long: + long lvalue = ((LongConstant) e).Value; + if (lvalue == long.MinValue) { + if (ec.ConstantCheckState) { + ConstantFold.Error_CompileTimeOverflow (ec, loc); + return null; + } + return e; + } + return new LongConstant (ec.BuiltinTypes, -lvalue, e.Location); + + case BuiltinTypeSpec.Type.UInt: + UIntLiteral uil = constant as UIntLiteral; + if (uil != null) { + if (uil.Value == int.MaxValue + (uint) 1) + return new IntLiteral (ec.BuiltinTypes, int.MinValue, e.Location); + return new LongLiteral (ec.BuiltinTypes, -uil.Value, e.Location); + } + return new LongConstant (ec.BuiltinTypes, -((UIntConstant) e).Value, e.Location); + + + case BuiltinTypeSpec.Type.ULong: + ULongLiteral ull = constant as ULongLiteral; + if (ull != null && ull.Value == 9223372036854775808) + return new LongLiteral (ec.BuiltinTypes, long.MinValue, e.Location); + return null; + + case BuiltinTypeSpec.Type.Float: + FloatLiteral fl = constant as FloatLiteral; + // For better error reporting + if (fl != null) + return new FloatLiteral (ec.BuiltinTypes, -fl.Value, e.Location); + + return new FloatConstant (ec.BuiltinTypes, -((FloatConstant) e).Value, e.Location); + + case BuiltinTypeSpec.Type.Double: + DoubleLiteral dl = constant as DoubleLiteral; + // For better error reporting + if (dl != null) + return new DoubleLiteral (ec.BuiltinTypes, -dl.Value, e.Location); + + return new DoubleConstant (ec.BuiltinTypes, -((DoubleConstant) e).Value, e.Location); + + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (ec.BuiltinTypes, -((DecimalConstant) e).Value, e.Location); + } + + return null; + + case Operator.LogicalNot: + if (expr_type.BuiltinType != BuiltinTypeSpec.Type.Bool) + return null; + + bool b = (bool)e.GetValue (); + return new BoolConstant (ec.BuiltinTypes, !b, e.Location); + + case Operator.OnesComplement: + // Unary numeric promotions + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + return new IntConstant (ec.BuiltinTypes, ~((ByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.SByte: + return new IntConstant (ec.BuiltinTypes, ~((SByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Short: + return new IntConstant (ec.BuiltinTypes, ~((ShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.UShort: + return new IntConstant (ec.BuiltinTypes, ~((UShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Char: + return new IntConstant (ec.BuiltinTypes, ~((CharConstant) e).Value, e.Location); + + // Predefined operators + case BuiltinTypeSpec.Type.Int: + return new IntConstant (ec.BuiltinTypes, ~((IntConstant)e).Value, e.Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (ec.BuiltinTypes, ~((UIntConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (ec.BuiltinTypes, ~((LongConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (ec.BuiltinTypes, ~((ULongConstant) e).Value, e.Location); + } + if (e is EnumConstant) { + var res = TryReduceConstant (ec, ((EnumConstant)e).Child); + if (res != null) { + // + // Numeric promotion upgraded types to int but for enum constant + // original underlying constant type is needed + // + if (res.Type.BuiltinType == BuiltinTypeSpec.Type.Int) { + int v = ((IntConstant) res).Value; + switch (((EnumConstant) e).Child.Type.BuiltinType) { + case BuiltinTypeSpec.Type.UShort: + res = new UShortConstant (ec.BuiltinTypes, (ushort) v, e.Location); + break; + case BuiltinTypeSpec.Type.Short: + res = new ShortConstant (ec.BuiltinTypes, (short) v, e.Location); + break; + case BuiltinTypeSpec.Type.Byte: + res = new ByteConstant (ec.BuiltinTypes, (byte) v, e.Location); + break; + case BuiltinTypeSpec.Type.SByte: + res = new SByteConstant (ec.BuiltinTypes, (sbyte) v, e.Location); + break; + } + } + + res = new EnumConstant (res, expr_type); + } + return res; + } + return null; + } + throw new Exception ("Can not constant fold: " + Oper.ToString()); + } + + protected virtual Expression ResolveOperator (ResolveContext ec, Expression expr) + { + eclass = ExprClass.Value; + + TypeSpec expr_type = expr.Type; + Expression best_expr; + + TypeSpec[] predefined = ec.BuiltinTypes.OperatorsUnary [(int) Oper]; + + // + // Primitive types first + // + if (BuiltinTypeSpec.IsPrimitiveType (expr_type)) { + best_expr = ResolvePrimitivePredefinedType (ec, expr, predefined); + if (best_expr == null) + return null; + + type = best_expr.Type; + Expr = best_expr; + return this; + } + + // + // E operator ~(E x); + // + if (Oper == Operator.OnesComplement && expr_type.IsEnum) + return ResolveEnumOperator (ec, expr, predefined); + + return ResolveUserType (ec, expr, predefined); + } + + protected virtual Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined) + { + TypeSpec underlying_type = EnumSpec.GetUnderlyingType (expr.Type); + Expression best_expr = ResolvePrimitivePredefinedType (ec, EmptyCast.Create (expr, underlying_type), predefined); + if (best_expr == null) + return null; + + Expr = best_expr; + enum_conversion = Binary.GetEnumResultCast (underlying_type); + type = expr.Type; + return EmptyCast.Create (this, type); + } + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return CreateExpressionTree (ec, null); + } + + Expression CreateExpressionTree (ResolveContext ec, Expression user_op) + { + string method_name; + switch (Oper) { + case Operator.AddressOf: + Error_PointerInsideExpressionTree (ec); + return null; + case Operator.UnaryNegation: + if (ec.HasSet (ResolveContext.Options.CheckedScope) && user_op == null && !IsFloat (type)) + method_name = "NegateChecked"; + else + method_name = "Negate"; + break; + case Operator.OnesComplement: + case Operator.LogicalNot: + method_name = "Not"; + break; + case Operator.UnaryPlus: + method_name = "UnaryPlus"; + break; + default: + throw new InternalErrorException ("Unknown unary operator " + Oper.ToString ()); + } + + Arguments args = new Arguments (2); + args.Add (new Argument (Expr.CreateExpressionTree (ec))); + if (user_op != null) + args.Add (new Argument (user_op)); + + return CreateExpressionFactoryCall (ec, method_name, args); + } + + public static TypeSpec[][] CreatePredefinedOperatorsTable (BuiltinTypes types) + { + var predefined_operators = new TypeSpec[(int) Operator.TOP][]; + + // + // 7.6.1 Unary plus operator + // + predefined_operators [(int) Operator.UnaryPlus] = new TypeSpec [] { + types.Int, types.UInt, + types.Long, types.ULong, + types.Float, types.Double, + types.Decimal + }; + + // + // 7.6.2 Unary minus operator + // + predefined_operators [(int) Operator.UnaryNegation] = new TypeSpec [] { + types.Int, types.Long, + types.Float, types.Double, + types.Decimal + }; + + // + // 7.6.3 Logical negation operator + // + predefined_operators [(int) Operator.LogicalNot] = new TypeSpec [] { + types.Bool + }; + + // + // 7.6.4 Bitwise complement operator + // + predefined_operators [(int) Operator.OnesComplement] = new TypeSpec [] { + types.Int, types.UInt, + types.Long, types.ULong + }; + + return predefined_operators; + } + + // + // Unary numeric promotions + // + static Expression DoNumericPromotion (ResolveContext rc, Operator op, Expression expr) + { + TypeSpec expr_type = expr.Type; + if (op == Operator.UnaryPlus || op == Operator.UnaryNegation || op == Operator.OnesComplement) { + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Char: + return Convert.ImplicitNumericConversion (expr, rc.BuiltinTypes.Int); + } + } + + if (op == Operator.UnaryNegation && expr_type.BuiltinType == BuiltinTypeSpec.Type.UInt) + return Convert.ImplicitNumericConversion (expr, rc.BuiltinTypes.Long); + + return expr; + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (Oper == Operator.AddressOf) { + return ResolveAddressOf (ec); + } + + Expr = Expr.Resolve (ec); + if (Expr == null) + return null; + + if (Expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments args = new Arguments (1); + args.Add (new Argument (Expr)); + return new DynamicUnaryConversion (GetOperatorExpressionTypeName (), args, loc).Resolve (ec); + } + + if (Expr.Type.IsNullableType) + return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec); + + // + // Attempt to use a constant folding operation. + // + Constant cexpr = Expr as Constant; + if (cexpr != null) { + cexpr = TryReduceConstant (ec, cexpr); + if (cexpr != null) + return cexpr; + } + + Expression expr = ResolveOperator (ec, Expr); + if (expr == null) + Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type); + + // + // Reduce unary operator on predefined types + // + if (expr == this && Oper == Operator.UnaryPlus) + return Expr; + + return expr; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right) + { + return null; + } + + public override void Emit (EmitContext ec) + { + EmitOperator (ec, type); + } + + protected void EmitOperator (EmitContext ec, TypeSpec type) + { + switch (Oper) { + case Operator.UnaryPlus: + Expr.Emit (ec); + break; + + case Operator.UnaryNegation: + if (ec.HasSet (EmitContext.Options.CheckedScope) && !IsFloat (type)) { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && Expr.ContainsEmitWithAwait ()) + Expr = Expr.EmitToField (ec); + + ec.EmitInt (0); + if (type.BuiltinType == BuiltinTypeSpec.Type.Long) + ec.Emit (OpCodes.Conv_U8); + Expr.Emit (ec); + ec.Emit (OpCodes.Sub_Ovf); + } else { + Expr.Emit (ec); + ec.Emit (OpCodes.Neg); + } + + break; + + case Operator.LogicalNot: + Expr.Emit (ec); + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + break; + + case Operator.OnesComplement: + Expr.Emit (ec); + ec.Emit (OpCodes.Not); + break; + + case Operator.AddressOf: + ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore); + break; + + default: + throw new Exception ("This should not happen: Operator = " + + Oper.ToString ()); + } + + // + // Same trick as in Binary expression + // + if (enum_conversion != 0) { + using (ec.With (BuilderContext.Options.CheckedScope, false)) { + ConvCast.Emit (ec, enum_conversion); + } + } + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + if (Oper == Operator.LogicalNot) + Expr.EmitBranchable (ec, target, !on_true); + else + base.EmitBranchable (ec, target, on_true); + } + + public override void EmitSideEffect (EmitContext ec) + { + Expr.EmitSideEffect (ec); + } + + public static void Error_Ambiguous (ResolveContext rc, string oper, TypeSpec type, Location loc) + { + rc.Report.Error (35, loc, "Operator `{0}' is ambiguous on an operand of type `{1}'", + oper, type.GetSignatureForError ()); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + FlowAnalysis (fc, false); + } + + public override void FlowAnalysisConditional (FlowAnalysisContext fc) + { + FlowAnalysis (fc, true); + } + + void FlowAnalysis (FlowAnalysisContext fc, bool conditional) + { + if (Oper == Operator.AddressOf) { + var vr = Expr as VariableReference; + if (vr != null && vr.VariableInfo != null) + fc.SetVariableAssigned (vr.VariableInfo); + + return; + } + + if (Oper == Operator.LogicalNot && conditional) { + Expr.FlowAnalysisConditional (fc); + + var temp = fc.DefiniteAssignmentOnTrue; + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse; + fc.DefiniteAssignmentOnFalse = temp; + } else { + Expr.FlowAnalysis (fc); + } + } + + // + // Converts operator to System.Linq.Expressions.ExpressionType enum name + // + string GetOperatorExpressionTypeName () + { + switch (Oper) { + case Operator.OnesComplement: + return "OnesComplement"; + case Operator.LogicalNot: + return "Not"; + case Operator.UnaryNegation: + return "Negate"; + case Operator.UnaryPlus: + return "UnaryPlus"; + default: + throw new NotImplementedException ("Unknown express type operator " + Oper.ToString ()); + } + } + + static bool IsFloat (TypeSpec t) + { + return t.BuiltinType == BuiltinTypeSpec.Type.Double || t.BuiltinType == BuiltinTypeSpec.Type.Float; + } + + // + // Returns a stringified representation of the Operator + // + public static string OperName (Operator oper) + { + switch (oper) { + case Operator.UnaryPlus: + return "+"; + case Operator.UnaryNegation: + return "-"; + case Operator.LogicalNot: + return "!"; + case Operator.OnesComplement: + return "~"; + case Operator.AddressOf: + return "&"; + } + + throw new NotImplementedException (oper.ToString ()); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + var expr = Expr.MakeExpression (ctx); + bool is_checked = ctx.HasSet (BuilderContext.Options.CheckedScope); + + switch (Oper) { + case Operator.UnaryNegation: + return is_checked ? SLE.Expression.NegateChecked (expr) : SLE.Expression.Negate (expr); + case Operator.LogicalNot: + return SLE.Expression.Not (expr); +#if NET_4_0 || MOBILE_DYNAMIC + case Operator.OnesComplement: + return SLE.Expression.OnesComplement (expr); +#endif + default: + throw new NotImplementedException (Oper.ToString ()); + } + } + + Expression ResolveAddressOf (ResolveContext ec) + { + if (!ec.IsUnsafe) + UnsafeError (ec, loc); + + Expr = Expr.DoResolveLValue (ec, EmptyExpression.UnaryAddress); + if (Expr == null || Expr.eclass != ExprClass.Variable) { + ec.Report.Error (211, loc, "Cannot take the address of the given expression"); + return null; + } + + if (!TypeManager.VerifyUnmanaged (ec.Module, Expr.Type, loc)) { + return null; + } + + IVariableReference vr = Expr as IVariableReference; + bool is_fixed; + if (vr != null) { + is_fixed = vr.IsFixed; + vr.SetHasAddressTaken (); + + if (vr.IsHoisted) { + AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, vr, loc); + } + } else { + IFixedExpression fe = Expr as IFixedExpression; + is_fixed = fe != null && fe.IsFixed; + } + + if (!is_fixed && !ec.HasSet (ResolveContext.Options.FixedInitializerScope)) { + ec.Report.Error (212, loc, "You can only take the address of unfixed expression inside of a fixed statement initializer"); + } + + type = PointerContainer.MakeType (ec.Module, Expr.Type); + eclass = ExprClass.Value; + return this; + } + + Expression ResolvePrimitivePredefinedType (ResolveContext rc, Expression expr, TypeSpec[] predefined) + { + expr = DoNumericPromotion (rc, Oper, expr); + TypeSpec expr_type = expr.Type; + foreach (TypeSpec t in predefined) { + if (t == expr_type) + return expr; + } + return null; + } + + // + // Perform user-operator overload resolution + // + protected virtual Expression ResolveUserOperator (ResolveContext ec, Expression expr) + { + CSharp.Operator.OpType op_type; + switch (Oper) { + case Operator.LogicalNot: + op_type = CSharp.Operator.OpType.LogicalNot; break; + case Operator.OnesComplement: + op_type = CSharp.Operator.OpType.OnesComplement; break; + case Operator.UnaryNegation: + op_type = CSharp.Operator.OpType.UnaryNegation; break; + case Operator.UnaryPlus: + op_type = CSharp.Operator.OpType.UnaryPlus; break; + default: + throw new InternalErrorException (Oper.ToString ()); + } + + var methods = MemberCache.GetUserOperator (expr.Type, op_type, false); + if (methods == null) + return null; + + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + + var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc); + var oper = res.ResolveOperator (ec, ref args); + + if (oper == null) + return null; + + Expr = args [0].Expr; + return new UserOperatorCall (oper, args, CreateExpressionTree, expr.Location); + } + + // + // Unary user type overload resolution + // + Expression ResolveUserType (ResolveContext ec, Expression expr, TypeSpec[] predefined) + { + Expression best_expr = ResolveUserOperator (ec, expr); + if (best_expr != null) + return best_expr; + + foreach (TypeSpec t in predefined) { + Expression oper_expr = Convert.ImplicitUserConversion (ec, expr, t, expr.Location); + if (oper_expr == null) + continue; + + if (oper_expr == ErrorExpression.Instance) + return oper_expr; + + // + // decimal type is predefined but has user-operators + // + if (oper_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) + oper_expr = ResolveUserType (ec, oper_expr, predefined); + else + oper_expr = ResolvePrimitivePredefinedType (ec, oper_expr, predefined); + + if (oper_expr == null) + continue; + + if (best_expr == null) { + best_expr = oper_expr; + continue; + } + + int result = OverloadResolver.BetterTypeConversion (ec, best_expr.Type, t); + if (result == 0) { + if ((oper_expr is UserOperatorCall || oper_expr is UserCast) && (best_expr is UserOperatorCall || best_expr is UserCast)) { + Error_Ambiguous (ec, OperName (Oper), expr.Type, loc); + } else { + Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), expr.Type); + } + + break; + } + + if (result == 2) + best_expr = oper_expr; + } + + if (best_expr == null) + return null; + + // + // HACK: Decimal user-operator is included in standard operators + // + if (best_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) + return best_expr; + + Expr = best_expr; + type = best_expr.Type; + return this; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Unary target = (Unary) t; + + target.Expr = Expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + // + // Unary operators are turned into Indirection expressions + // after semantic analysis (this is so we can take the address + // of an indirection). + // + public class Indirection : Expression, IMemoryLocation, IAssignMethod, IFixedExpression { + Expression expr; + LocalTemporary temporary; + bool prepared; + + public Indirection (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + public Expression Expr { + get { + return expr; + } + } + + public bool IsFixed { + get { return true; } + } + + public override Location StartLocation { + get { + return expr.StartLocation; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Indirection target = (Indirection) t; + target.expr = expr.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return null; + } + + public override void Emit (EmitContext ec) + { + if (!prepared) + expr.Emit (ec); + + ec.EmitLoadFromPtr (Type); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + Emit (ec); + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temporary = new LocalTemporary (expr.Type); + temporary.Store (ec); + } + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + prepared = isCompound; + + expr.Emit (ec); + + if (isCompound) + ec.Emit (OpCodes.Dup); + + source.Emit (ec); + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temporary = new LocalTemporary (source.Type); + temporary.Store (ec); + } + + ec.EmitStoreFromPtr (type); + + if (temporary != null) { + temporary.Emit (ec); + temporary.Release (ec); + } + } + + public void AddressOf (EmitContext ec, AddressOp Mode) + { + expr.Emit (ec); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return DoResolve (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + if (!ec.IsUnsafe) + UnsafeError (ec, loc); + + var pc = expr.Type as PointerContainer; + + if (pc == null) { + ec.Report.Error (193, loc, "The * or -> operator must be applied to a pointer"); + return null; + } + + type = pc.Element; + + if (type.Kind == MemberKind.Void) { + Error_VoidPointerOperation (ec); + return null; + } + + eclass = ExprClass.Variable; + return this; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Unary Mutator expressions (pre and post ++ and --) + /// + /// + /// + /// UnaryMutator implements ++ and -- expressions. It derives from + /// ExpressionStatement becuase the pre/post increment/decrement + /// operators can be used in a statement context. + /// + /// FIXME: Idea, we could split this up in two classes, one simpler + /// for the common case, and one with the extra fields for more complex + /// classes (indexers require temporary access; overloaded require method) + /// + /// + public class UnaryMutator : ExpressionStatement + { + class DynamicPostMutator : Expression, IAssignMethod + { + LocalTemporary temp; + Expression expr; + + public DynamicPostMutator (Expression expr) + { + this.expr = expr; + this.type = expr.Type; + this.loc = expr.Location; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotImplementedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext rc) + { + eclass = expr.eclass; + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + expr.DoResolveLValue (ec, right_side); + return DoResolve (ec); + } + + public override void Emit (EmitContext ec) + { + temp.Emit (ec); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + // + // Emits target assignment using unmodified source value + // + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + // + // Allocate temporary variable to keep original value before it's modified + // + temp = new LocalTemporary (type); + expr.Emit (ec); + temp.Store (ec); + + ((IAssignMethod) expr).EmitAssign (ec, source, false, isCompound); + + if (leave_copy) + Emit (ec); + + temp.Release (ec); + temp = null; + } + } + + [Flags] + public enum Mode : byte { + IsIncrement = 0, + IsDecrement = 1, + IsPre = 0, + IsPost = 2, + + PreIncrement = 0, + PreDecrement = IsDecrement, + PostIncrement = IsPost, + PostDecrement = IsPost | IsDecrement + } + + Mode mode; + bool is_expr, recurse; + + protected Expression expr; + + // Holds the real operation + Expression operation; + + public UnaryMutator (Mode m, Expression e, Location loc) + { + mode = m; + this.loc = loc; + expr = e; + } + + public Mode UnaryMutatorMode { + get { + return mode; + } + } + + public Expression Expr { + get { + return expr; + } + } + + public override Location StartLocation { + get { + return (mode & Mode.IsPost) != 0 ? expr.Location : loc; + } + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return new SimpleAssign (this, this).CreateExpressionTree (ec); + } + + public static TypeSpec[] CreatePredefinedOperatorsTable (BuiltinTypes types) + { + // + // Predefined ++ and -- operators exist for the following types: + // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal + // + return new TypeSpec[] { + types.Int, + types.Long, + + types.SByte, + types.Byte, + types.Short, + types.UInt, + types.ULong, + types.Char, + types.Float, + types.Double, + types.Decimal + }; + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + + if (expr == null || expr.Type == InternalType.ErrorType) + return null; + + if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + // + // Handle postfix unary operators using local + // temporary variable + // + if ((mode & Mode.IsPost) != 0) + expr = new DynamicPostMutator (expr); + + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + return new SimpleAssign (expr, new DynamicUnaryConversion (GetOperatorExpressionTypeName (), args, loc)).Resolve (ec); + } + + if (expr.Type.IsNullableType) + return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec); + + return DoResolveOperation (ec); + } + + protected Expression DoResolveOperation (ResolveContext ec) + { + eclass = ExprClass.Value; + type = expr.Type; + + if (expr is RuntimeValueExpression) { + operation = expr; + } else { + // Use itself at the top of the stack + operation = new EmptyExpression (type); + } + + // + // The operand of the prefix/postfix increment decrement operators + // should be an expression that is classified as a variable, + // a property access or an indexer access + // + // TODO: Move to parser, expr is ATypeNameExpression + if (expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess) { + expr = expr.ResolveLValue (ec, expr); + } else { + ec.Report.Error (1059, loc, "The operand of an increment or decrement operator must be a variable, property or indexer"); + } + + // + // Step 1: Try to find a user operator, it has priority over predefined ones + // + var user_op = IsDecrement ? Operator.OpType.Decrement : Operator.OpType.Increment; + var methods = MemberCache.GetUserOperator (type, user_op, false); + + if (methods != null) { + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + + var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc); + var method = res.ResolveOperator (ec, ref args); + if (method == null) + return null; + + args[0].Expr = operation; + operation = new UserOperatorCall (method, args, null, loc); + operation = Convert.ImplicitConversionRequired (ec, operation, type, loc); + return this; + } + + // + // Step 2: Try predefined types + // + + Expression source = null; + bool primitive_type; + + // + // Predefined without user conversion first for speed-up + // + // Predefined ++ and -- operators exist for the following types: + // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal + // + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.Double: + case BuiltinTypeSpec.Type.Decimal: + source = operation; + primitive_type = true; + break; + default: + primitive_type = false; + + // ++/-- on pointer variables of all types except void* + if (type.IsPointer) { + if (((PointerContainer) type).Element.Kind == MemberKind.Void) { + Error_VoidPointerOperation (ec); + return null; + } + + source = operation; + } else { + Expression best_source = null; + foreach (var t in ec.BuiltinTypes.OperatorsUnaryMutator) { + source = Convert.ImplicitUserConversion (ec, operation, t, loc); + + // LAMESPEC: It should error on ambiguous operators but that would make us incompatible + if (source == null) + continue; + + if (best_source == null) { + best_source = source; + continue; + } + + var better = OverloadResolver.BetterTypeConversion (ec, best_source.Type, source.Type); + if (better == 1) + continue; + + if (better == 2) { + best_source = source; + continue; + } + + Unary.Error_Ambiguous (ec, OperName (mode), type, loc); + break; + } + + source = best_source; + } + + // ++/-- on enum types + if (source == null && type.IsEnum) + source = operation; + + if (source == null) { + expr.Error_OperatorCannotBeApplied (ec, loc, Operator.GetName (user_op), type); + return null; + } + + break; + } + + var one = new IntConstant (ec.BuiltinTypes, 1, loc); + var op = IsDecrement ? Binary.Operator.Subtraction : Binary.Operator.Addition; + operation = new Binary (op, source, one); + operation = operation.Resolve (ec); + if (operation == null) + throw new NotImplementedException ("should not be reached"); + + if (operation.Type != type) { + if (primitive_type) + operation = Convert.ExplicitNumericConversion (ec, operation, type); + else + operation = Convert.ImplicitConversionRequired (ec, operation, type, loc); + } + + return this; + } + + void EmitCode (EmitContext ec, bool is_expr) + { + recurse = true; + this.is_expr = is_expr; + ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true); + } + + public override void Emit (EmitContext ec) + { + // + // We use recurse to allow ourselfs to be the source + // of an assignment. This little hack prevents us from + // having to allocate another expression + // + if (recurse) { + ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement)); + + EmitOperation (ec); + + recurse = false; + return; + } + + EmitCode (ec, true); + } + + protected virtual void EmitOperation (EmitContext ec) + { + operation.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + EmitCode (ec, false); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + // + // Converts operator to System.Linq.Expressions.ExpressionType enum name + // + string GetOperatorExpressionTypeName () + { + return IsDecrement ? "Decrement" : "Increment"; + } + + bool IsDecrement { + get { return (mode & Mode.IsDecrement) != 0; } + } + + +#if NET_4_0 || MOBILE_DYNAMIC + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + var target = ((RuntimeValueExpression) expr).MetaObject.Expression; + var source = SLE.Expression.Convert (operation.MakeExpression (ctx), target.Type); + return SLE.Expression.Assign (target, source); + } +#endif + + public static string OperName (Mode oper) + { + return (oper & Mode.IsDecrement) != 0 ? "--" : "++"; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + UnaryMutator target = (UnaryMutator) t; + + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + // + // Base class for the `is' and `as' operators + // + public abstract class Probe : Expression + { + public Expression ProbeType; + protected Expression expr; + protected TypeSpec probe_type_expr; + + protected Probe (Expression expr, Expression probe_type, Location l) + { + ProbeType = probe_type; + loc = l; + this.expr = expr; + } + + public Expression Expr { + get { + return expr; + } + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + protected Expression ResolveCommon (ResolveContext rc) + { + expr = expr.Resolve (rc); + if (expr == null) + return null; + + ResolveProbeType (rc); + if (probe_type_expr == null) + return this; + + if (probe_type_expr.IsStatic) { + rc.Report.Error (7023, loc, "The second operand of `is' or `as' operator cannot be static type `{0}'", + probe_type_expr.GetSignatureForError ()); + return null; + } + + if (expr.Type.IsPointer || probe_type_expr.IsPointer) { + rc.Report.Error (244, loc, "The `{0}' operator cannot be applied to an operand of pointer type", + OperatorName); + return null; + } + + if (expr.Type == InternalType.AnonymousMethod || expr.Type == InternalType.MethodGroup) { + rc.Report.Error (837, loc, "The `{0}' operator cannot be applied to a lambda expression, anonymous method, or method group", + OperatorName); + return null; + } + + return this; + } + + protected virtual void ResolveProbeType (ResolveContext rc) + { + probe_type_expr = ProbeType.ResolveAsType (rc); + } + + public override void EmitSideEffect (EmitContext ec) + { + expr.EmitSideEffect (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + protected abstract string OperatorName { get; } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Probe target = (Probe) t; + + target.expr = expr.Clone (clonectx); + target.ProbeType = ProbeType.Clone (clonectx); + } + + } + + /// + /// Implementation of the `is' operator. + /// + public class Is : Probe + { + Nullable.Unwrap expr_unwrap; + MethodSpec number_mg; + Arguments number_args; + + public Is (Expression expr, Expression probe_type, Location l) + : base (expr, probe_type, l) + { + } + + protected override string OperatorName { + get { return "is"; } + } + + public LocalVariable Variable { get; set; } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (Variable != null) + throw new NotSupportedException (); + + Arguments args = Arguments.CreateForExpressionTree (ec, null, + expr.CreateExpressionTree (ec), + new TypeOf (probe_type_expr, loc)); + + return CreateExpressionFactoryCall (ec, "TypeIs", args); + } + + Expression CreateConstantResult (ResolveContext rc, bool result) + { + if (result) + rc.Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type", + probe_type_expr.GetSignatureForError ()); + else + rc.Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", + probe_type_expr.GetSignatureForError ()); + + var c = new BoolConstant (rc.BuiltinTypes, result, loc); + return expr.IsSideEffectFree ? + ReducedExpression.Create (c, this) : + new SideEffectConstant (c, this, loc); + } + + public override void Emit (EmitContext ec) + { + if (probe_type_expr == null) { + EmitConstantMatch (ec); + return; + } + + EmitLoad (ec); + + if (expr_unwrap == null) { + ec.EmitNull (); + ec.Emit (OpCodes.Cgt_Un); + } + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + if (probe_type_expr == null) { + EmitConstantMatch (ec); + } else { + EmitLoad (ec); + } + + ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target); + } + + void EmitConstantMatch (EmitContext ec) + { + var no_match = ec.DefineLabel (); + var end = ec.DefineLabel (); + + if (expr_unwrap != null) { + expr_unwrap.EmitCheck (ec); + + if (ProbeType.IsNull) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + return; + } + + ec.Emit (OpCodes.Brfalse_S, no_match); + expr_unwrap.Emit (ec); + ProbeType.Emit (ec); + ec.Emit (OpCodes.Ceq); + ec.Emit (OpCodes.Br_S, end); + ec.MarkLabel (no_match); + ec.EmitInt (0); + ec.MarkLabel (end); + return; + } + + if (number_args != null && number_args.Count == 3) { + var ce = new CallEmitter (); + ce.Emit (ec, number_mg, number_args, loc); + return; + } + + Expr.Emit (ec); + ec.Emit (OpCodes.Isinst, ProbeType.Type); + ec.Emit (OpCodes.Dup); + ec.Emit (OpCodes.Brfalse, no_match); + + if (number_mg != null) { + var ce = new CallEmitter (); + ce.Emit (ec, number_mg, number_args, loc); + } else { + ProbeType.Emit (ec); + ec.Emit (OpCodes.Ceq); + } + ec.Emit (OpCodes.Br_S, end); + ec.MarkLabel (no_match); + + ec.Emit (OpCodes.Pop); + ec.EmitInt (0); + ec.MarkLabel (end); + } + + void EmitLoad (EmitContext ec) + { + Label no_value_label = new Label (); + + if (expr_unwrap != null) { + expr_unwrap.EmitCheck (ec); + + if (Variable == null) + return; + + ec.Emit (OpCodes.Dup); + no_value_label = ec.DefineLabel (); + ec.Emit (OpCodes.Brfalse_S, no_value_label); + expr_unwrap.Emit (ec); + } else { + expr.Emit (ec); + + // Only to make verifier happy + if (probe_type_expr.IsGenericParameter && TypeSpec.IsValueType (expr.Type)) + ec.Emit (OpCodes.Box, expr.Type); + + ec.Emit (OpCodes.Isinst, probe_type_expr); + } + + if (Variable != null) { + bool value_on_stack; + if (probe_type_expr.IsGenericParameter || probe_type_expr.IsNullableType) { + ec.Emit (OpCodes.Dup); + ec.Emit (OpCodes.Unbox_Any, probe_type_expr); + value_on_stack = true; + } else { + value_on_stack = false; + } + + Variable.CreateBuilder (ec); + Variable.EmitAssign (ec); + + if (expr_unwrap != null) { + ec.MarkLabel (no_value_label); + } else if (!value_on_stack) { + Variable.Emit (ec); + } + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (ResolveCommon (rc) == null) + return null; + + type = rc.BuiltinTypes.Bool; + eclass = ExprClass.Value; + + if (probe_type_expr == null) + return ResolveMatchingExpression (rc); + + var res = ResolveResultExpression (rc); + if (Variable != null) { + if (res is Constant) + throw new NotImplementedException ("constant in type pattern matching"); + + Variable.Type = probe_type_expr; + var bc = rc as BlockContext; + if (bc != null) + Variable.PrepareAssignmentAnalysis (bc); + } + + return res; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + + if (Variable != null) + fc.SetVariableAssigned (Variable.VariableInfo, true); + } + + protected override void ResolveProbeType (ResolveContext rc) + { + if (!(ProbeType is TypeExpr) && rc.Module.Compiler.Settings.Version == LanguageVersion.Experimental) { + ProbeType = ProbeType.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup | ResolveFlags.Type); + if (ProbeType == null) + return; + + if (ProbeType.eclass == ExprClass.Type) { + probe_type_expr = ProbeType.Type; + } + + return; + } + + base.ResolveProbeType (rc); + } + + Expression ResolveMatchingExpression (ResolveContext rc) + { + var mc = ProbeType as Constant; + if (mc != null) { + if (!Convert.ImplicitConversionExists (rc, ProbeType, Expr.Type)) { + ProbeType.Error_ValueCannotBeConverted (rc, Expr.Type, false); + return null; + } + + if (mc.IsNull) + return new Binary (Binary.Operator.Equality, Expr, mc).Resolve (rc); + + var c = Expr as Constant; + if (c != null) { + c = ConstantFold.BinaryFold (rc, Binary.Operator.Equality, c, mc, loc); + if (c != null) + return c; + } + + if (Expr.Type.IsNullableType) { + expr_unwrap = new Nullable.Unwrap (Expr); + expr_unwrap.Resolve (rc); + } else if (ProbeType.Type.IsEnum || (ProbeType.Type.BuiltinType >= BuiltinTypeSpec.Type.Byte && ProbeType.Type.BuiltinType <= BuiltinTypeSpec.Type.Decimal)) { + var helper = rc.Module.CreatePatterMatchingHelper (); + number_mg = helper.NumberMatcher.Spec; + + // + // There are actually 3 arguments but the first one is already on the stack + // + number_args = new Arguments (3); + if (!ProbeType.Type.IsEnum) + number_args.Add (new Argument (Expr)); + + number_args.Add (new Argument (Convert.ImplicitConversion (rc, ProbeType, rc.BuiltinTypes.Object, loc))); + number_args.Add (new Argument (new BoolLiteral (rc.BuiltinTypes, ProbeType.Type.IsEnum, loc))); + } + + return this; + } + + throw new NotImplementedException (); + } + + Expression ResolveResultExpression (ResolveContext ec) + { + TypeSpec d = expr.Type; + bool d_is_nullable = false; + + // + // If E is a method group or the null literal, or if the type of E is a reference + // type or a nullable type and the value of E is null, the result is false + // + if (expr.IsNull || expr.eclass == ExprClass.MethodGroup) + return CreateConstantResult (ec, false); + + if (d.IsNullableType) { + var ut = Nullable.NullableInfo.GetUnderlyingType (d); + if (!ut.IsGenericParameter) { + d = ut; + d_is_nullable = true; + } + } + + TypeSpec t = probe_type_expr; + bool t_is_nullable = false; + if (t.IsNullableType) { + var ut = Nullable.NullableInfo.GetUnderlyingType (t); + if (!ut.IsGenericParameter) { + t = ut; + t_is_nullable = true; + } + } + + if (t.IsStruct) { + if (d == t) { + // + // D and T are the same value types but D can be null + // + if (d_is_nullable && !t_is_nullable) { + expr_unwrap = Nullable.Unwrap.Create (expr, true); + return this; + } + + // + // The result is true if D and T are the same value types + // + return CreateConstantResult (ec, true); + } + + var tp = d as TypeParameterSpec; + if (tp != null) + return ResolveGenericParameter (ec, t, tp); + + // + // An unboxing conversion exists + // + if (Convert.ExplicitReferenceConversionExists (d, t)) + return this; + + // + // open generic type + // + if (d is InflatedTypeSpec && InflatedTypeSpec.ContainsTypeParameter (d)) + return this; + } else { + var tps = t as TypeParameterSpec; + if (tps != null) + return ResolveGenericParameter (ec, d, tps); + + if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + ec.Report.Warning (1981, 3, loc, + "Using `{0}' to test compatibility with `{1}' is identical to testing compatibility with `object'", + OperatorName, t.GetSignatureForError ()); + } + + if (TypeManager.IsGenericParameter (d)) + return ResolveGenericParameter (ec, t, (TypeParameterSpec) d); + + if (TypeSpec.IsValueType (d)) { + if (Convert.ImplicitBoxingConversion (null, d, t) != null) { + if (d_is_nullable && !t_is_nullable) { + expr_unwrap = Nullable.Unwrap.Create (expr, false); + return this; + } + + return CreateConstantResult (ec, true); + } + } else { + if (Convert.ImplicitReferenceConversionExists (d, t)) { + var c = expr as Constant; + if (c != null) + return CreateConstantResult (ec, !c.IsNull); + + // + // Do not optimize for imported type or dynamic type + // + if (d.MemberDefinition.IsImported && d.BuiltinType != BuiltinTypeSpec.Type.None && + d.MemberDefinition.DeclaringAssembly != t.MemberDefinition.DeclaringAssembly) { + return this; + } + + if (d.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return this; + + // + // Turn is check into simple null check for implicitly convertible reference types + // + return ReducedExpression.Create ( + new Binary (Binary.Operator.Inequality, expr, new NullLiteral (loc)).Resolve (ec), + this).Resolve (ec); + } + + if (Convert.ExplicitReferenceConversionExists (d, t)) + return this; + + // + // open generic type + // + if ((d is InflatedTypeSpec || d.IsArray) && InflatedTypeSpec.ContainsTypeParameter (d)) + return this; + } + } + + return CreateConstantResult (ec, false); + } + + Expression ResolveGenericParameter (ResolveContext ec, TypeSpec d, TypeParameterSpec t) + { + if (t.IsReferenceType) { + if (d.IsStruct) + return CreateConstantResult (ec, false); + } + + if (expr.Type.IsGenericParameter) { + if (expr.Type == d && TypeSpec.IsValueType (t) && TypeSpec.IsValueType (d)) + return CreateConstantResult (ec, true); + + expr = new BoxedCast (expr, d); + } + + return this; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implementation of the `as' operator. + /// + public class As : Probe { + + public As (Expression expr, Expression probe_type, Location l) + : base (expr, probe_type, l) + { + } + + protected override string OperatorName { + get { return "as"; } + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = Arguments.CreateForExpressionTree (ec, null, + expr.CreateExpressionTree (ec), + new TypeOf (probe_type_expr, loc)); + + return CreateExpressionFactoryCall (ec, "TypeAs", args); + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + + ec.Emit (OpCodes.Isinst, type); + + if (TypeManager.IsGenericParameter (type) || type.IsNullableType) + ec.Emit (OpCodes.Unbox_Any, type); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (ResolveCommon (ec) == null) + return null; + + type = probe_type_expr; + eclass = ExprClass.Value; + TypeSpec etype = expr.Type; + + if (!TypeSpec.IsReferenceType (type) && !type.IsNullableType) { + if (TypeManager.IsGenericParameter (type)) { + ec.Report.Error (413, loc, + "The `as' operator cannot be used with a non-reference type parameter `{0}'. Consider adding `class' or a reference type constraint", + probe_type_expr.GetSignatureForError ()); + } else { + ec.Report.Error (77, loc, + "The `as' operator cannot be used with a non-nullable value type `{0}'", + type.GetSignatureForError ()); + } + return null; + } + + if (expr.IsNull && type.IsNullableType) { + return Nullable.LiftedNull.CreateFromExpression (ec, this); + } + + // If the compile-time type of E is dynamic, unlike the cast operator the as operator is not dynamically bound + if (etype.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + return this; + } + + Expression e = Convert.ImplicitConversionStandard (ec, expr, type, loc); + if (e != null) { + e = EmptyCast.Create (e, type); + return ReducedExpression.Create (e, this).Resolve (ec); + } + + if (Convert.ExplicitReferenceConversionExists (etype, type)){ + if (TypeManager.IsGenericParameter (etype)) + expr = new BoxedCast (expr, etype); + + return this; + } + + if (InflatedTypeSpec.ContainsTypeParameter (etype) || InflatedTypeSpec.ContainsTypeParameter (type)) { + expr = new BoxedCast (expr, etype); + return this; + } + + if (etype != InternalType.ErrorType) { + ec.Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion", + etype.GetSignatureForError (), type.GetSignatureForError ()); + } + + return null; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // This represents a typecast in the source language. + // + public class Cast : ShimExpression { + Expression target_type; + + public Cast (Expression cast_type, Expression expr, Location loc) + : base (expr) + { + this.target_type = cast_type; + this.loc = loc; + } + + public Expression TargetType { + get { return target_type; } + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + type = target_type.ResolveAsType (ec); + if (type == null) + return null; + + if (type.IsStatic) { + ec.Report.Error (716, loc, "Cannot convert to static type `{0}'", type.GetSignatureForError ()); + return null; + } + + if (type.IsPointer && !ec.IsUnsafe) { + UnsafeError (ec, loc); + } + + eclass = ExprClass.Value; + + Constant c = expr as Constant; + if (c != null) { + c = c.Reduce (ec, type); + if (c != null) + return c; + } + + var res = Convert.ExplicitConversion (ec, expr, type, loc); + if (res == expr) + return EmptyCast.Create (res, type); + + return res; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Cast target = (Cast) t; + + target.target_type = target_type.Clone (clonectx); + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ImplicitCast : ShimExpression + { + bool arrayAccess; + + public ImplicitCast (Expression expr, TypeSpec target, bool arrayAccess) + : base (expr) + { + this.loc = expr.Location; + this.type = target; + this.arrayAccess = arrayAccess; + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + if (arrayAccess) + expr = ConvertExpressionToArrayIndex (ec, expr); + else + expr = Convert.ImplicitConversionRequired (ec, expr, type, loc); + + return expr; + } + } + + // + // C# 2.0 Default value expression + // + public class DefaultValueExpression : Expression + { + Expression expr; + + public DefaultValueExpression (Expression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public Expression Expr { + get { + return this.expr; + } + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (type, loc))); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = expr.ResolveAsType (ec); + if (type == null) + return null; + + if (type.IsStatic) { + ec.Report.Error (-244, loc, "The `default value' operator cannot be applied to an operand of a static type"); + } + + if (type.IsPointer) + return new NullLiteral (Location).ConvertImplicitly (type); + + if (TypeSpec.IsReferenceType (type)) + return new NullConstant (type, loc); + + Constant c = New.Constantify (type, expr.Location); + if (c != null) + return c; + + eclass = ExprClass.Variable; + return this; + } + + public override void Emit (EmitContext ec) + { + LocalTemporary temp_storage = new LocalTemporary(type); + + temp_storage.AddressOf(ec, AddressOp.LoadStore); + ec.Emit(OpCodes.Initobj, type); + temp_storage.Emit(ec); + temp_storage.Release (ec); + } + +#if (NET_4_0 || MOBILE_DYNAMIC) && !STATIC + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return SLE.Expression.Default (type.GetMetaInfo ()); + } +#endif + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + DefaultValueExpression target = (DefaultValueExpression) t; + + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Binary operators + /// + public class Binary : Expression, IDynamicBinder + { + public class PredefinedOperator + { + protected readonly TypeSpec left; + protected readonly TypeSpec right; + protected readonly TypeSpec left_unwrap; + protected readonly TypeSpec right_unwrap; + public readonly Operator OperatorsMask; + public TypeSpec ReturnType; + + public PredefinedOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask) + : this (ltype, rtype, op_mask, ltype) + { + } + + public PredefinedOperator (TypeSpec type, Operator op_mask, TypeSpec return_type) + : this (type, type, op_mask, return_type) + { + } + + public PredefinedOperator (TypeSpec type, Operator op_mask) + : this (type, type, op_mask, type) + { + } + + public PredefinedOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec return_type) + { + if ((op_mask & Operator.ValuesOnlyMask) != 0) + throw new InternalErrorException ("Only masked values can be used"); + + if ((op_mask & Operator.NullableMask) != 0) { + left_unwrap = Nullable.NullableInfo.GetUnderlyingType (ltype); + right_unwrap = Nullable.NullableInfo.GetUnderlyingType (rtype); + } else { + left_unwrap = ltype; + right_unwrap = rtype; + } + + this.left = ltype; + this.right = rtype; + this.OperatorsMask = op_mask; + this.ReturnType = return_type; + } + + public bool IsLifted { + get { + return (OperatorsMask & Operator.NullableMask) != 0; + } + } + + public virtual Expression ConvertResult (ResolveContext rc, Binary b) + { + Constant c; + + var left_expr = b.left; + var right_expr = b.right; + + b.type = ReturnType; + + if (IsLifted) { + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + } + + if (right_expr.IsNull) { + if ((b.oper & Operator.EqualityMask) != 0) { + if (!left_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (left_expr.Type)) + return b.CreateLiftedValueTypeResult (rc, left_expr.Type); + } else if ((b.oper & Operator.BitwiseMask) != 0) { + if (left_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool) + return Nullable.LiftedNull.CreateFromExpression (rc, b); + } else { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + + if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, b); + + return b.CreateLiftedValueTypeResult (rc, left); + } + } else if (left_expr.IsNull) { + if ((b.oper & Operator.EqualityMask) != 0) { + if (!right_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (right_expr.Type)) + return b.CreateLiftedValueTypeResult (rc, right_expr.Type); + } else if ((b.oper & Operator.BitwiseMask) != 0) { + if (right_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool) + return Nullable.LiftedNull.CreateFromExpression (rc, b); + } else { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + + if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, b); + + return b.CreateLiftedValueTypeResult (rc, right); + } + } + } + + // + // A user operators does not support multiple user conversions, but decimal type + // is considered to be predefined type therefore we apply predefined operators rules + // and then look for decimal user-operator implementation + // + if (left.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + + return b.ResolveUserOperator (rc, b.left, b.right); + } + + c = right_expr as Constant; + if (c != null) { + if (c.IsDefaultValue) { + // + // Optimizes + // + // (expr + 0) to expr + // (expr - 0) to expr + // (bool? | false) to bool? + // + if (b.oper == Operator.Addition || b.oper == Operator.Subtraction || + (b.oper == Operator.BitwiseOr && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + return ReducedExpression.Create (b.left, b).Resolve (rc); + } + + // + // Optimizes (value &/&& 0) to 0 + // + if ((b.oper == Operator.BitwiseAnd || b.oper == Operator.LogicalAnd) && !IsLifted) { + Constant side_effect = new SideEffectConstant (c, b.left, c.Location); + return ReducedExpression.Create (side_effect, b); + } + } else { + // + // Optimizes (bool? & true) to bool? + // + if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) { + return ReducedExpression.Create (b.left, b).Resolve (rc); + } + } + + if ((b.oper == Operator.Multiply || b.oper == Operator.Division) && c.IsOneInteger) + return ReducedExpression.Create (b.left, b).Resolve (rc); + + if ((b.oper & Operator.ShiftMask) != 0 && c is IntConstant) { + b.right = new IntConstant (rc.BuiltinTypes, ((IntConstant) c).Value & GetShiftMask (left_unwrap), b.right.Location); + } + } + + c = b.left as Constant; + if (c != null) { + if (c.IsDefaultValue) { + // + // Optimizes + // + // (0 + expr) to expr + // (false | bool?) to bool? + // + if (b.oper == Operator.Addition || + (b.oper == Operator.BitwiseOr && right_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) { + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + return ReducedExpression.Create (b.right, b).Resolve (rc); + } + + // + // Optimizes (false && expr) to false + // + if (b.oper == Operator.LogicalAnd && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) { + // No rhs side-effects + Expression.Warning_UnreachableExpression (rc, b.right.StartLocation); + return ReducedExpression.Create (c, b); + } + + // + // Optimizes (0 & value) to 0 + // + if (b.oper == Operator.BitwiseAnd && !IsLifted) { + Constant side_effect = new SideEffectConstant (c, b.right, c.Location); + return ReducedExpression.Create (side_effect, b); + } + } else { + // + // Optimizes (true & bool?) to bool? + // + if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) { + return ReducedExpression.Create (b.right, b).Resolve (rc); + } + + // + // Optimizes (true || expr) to true + // + if (b.oper == Operator.LogicalOr && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) { + // No rhs side-effects + Expression.Warning_UnreachableExpression (rc, b.right.StartLocation); + return ReducedExpression.Create (c, b); + } + } + + if (b.oper == Operator.Multiply && c.IsOneInteger) + return ReducedExpression.Create (b.right, b).Resolve (rc); + } + + if (IsLifted) { + var lifted = new Nullable.LiftedBinaryOperator (b); + + TypeSpec ltype, rtype; + if (b.left.Type.IsNullableType) { + lifted.UnwrapLeft = new Nullable.Unwrap (b.left); + ltype = left_unwrap; + } else { + ltype = left; + } + + if (b.right.Type.IsNullableType) { + lifted.UnwrapRight = new Nullable.Unwrap (b.right); + rtype = right_unwrap; + } else { + rtype = right; + } + + lifted.Left = b.left.IsNull ? + b.left : + Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? b.left, ltype, b.left.Location); + + lifted.Right = b.right.IsNull ? + b.right : + Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? b.right, rtype, b.right.Location); + + return lifted.Resolve (rc); + } + + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + + return b; + } + + public bool IsPrimitiveApplicable (TypeSpec ltype, TypeSpec rtype) + { + // + // We are dealing with primitive types only + // + return left == ltype && ltype == rtype; + } + + public virtual bool IsApplicable (ResolveContext ec, Expression lexpr, Expression rexpr) + { + // Quick path + if (left == lexpr.Type && right == rexpr.Type) + return true; + + return Convert.ImplicitConversionExists (ec, lexpr, left) && + Convert.ImplicitConversionExists (ec, rexpr, right); + } + + public PredefinedOperator ResolveBetterOperator (ResolveContext ec, PredefinedOperator best_operator) + { + if ((OperatorsMask & Operator.DecomposedMask) != 0) + return best_operator; + + if ((best_operator.OperatorsMask & Operator.DecomposedMask) != 0) + return this; + + int result = 0; + if (left != null && best_operator.left != null) { + result = OverloadResolver.BetterTypeConversion (ec, best_operator.left_unwrap, left_unwrap); + } + + // + // When second argument is same as the first one, the result is same + // + if (right != null && (left != right || best_operator.left != best_operator.right)) { + result |= OverloadResolver.BetterTypeConversion (ec, best_operator.right_unwrap, right_unwrap); + } + + if (result == 0 || result > 2) + return null; + + return result == 1 ? best_operator : this; + } + } + + sealed class PredefinedStringOperator : PredefinedOperator + { + public PredefinedStringOperator (TypeSpec type, Operator op_mask, TypeSpec retType) + : base (type, type, op_mask, retType) + { + } + + public PredefinedStringOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec retType) + : base (ltype, rtype, op_mask, retType) + { + } + + public override Expression ConvertResult (ResolveContext ec, Binary b) + { + // + // Use original expression for nullable arguments + // + Nullable.Unwrap unwrap = b.left as Nullable.Unwrap; + if (unwrap != null) + b.left = unwrap.Original; + + unwrap = b.right as Nullable.Unwrap; + if (unwrap != null) + b.right = unwrap.Original; + + b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location); + + // + // Start a new concat expression using converted expression + // + return StringConcat.Create (ec, b.left, b.right, b.loc); + } + } + + sealed class PredefinedEqualityOperator : PredefinedOperator + { + MethodSpec equal_method, inequal_method; + + public PredefinedEqualityOperator (TypeSpec arg, TypeSpec retType) + : base (arg, arg, Operator.EqualityMask, retType) + { + } + + public override Expression ConvertResult (ResolveContext ec, Binary b) + { + b.type = ReturnType; + + b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location); + + Arguments args = new Arguments (2); + args.Add (new Argument (b.left)); + args.Add (new Argument (b.right)); + + MethodSpec method; + if (b.oper == Operator.Equality) { + if (equal_method == null) { + if (left.BuiltinType == BuiltinTypeSpec.Type.String) + equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (b.loc); + else if (left.BuiltinType == BuiltinTypeSpec.Type.Delegate) + equal_method = ec.Module.PredefinedMembers.DelegateEqual.Resolve (b.loc); + else + throw new NotImplementedException (left.GetSignatureForError ()); + } + + method = equal_method; + } else { + if (inequal_method == null) { + if (left.BuiltinType == BuiltinTypeSpec.Type.String) + inequal_method = ec.Module.PredefinedMembers.StringInequal.Resolve (b.loc); + else if (left.BuiltinType == BuiltinTypeSpec.Type.Delegate) + inequal_method = ec.Module.PredefinedMembers.DelegateInequal.Resolve (b.loc); + else + throw new NotImplementedException (left.GetSignatureForError ()); + } + + method = inequal_method; + } + + return new UserOperatorCall (method, args, b.CreateExpressionTree, b.loc); + } + } + + class PredefinedPointerOperator : PredefinedOperator + { + public PredefinedPointerOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask) + : base (ltype, rtype, op_mask) + { + } + + public PredefinedPointerOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec retType) + : base (ltype, rtype, op_mask, retType) + { + } + + public PredefinedPointerOperator (TypeSpec type, Operator op_mask, TypeSpec return_type) + : base (type, op_mask, return_type) + { + } + + public override bool IsApplicable (ResolveContext ec, Expression lexpr, Expression rexpr) + { + if (left == null) { + if (!lexpr.Type.IsPointer) + return false; + } else { + if (!Convert.ImplicitConversionExists (ec, lexpr, left)) + return false; + } + + if (right == null) { + if (!rexpr.Type.IsPointer) + return false; + } else { + if (!Convert.ImplicitConversionExists (ec, rexpr, right)) + return false; + } + + return true; + } + + public override Expression ConvertResult (ResolveContext ec, Binary b) + { + if (left != null) { + b.left = EmptyCast.Create (b.left, left); + } else if (right != null) { + b.right = EmptyCast.Create (b.right, right); + } + + TypeSpec r_type = ReturnType; + Expression left_arg, right_arg; + if (r_type == null) { + if (left == null) { + left_arg = b.left; + right_arg = b.right; + r_type = b.left.Type; + } else { + left_arg = b.right; + right_arg = b.left; + r_type = b.right.Type; + } + } else { + left_arg = b.left; + right_arg = b.right; + } + + return new PointerArithmetic (b.oper, left_arg, right_arg, r_type, b.loc).Resolve (ec); + } + } + + [Flags] + public enum Operator { + Multiply = 0 | ArithmeticMask, + Division = 1 | ArithmeticMask, + Modulus = 2 | ArithmeticMask, + Addition = 3 | ArithmeticMask | AdditionMask, + Subtraction = 4 | ArithmeticMask | SubtractionMask, + + LeftShift = 5 | ShiftMask, + RightShift = 6 | ShiftMask, + + LessThan = 7 | ComparisonMask | RelationalMask, + GreaterThan = 8 | ComparisonMask | RelationalMask, + LessThanOrEqual = 9 | ComparisonMask | RelationalMask, + GreaterThanOrEqual = 10 | ComparisonMask | RelationalMask, + Equality = 11 | ComparisonMask | EqualityMask, + Inequality = 12 | ComparisonMask | EqualityMask, + + BitwiseAnd = 13 | BitwiseMask, + ExclusiveOr = 14 | BitwiseMask, + BitwiseOr = 15 | BitwiseMask, + + LogicalAnd = 16 | LogicalMask, + LogicalOr = 17 | LogicalMask, + + // + // Operator masks + // + ValuesOnlyMask = ArithmeticMask - 1, + ArithmeticMask = 1 << 5, + ShiftMask = 1 << 6, + ComparisonMask = 1 << 7, + EqualityMask = 1 << 8, + BitwiseMask = 1 << 9, + LogicalMask = 1 << 10, + AdditionMask = 1 << 11, + SubtractionMask = 1 << 12, + RelationalMask = 1 << 13, + + DecomposedMask = 1 << 19, + NullableMask = 1 << 20, + } + + [Flags] + enum State : byte + { + None = 0, + Compound = 1 << 1, + } + + readonly Operator oper; + Expression left, right; + State state; + ConvCast.Mode enum_conversion; + + public Binary (Operator oper, Expression left, Expression right, bool isCompound) + : this (oper, left, right) + { + if (isCompound) + state |= State.Compound; + } + + public Binary (Operator oper, Expression left, Expression right) + { + this.oper = oper; + this.left = left; + this.right = right; + this.loc = left.Location; + } + + #region Properties + + public bool IsCompound { + get { + return (state & State.Compound) != 0; + } + } + + public Operator Oper { + get { + return oper; + } + } + + public Expression Left { + get { + return this.left; + } + } + + public Expression Right { + get { + return this.right; + } + } + + public override Location StartLocation { + get { + return left.StartLocation; + } + } + + #endregion + + /// + /// Returns a stringified representation of the Operator + /// + string OperName (Operator oper) + { + string s; + switch (oper){ + case Operator.Multiply: + s = "*"; + break; + case Operator.Division: + s = "/"; + break; + case Operator.Modulus: + s = "%"; + break; + case Operator.Addition: + s = "+"; + break; + case Operator.Subtraction: + s = "-"; + break; + case Operator.LeftShift: + s = "<<"; + break; + case Operator.RightShift: + s = ">>"; + break; + case Operator.LessThan: + s = "<"; + break; + case Operator.GreaterThan: + s = ">"; + break; + case Operator.LessThanOrEqual: + s = "<="; + break; + case Operator.GreaterThanOrEqual: + s = ">="; + break; + case Operator.Equality: + s = "=="; + break; + case Operator.Inequality: + s = "!="; + break; + case Operator.BitwiseAnd: + s = "&"; + break; + case Operator.BitwiseOr: + s = "|"; + break; + case Operator.ExclusiveOr: + s = "^"; + break; + case Operator.LogicalOr: + s = "||"; + break; + case Operator.LogicalAnd: + s = "&&"; + break; + default: + s = oper.ToString (); + break; + } + + if (IsCompound) + return s + "="; + + return s; + } + + public static void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right, Operator oper, Location loc) + { + new Binary (oper, left, right).Error_OperatorCannotBeApplied (ec, left, right); + } + + public static void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right, string oper, Location loc) + { + if (left.Type == InternalType.ErrorType || right.Type == InternalType.ErrorType) + return; + + string l, r; + l = left.Type.GetSignatureForError (); + r = right.Type.GetSignatureForError (); + + ec.Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'", + oper, l, r); + } + + void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right) + { + Error_OperatorCannotBeApplied (ec, left, right, OperName (oper), loc); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + // + // Optimized version when on-true/on-false data are not needed + // + if ((oper & Operator.LogicalMask) == 0) { + left.FlowAnalysis (fc); + right.FlowAnalysis (fc); + return; + } + + left.FlowAnalysisConditional (fc); + var left_fc_ontrue = fc.DefiniteAssignmentOnTrue; + var left_fc_onfalse = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet ( + oper == Operator.LogicalOr ? left_fc_onfalse : left_fc_ontrue); + right.FlowAnalysisConditional (fc); + + if (oper == Operator.LogicalOr) + fc.DefiniteAssignment = (left_fc_onfalse | (fc.DefiniteAssignmentOnFalse & fc.DefiniteAssignmentOnTrue)) & left_fc_ontrue; + else + fc.DefiniteAssignment = (left_fc_ontrue | (fc.DefiniteAssignmentOnFalse & fc.DefiniteAssignmentOnTrue)) & left_fc_onfalse; + } + + public override void FlowAnalysisConditional (FlowAnalysisContext fc) + { + if ((oper & Operator.LogicalMask) == 0) { + base.FlowAnalysisConditional (fc); + return; + } + + left.FlowAnalysisConditional (fc); + var left_fc_ontrue = fc.DefiniteAssignmentOnTrue; + var left_fc_onfalse = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet ( + oper == Operator.LogicalOr ? left_fc_onfalse : left_fc_ontrue); + right.FlowAnalysisConditional (fc); + + var lc = left as Constant; + if (oper == Operator.LogicalOr) { + fc.DefiniteAssignmentOnFalse = left_fc_onfalse | fc.DefiniteAssignmentOnFalse; + if (lc != null && lc.IsDefaultValue) + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse; + else + fc.DefiniteAssignmentOnTrue = new DefiniteAssignmentBitSet (left_fc_ontrue & (left_fc_onfalse | fc.DefiniteAssignmentOnTrue)); + } else { + fc.DefiniteAssignmentOnTrue = left_fc_ontrue | fc.DefiniteAssignmentOnTrue; + if (lc != null && !lc.IsDefaultValue) + fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignmentOnTrue; + else + fc.DefiniteAssignmentOnFalse = new DefiniteAssignmentBitSet ((left_fc_ontrue | fc.DefiniteAssignmentOnFalse) & left_fc_onfalse); + } + } + + // + // Converts operator to System.Linq.Expressions.ExpressionType enum name + // + string GetOperatorExpressionTypeName () + { + switch (oper) { + case Operator.Addition: + return IsCompound ? "AddAssign" : "Add"; + case Operator.BitwiseAnd: + return IsCompound ? "AndAssign" : "And"; + case Operator.BitwiseOr: + return IsCompound ? "OrAssign" : "Or"; + case Operator.Division: + return IsCompound ? "DivideAssign" : "Divide"; + case Operator.ExclusiveOr: + return IsCompound ? "ExclusiveOrAssign" : "ExclusiveOr"; + case Operator.Equality: + return "Equal"; + case Operator.GreaterThan: + return "GreaterThan"; + case Operator.GreaterThanOrEqual: + return "GreaterThanOrEqual"; + case Operator.Inequality: + return "NotEqual"; + case Operator.LeftShift: + return IsCompound ? "LeftShiftAssign" : "LeftShift"; + case Operator.LessThan: + return "LessThan"; + case Operator.LessThanOrEqual: + return "LessThanOrEqual"; + case Operator.LogicalAnd: + return "And"; + case Operator.LogicalOr: + return "Or"; + case Operator.Modulus: + return IsCompound ? "ModuloAssign" : "Modulo"; + case Operator.Multiply: + return IsCompound ? "MultiplyAssign" : "Multiply"; + case Operator.RightShift: + return IsCompound ? "RightShiftAssign" : "RightShift"; + case Operator.Subtraction: + return IsCompound ? "SubtractAssign" : "Subtract"; + default: + throw new NotImplementedException ("Unknown expression type operator " + oper.ToString ()); + } + } + + static CSharp.Operator.OpType ConvertBinaryToUserOperator (Operator op) + { + switch (op) { + case Operator.Addition: + return CSharp.Operator.OpType.Addition; + case Operator.BitwiseAnd: + case Operator.LogicalAnd: + return CSharp.Operator.OpType.BitwiseAnd; + case Operator.BitwiseOr: + case Operator.LogicalOr: + return CSharp.Operator.OpType.BitwiseOr; + case Operator.Division: + return CSharp.Operator.OpType.Division; + case Operator.Equality: + return CSharp.Operator.OpType.Equality; + case Operator.ExclusiveOr: + return CSharp.Operator.OpType.ExclusiveOr; + case Operator.GreaterThan: + return CSharp.Operator.OpType.GreaterThan; + case Operator.GreaterThanOrEqual: + return CSharp.Operator.OpType.GreaterThanOrEqual; + case Operator.Inequality: + return CSharp.Operator.OpType.Inequality; + case Operator.LeftShift: + return CSharp.Operator.OpType.LeftShift; + case Operator.LessThan: + return CSharp.Operator.OpType.LessThan; + case Operator.LessThanOrEqual: + return CSharp.Operator.OpType.LessThanOrEqual; + case Operator.Modulus: + return CSharp.Operator.OpType.Modulus; + case Operator.Multiply: + return CSharp.Operator.OpType.Multiply; + case Operator.RightShift: + return CSharp.Operator.OpType.RightShift; + case Operator.Subtraction: + return CSharp.Operator.OpType.Subtraction; + default: + throw new InternalErrorException (op.ToString ()); + } + } + + public override bool ContainsEmitWithAwait () + { + return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait (); + } + + public static void EmitOperatorOpcode (EmitContext ec, Operator oper, TypeSpec l, Expression right) + { + OpCode opcode; + + switch (oper){ + case Operator.Multiply: + if (ec.HasSet (EmitContext.Options.CheckedScope)) { + if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long) + opcode = OpCodes.Mul_Ovf; + else if (!IsFloat (l)) + opcode = OpCodes.Mul_Ovf_Un; + else + opcode = OpCodes.Mul; + } else + opcode = OpCodes.Mul; + + break; + + case Operator.Division: + if (IsUnsigned (l)) + opcode = OpCodes.Div_Un; + else + opcode = OpCodes.Div; + break; + + case Operator.Modulus: + if (IsUnsigned (l)) + opcode = OpCodes.Rem_Un; + else + opcode = OpCodes.Rem; + break; + + case Operator.Addition: + if (ec.HasSet (EmitContext.Options.CheckedScope)) { + if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long) + opcode = OpCodes.Add_Ovf; + else if (!IsFloat (l)) + opcode = OpCodes.Add_Ovf_Un; + else + opcode = OpCodes.Add; + } else + opcode = OpCodes.Add; + break; + + case Operator.Subtraction: + if (ec.HasSet (EmitContext.Options.CheckedScope)) { + if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long) + opcode = OpCodes.Sub_Ovf; + else if (!IsFloat (l)) + opcode = OpCodes.Sub_Ovf_Un; + else + opcode = OpCodes.Sub; + } else + opcode = OpCodes.Sub; + break; + + case Operator.RightShift: + if (!(right is IntConstant)) { + ec.EmitInt (GetShiftMask (l)); + ec.Emit (OpCodes.And); + } + + if (IsUnsigned (l)) + opcode = OpCodes.Shr_Un; + else + opcode = OpCodes.Shr; + break; + + case Operator.LeftShift: + if (!(right is IntConstant)) { + ec.EmitInt (GetShiftMask (l)); + ec.Emit (OpCodes.And); + } + + opcode = OpCodes.Shl; + break; + + case Operator.Equality: + opcode = OpCodes.Ceq; + break; + + case Operator.Inequality: + ec.Emit (OpCodes.Ceq); + ec.EmitInt (0); + + opcode = OpCodes.Ceq; + break; + + case Operator.LessThan: + if (IsUnsigned (l)) + opcode = OpCodes.Clt_Un; + else + opcode = OpCodes.Clt; + break; + + case Operator.GreaterThan: + if (IsUnsigned (l)) + opcode = OpCodes.Cgt_Un; + else + opcode = OpCodes.Cgt; + break; + + case Operator.LessThanOrEqual: + if (IsUnsigned (l) || IsFloat (l)) + ec.Emit (OpCodes.Cgt_Un); + else + ec.Emit (OpCodes.Cgt); + ec.EmitInt (0); + + opcode = OpCodes.Ceq; + break; + + case Operator.GreaterThanOrEqual: + if (IsUnsigned (l) || IsFloat (l)) + ec.Emit (OpCodes.Clt_Un); + else + ec.Emit (OpCodes.Clt); + + ec.EmitInt (0); + + opcode = OpCodes.Ceq; + break; + + case Operator.BitwiseOr: + opcode = OpCodes.Or; + break; + + case Operator.BitwiseAnd: + opcode = OpCodes.And; + break; + + case Operator.ExclusiveOr: + opcode = OpCodes.Xor; + break; + + default: + throw new InternalErrorException (oper.ToString ()); + } + + ec.Emit (opcode); + } + + static int GetShiftMask (TypeSpec type) + { + return type.BuiltinType == BuiltinTypeSpec.Type.Int || type.BuiltinType == BuiltinTypeSpec.Type.UInt ? 0x1f : 0x3f; + } + + static bool IsUnsigned (TypeSpec t) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Byte: + return true; + } + + return t.IsPointer; + } + + static bool IsFloat (TypeSpec t) + { + return t.BuiltinType == BuiltinTypeSpec.Type.Float || t.BuiltinType == BuiltinTypeSpec.Type.Double; + } + + public Expression ResolveOperator (ResolveContext rc) + { + eclass = ExprClass.Value; + + TypeSpec l = left.Type; + TypeSpec r = right.Type; + Expression expr; + bool primitives_only = false; + + // + // Handles predefined primitive types + // + if ((BuiltinTypeSpec.IsPrimitiveType (l) || (l.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (l)))) && + (BuiltinTypeSpec.IsPrimitiveType (r) || (r.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (r))))) { + if ((oper & Operator.ShiftMask) == 0) { + if (!DoBinaryOperatorPromotion (rc)) + return null; + + primitives_only = BuiltinTypeSpec.IsPrimitiveType (l) && BuiltinTypeSpec.IsPrimitiveType (r); + } + } else { + // Pointers + if (l.IsPointer || r.IsPointer) + return ResolveOperatorPointer (rc, l, r); + + // User operators + expr = ResolveUserOperator (rc, left, right); + if (expr != null) + return expr; + + + bool lenum = l.IsEnum; + bool renum = r.IsEnum; + if ((oper & (Operator.ComparisonMask | Operator.BitwiseMask)) != 0) { + // + // Enumerations + // + if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) { + expr = ResolveSingleEnumOperators (rc, lenum, renum, l, r); + + if (expr == null) + return null; + + if ((oper & Operator.BitwiseMask) != 0) { + expr = EmptyCast.Create (expr, type); + enum_conversion = GetEnumResultCast (type); + + if (oper == Operator.BitwiseAnd && left.Type.IsEnum && right.Type.IsEnum) { + expr = OptimizeAndOperation (expr); + } + } + + left = ConvertEnumOperandToUnderlyingType (rc, left, r.IsNullableType); + right = ConvertEnumOperandToUnderlyingType (rc, right, l.IsNullableType); + return expr; + } + } else if ((oper == Operator.Addition || oper == Operator.Subtraction)) { + if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) { + // + // Enumerations + // + expr = ResolveEnumOperators (rc, lenum, renum, l, r); + + // + // We cannot break here there is also Enum + String possible match + // which is not ambiguous with predefined enum operators + // + if (expr != null) { + left = ConvertEnumOperandToUnderlyingType (rc, left, false); + right = ConvertEnumOperandToUnderlyingType (rc, right, false); + + return expr; + } + } else if (l.IsDelegate || r.IsDelegate) { + // + // Delegates + // + expr = ResolveOperatorDelegate (rc, l, r); + + // TODO: Can this be ambiguous + if (expr != null) + return expr; + } + } + } + + // + // Equality operators are more complicated + // + if ((oper & Operator.EqualityMask) != 0) { + return ResolveEquality (rc, l, r, primitives_only); + } + + expr = ResolveOperatorPredefined (rc, rc.BuiltinTypes.OperatorsBinaryStandard, primitives_only); + if (expr != null) + return expr; + + if (primitives_only) + return null; + + // + // Lifted operators have lower priority + // + return ResolveOperatorPredefined (rc, rc.Module.OperatorsBinaryLifted, false); + } + + static bool IsEnumOrNullableEnum (TypeSpec type) + { + return type.IsEnum || (type.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (type).IsEnum); + } + + + // at least one of 'left' or 'right' is an enumeration constant (EnumConstant or SideEffectConstant or ...) + // if 'left' is not an enumeration constant, create one from the type of 'right' + Constant EnumLiftUp (ResolveContext ec, Constant left, Constant right) + { + switch (oper) { + case Operator.BitwiseOr: + case Operator.BitwiseAnd: + case Operator.ExclusiveOr: + case Operator.Equality: + case Operator.Inequality: + case Operator.LessThan: + case Operator.LessThanOrEqual: + case Operator.GreaterThan: + case Operator.GreaterThanOrEqual: + if (left.Type.IsEnum) + return left; + + if (left.IsZeroInteger) + return left.Reduce (ec, right.Type); + + break; + + case Operator.Addition: + case Operator.Subtraction: + return left; + + case Operator.Multiply: + case Operator.Division: + case Operator.Modulus: + case Operator.LeftShift: + case Operator.RightShift: + if (right.Type.IsEnum || left.Type.IsEnum) + break; + return left; + } + + return null; + } + + // + // The `|' operator used on types which were extended is dangerous + // + void CheckBitwiseOrOnSignExtended (ResolveContext ec) + { + OpcodeCast lcast = left as OpcodeCast; + if (lcast != null) { + if (IsUnsigned (lcast.UnderlyingType)) + lcast = null; + } + + OpcodeCast rcast = right as OpcodeCast; + if (rcast != null) { + if (IsUnsigned (rcast.UnderlyingType)) + rcast = null; + } + + if (lcast == null && rcast == null) + return; + + // FIXME: consider constants + + var ltype = lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType; + ec.Report.Warning (675, 3, loc, + "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first", + ltype.GetSignatureForError ()); + } + + public static PredefinedOperator[] CreatePointerOperatorsTable (BuiltinTypes types) + { + return new PredefinedOperator[] { + // + // Pointer arithmetic: + // + // T* operator + (T* x, int y); T* operator - (T* x, int y); + // T* operator + (T* x, uint y); T* operator - (T* x, uint y); + // T* operator + (T* x, long y); T* operator - (T* x, long y); + // T* operator + (T* x, ulong y); T* operator - (T* x, ulong y); + // + new PredefinedPointerOperator (null, types.Int, Operator.AdditionMask | Operator.SubtractionMask), + new PredefinedPointerOperator (null, types.UInt, Operator.AdditionMask | Operator.SubtractionMask), + new PredefinedPointerOperator (null, types.Long, Operator.AdditionMask | Operator.SubtractionMask), + new PredefinedPointerOperator (null, types.ULong, Operator.AdditionMask | Operator.SubtractionMask), + + // + // T* operator + (int y, T* x); + // T* operator + (uint y, T *x); + // T* operator + (long y, T *x); + // T* operator + (ulong y, T *x); + // + new PredefinedPointerOperator (types.Int, null, Operator.AdditionMask, null), + new PredefinedPointerOperator (types.UInt, null, Operator.AdditionMask, null), + new PredefinedPointerOperator (types.Long, null, Operator.AdditionMask, null), + new PredefinedPointerOperator (types.ULong, null, Operator.AdditionMask, null), + + // + // long operator - (T* x, T *y) + // + new PredefinedPointerOperator (null, Operator.SubtractionMask, types.Long) + }; + } + + public static PredefinedOperator[] CreateStandardOperatorsTable (BuiltinTypes types) + { + TypeSpec bool_type = types.Bool; + + return new [] { + new PredefinedOperator (types.Int, Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask), + new PredefinedOperator (types.UInt, Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (types.Long, Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (types.ULong, Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (types.Float, Operator.ArithmeticMask), + new PredefinedOperator (types.Double, Operator.ArithmeticMask), + new PredefinedOperator (types.Decimal, Operator.ArithmeticMask), + + new PredefinedOperator (types.Int, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.UInt, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.Long, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.ULong, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.Float, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.Double, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.Decimal, Operator.ComparisonMask, bool_type), + + new PredefinedStringOperator (types.String, Operator.AdditionMask, types.String), + // Remaining string operators are in lifted tables + + new PredefinedOperator (bool_type, Operator.BitwiseMask | Operator.LogicalMask | Operator.EqualityMask, bool_type), + + new PredefinedOperator (types.UInt, types.Int, Operator.ShiftMask), + new PredefinedOperator (types.Long, types.Int, Operator.ShiftMask), + new PredefinedOperator (types.ULong, types.Int, Operator.ShiftMask) + }; + + } + public static PredefinedOperator[] CreateStandardLiftedOperatorsTable (ModuleContainer module) + { + var types = module.Compiler.BuiltinTypes; + + // + // Not strictly lifted but need to be in second group otherwise expressions like + // int + null would resolve to +(object, string) instead of +(int?, int?) + // + var string_operators = new [] { + new PredefinedStringOperator (types.String, types.Object, Operator.AdditionMask, types.String), + new PredefinedStringOperator (types.Object, types.String, Operator.AdditionMask, types.String), + }; + + var nullable = module.PredefinedTypes.Nullable.TypeSpec; + if (nullable == null) + return string_operators; + + var bool_type = types.Bool; + + var nullable_bool = nullable.MakeGenericType (module, new[] { bool_type }); + var nullable_int = nullable.MakeGenericType (module, new[] { types.Int }); + var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt }); + var nullable_long = nullable.MakeGenericType (module, new[] { types.Long }); + var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong }); + var nullable_float = nullable.MakeGenericType (module, new[] { types.Float }); + var nullable_double = nullable.MakeGenericType (module, new[] { types.Double }); + var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal }); + + return new[] { + new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask), + new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ArithmeticMask), + new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ArithmeticMask), + new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ArithmeticMask), + + new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ComparisonMask, bool_type), + + new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.BitwiseMask, nullable_bool), + + new PredefinedOperator (nullable_uint, nullable_int, Operator.NullableMask | Operator.ShiftMask), + new PredefinedOperator (nullable_long, nullable_int, Operator.NullableMask | Operator.ShiftMask), + new PredefinedOperator (nullable_ulong, nullable_int, Operator.NullableMask | Operator.ShiftMask), + + string_operators [0], + string_operators [1] + }; + } + + public static PredefinedOperator[] CreateEqualityOperatorsTable (BuiltinTypes types) + { + TypeSpec bool_type = types.Bool; + + return new[] { + new PredefinedEqualityOperator (types.String, bool_type), + new PredefinedEqualityOperator (types.Delegate, bool_type), + new PredefinedOperator (bool_type, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Int, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.UInt, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Long, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.ULong, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Float, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Double, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Decimal, Operator.EqualityMask, bool_type), + }; + } + + public static PredefinedOperator[] CreateEqualityLiftedOperatorsTable (ModuleContainer module) + { + var nullable = module.PredefinedTypes.Nullable.TypeSpec; + + if (nullable == null) + return new PredefinedOperator [0]; + + var types = module.Compiler.BuiltinTypes; + var bool_type = types.Bool; + var nullable_bool = nullable.MakeGenericType (module, new [] { bool_type }); + var nullable_int = nullable.MakeGenericType (module, new[] { types.Int }); + var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt }); + var nullable_long = nullable.MakeGenericType (module, new[] { types.Long }); + var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong }); + var nullable_float = nullable.MakeGenericType (module, new[] { types.Float }); + var nullable_double = nullable.MakeGenericType (module, new[] { types.Double }); + var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal }); + + return new [] { + new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.EqualityMask, bool_type) + }; + } + + // + // 7.2.6.2 Binary numeric promotions + // + bool DoBinaryOperatorPromotion (ResolveContext rc) + { + TypeSpec ltype = left.Type; + if (ltype.IsNullableType) { + ltype = Nullable.NullableInfo.GetUnderlyingType (ltype); + } + + // + // This is numeric promotion code only + // + if (ltype.BuiltinType == BuiltinTypeSpec.Type.Bool) + return true; + + TypeSpec rtype = right.Type; + if (rtype.IsNullableType) { + rtype = Nullable.NullableInfo.GetUnderlyingType (rtype); + } + + var lb = ltype.BuiltinType; + var rb = rtype.BuiltinType; + TypeSpec type; + Expression expr; + + if (lb == BuiltinTypeSpec.Type.Decimal || rb == BuiltinTypeSpec.Type.Decimal) { + type = rc.BuiltinTypes.Decimal; + } else if (lb == BuiltinTypeSpec.Type.Double || rb == BuiltinTypeSpec.Type.Double) { + type = rc.BuiltinTypes.Double; + } else if (lb == BuiltinTypeSpec.Type.Float || rb == BuiltinTypeSpec.Type.Float) { + type = rc.BuiltinTypes.Float; + } else if (lb == BuiltinTypeSpec.Type.ULong || rb == BuiltinTypeSpec.Type.ULong) { + type = rc.BuiltinTypes.ULong; + + if (IsSignedType (lb)) { + expr = ConvertSignedConstant (left, type); + if (expr == null) + return false; + left = expr; + } else if (IsSignedType (rb)) { + expr = ConvertSignedConstant (right, type); + if (expr == null) + return false; + right = expr; + } + + } else if (lb == BuiltinTypeSpec.Type.Long || rb == BuiltinTypeSpec.Type.Long) { + type = rc.BuiltinTypes.Long; + } else if (lb == BuiltinTypeSpec.Type.UInt || rb == BuiltinTypeSpec.Type.UInt) { + type = rc.BuiltinTypes.UInt; + + if (IsSignedType (lb)) { + expr = ConvertSignedConstant (left, type); + if (expr == null) + type = rc.BuiltinTypes.Long; + } else if (IsSignedType (rb)) { + expr = ConvertSignedConstant (right, type); + if (expr == null) + type = rc.BuiltinTypes.Long; + } + } else { + type = rc.BuiltinTypes.Int; + } + + if (ltype != type) { + expr = PromoteExpression (rc, left, type); + if (expr == null) + return false; + + left = expr; + } + + if (rtype != type) { + expr = PromoteExpression (rc, right, type); + if (expr == null) + return false; + + right = expr; + } + + return true; + } + + static bool IsSignedType (BuiltinTypeSpec.Type type) + { + switch (type) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Long: + return true; + default: + return false; + } + } + + static Expression ConvertSignedConstant (Expression expr, TypeSpec type) + { + var c = expr as Constant; + if (c == null) + return null; + + return c.ConvertImplicitly (type); + } + + static Expression PromoteExpression (ResolveContext rc, Expression expr, TypeSpec type) + { + if (expr.Type.IsNullableType) { + return Convert.ImplicitConversionStandard (rc, expr, + rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc, new[] { type }), expr.Location); + } + + var c = expr as Constant; + if (c != null) + return c.ConvertImplicitly (type); + + return Convert.ImplicitNumericConversion (expr, type); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (left == null) + return null; + + if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) { + left = ((ParenthesizedExpression) left).Expr; + left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type); + if (left == null) + return null; + + if (left.eclass == ExprClass.Type) { + ec.Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses"); + return null; + } + } else + left = left.Resolve (ec); + + if (left == null) + return null; + + right = right.Resolve (ec); + if (right == null) + return null; + + Constant lc = left as Constant; + Constant rc = right as Constant; + + // The conversion rules are ignored in enum context but why + if (!ec.HasSet (ResolveContext.Options.EnumScope) && lc != null && rc != null && (left.Type.IsEnum || right.Type.IsEnum)) { + lc = EnumLiftUp (ec, lc, rc); + if (lc != null) + rc = EnumLiftUp (ec, rc, lc); + } + + if (rc != null && lc != null) { + int prev_e = ec.Report.Errors; + Expression e = ConstantFold.BinaryFold (ec, oper, lc, rc, loc); + if (e != null || ec.Report.Errors != prev_e) + return e; + } + + // Comparison warnings + if ((oper & Operator.ComparisonMask) != 0) { + if (left.Equals (right)) { + ec.Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?"); + } + CheckOutOfRangeComparison (ec, lc, right.Type); + CheckOutOfRangeComparison (ec, rc, left.Type); + } + + if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic || right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return DoResolveDynamic (ec); + + return DoResolveCore (ec, left, right); + } + + Expression DoResolveDynamic (ResolveContext rc) + { + var lt = left.Type; + var rt = right.Type; + if (lt.Kind == MemberKind.Void || lt == InternalType.MethodGroup || lt == InternalType.AnonymousMethod || + rt.Kind == MemberKind.Void || rt == InternalType.MethodGroup || rt == InternalType.AnonymousMethod) { + Error_OperatorCannotBeApplied (rc, left, right); + return null; + } + + Arguments args; + + // + // Special handling for logical boolean operators which require rhs not to be + // evaluated based on lhs value + // + if ((oper & Operator.LogicalMask) != 0) { + Expression cond_left, cond_right, expr; + + args = new Arguments (2); + + if (lt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + LocalVariable temp = LocalVariable.CreateCompilerGenerated (lt, rc.CurrentBlock, loc); + + var cond_args = new Arguments (1); + cond_args.Add (new Argument (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left).Resolve (rc))); + + // + // dynamic && bool => IsFalse (temp = left) ? temp : temp && right; + // dynamic || bool => IsTrue (temp = left) ? temp : temp || right; + // + left = temp.CreateReferenceExpression (rc, loc); + if (oper == Operator.LogicalAnd) { + expr = DynamicUnaryConversion.CreateIsFalse (rc, cond_args, loc); + cond_left = left; + } else { + expr = DynamicUnaryConversion.CreateIsTrue (rc, cond_args, loc); + cond_left = left; + } + + args.Add (new Argument (left)); + args.Add (new Argument (right)); + cond_right = new DynamicExpressionStatement (this, args, loc); + } else { + LocalVariable temp = LocalVariable.CreateCompilerGenerated (rc.BuiltinTypes.Bool, rc.CurrentBlock, loc); + + if (!Convert.ImplicitConversionExists (rc, left, temp.Type) && (oper == Operator.LogicalAnd ? GetOperatorFalse (rc, left, loc) : GetOperatorTrue (rc, left, loc)) == null) { + rc.Report.Error (7083, left.Location, + "Expression must be implicitly convertible to Boolean or its type `{0}' must define operator `{1}'", + lt.GetSignatureForError (), oper == Operator.LogicalAnd ? "false" : "true"); + return null; + } + + args.Add (new Argument (temp.CreateReferenceExpression (rc, loc).Resolve (rc))); + args.Add (new Argument (right)); + right = new DynamicExpressionStatement (this, args, loc); + + // + // bool && dynamic => (temp = left) ? temp && right : temp; + // bool || dynamic => (temp = left) ? temp : temp || right; + // + if (oper == Operator.LogicalAnd) { + cond_left = right; + cond_right = temp.CreateReferenceExpression (rc, loc); + } else { + cond_left = temp.CreateReferenceExpression (rc, loc); + cond_right = right; + } + + expr = new BooleanExpression (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left)); + } + + return new Conditional (expr, cond_left, cond_right, loc).Resolve (rc); + } + + args = new Arguments (2); + args.Add (new Argument (left)); + args.Add (new Argument (right)); + return new DynamicExpressionStatement (this, args, loc).Resolve (rc); + } + + Expression DoResolveCore (ResolveContext ec, Expression left_orig, Expression right_orig) + { + Expression expr = ResolveOperator (ec); + if (expr == null) + Error_OperatorCannotBeApplied (ec, left_orig, right_orig); + + if (left == null || right == null) + throw new InternalErrorException ("Invalid conversion"); + + if (oper == Operator.BitwiseOr) + CheckBitwiseOrOnSignExtended (ec); + + return expr; + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return MakeExpression (ctx, left, right); + } + + public SLE.Expression MakeExpression (BuilderContext ctx, Expression left, Expression right) + { + var le = left.MakeExpression (ctx); + var re = right.MakeExpression (ctx); + bool is_checked = ctx.HasSet (BuilderContext.Options.CheckedScope); + + switch (oper) { + case Operator.Addition: + return is_checked ? SLE.Expression.AddChecked (le, re) : SLE.Expression.Add (le, re); + case Operator.BitwiseAnd: + return SLE.Expression.And (le, re); + case Operator.BitwiseOr: + return SLE.Expression.Or (le, re); + case Operator.Division: + return SLE.Expression.Divide (le, re); + case Operator.Equality: + return SLE.Expression.Equal (le, re); + case Operator.ExclusiveOr: + return SLE.Expression.ExclusiveOr (le, re); + case Operator.GreaterThan: + return SLE.Expression.GreaterThan (le, re); + case Operator.GreaterThanOrEqual: + return SLE.Expression.GreaterThanOrEqual (le, re); + case Operator.Inequality: + return SLE.Expression.NotEqual (le, re); + case Operator.LeftShift: + return SLE.Expression.LeftShift (le, re); + case Operator.LessThan: + return SLE.Expression.LessThan (le, re); + case Operator.LessThanOrEqual: + return SLE.Expression.LessThanOrEqual (le, re); + case Operator.LogicalAnd: + return SLE.Expression.AndAlso (le, re); + case Operator.LogicalOr: + return SLE.Expression.OrElse (le, re); + case Operator.Modulus: + return SLE.Expression.Modulo (le, re); + case Operator.Multiply: + return is_checked ? SLE.Expression.MultiplyChecked (le, re) : SLE.Expression.Multiply (le, re); + case Operator.RightShift: + return SLE.Expression.RightShift (le, re); + case Operator.Subtraction: + return is_checked ? SLE.Expression.SubtractChecked (le, re) : SLE.Expression.Subtract (le, re); + default: + throw new NotImplementedException (oper.ToString ()); + } + } + + // + // D operator + (D x, D y) + // D operator - (D x, D y) + // + Expression ResolveOperatorDelegate (ResolveContext ec, TypeSpec l, TypeSpec r) + { + if (l != r && !TypeSpecComparer.Variant.IsEqual (r, l)) { + Expression tmp; + if (right.eclass == ExprClass.MethodGroup || r == InternalType.AnonymousMethod || r == InternalType.NullLiteral) { + tmp = Convert.ImplicitConversionRequired (ec, right, l, loc); + if (tmp == null) + return null; + right = tmp; + r = right.Type; + } else if (left.eclass == ExprClass.MethodGroup || (l == InternalType.AnonymousMethod || l == InternalType.NullLiteral)) { + tmp = Convert.ImplicitConversionRequired (ec, left, r, loc); + if (tmp == null) + return null; + left = tmp; + l = left.Type; + } else { + return null; + } + } + + MethodSpec method = null; + Arguments args = new Arguments (2); + args.Add (new Argument (left)); + args.Add (new Argument (right)); + + if (oper == Operator.Addition) { + method = ec.Module.PredefinedMembers.DelegateCombine.Resolve (loc); + } else if (oper == Operator.Subtraction) { + method = ec.Module.PredefinedMembers.DelegateRemove.Resolve (loc); + } + + if (method == null) + return new EmptyExpression (ec.BuiltinTypes.Decimal); + + Expression expr = new UserOperatorCall (method, args, CreateExpressionTree, loc); + return new ClassCast (expr, l); + } + + // + // Resolves enumeration operators where only single predefined overload exists, handles lifted versions too + // + Expression ResolveSingleEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype) + { + // + // bool operator == (E x, E y); + // bool operator != (E x, E y); + // bool operator < (E x, E y); + // bool operator > (E x, E y); + // bool operator <= (E x, E y); + // bool operator >= (E x, E y); + // + // E operator & (E x, E y); + // E operator | (E x, E y); + // E operator ^ (E x, E y); + // + Expression expr; + if ((oper & Operator.ComparisonMask) != 0) { + type = rc.BuiltinTypes.Bool; + } else { + if (lenum) + type = ltype; + else if (renum) + type = rtype; + else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) + type = ltype; + else + type = rtype; + } + + if (ltype == rtype) { + if (lenum || renum) + return this; + + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = left; + lifted.Right = right; + return lifted.Resolve (rc); + } + + if (renum && !ltype.IsNullableType) { + expr = Convert.ImplicitConversion (rc, left, rtype, loc); + if (expr != null) { + left = expr; + return this; + } + } else if (lenum && !rtype.IsNullableType) { + expr = Convert.ImplicitConversion (rc, right, ltype, loc); + if (expr != null) { + right = expr; + return this; + } + } + + // + // Now try lifted version of predefined operator + // + var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec; + if (nullable_type != null) { + if (renum && !ltype.IsNullableType) { + var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { rtype }); + + expr = Convert.ImplicitConversion (rc, left, lifted_type, loc); + if (expr != null) { + left = expr; + right = Convert.ImplicitConversion (rc, right, lifted_type, loc); + } + + if ((oper & Operator.BitwiseMask) != 0) + type = lifted_type; + + if (left.IsNull) { + if ((oper & Operator.BitwiseMask) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, this); + + return CreateLiftedValueTypeResult (rc, rtype); + } + + if (expr != null) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = expr; + lifted.Right = right; + return lifted.Resolve (rc); + } + } else if (lenum && !rtype.IsNullableType) { + var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { ltype }); + + expr = Convert.ImplicitConversion (rc, right, lifted_type, loc); + if (expr != null) { + right = expr; + left = Convert.ImplicitConversion (rc, left, lifted_type, loc); + } + + if ((oper & Operator.BitwiseMask) != 0) + type = lifted_type; + + if (right.IsNull) { + if ((oper & Operator.BitwiseMask) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, this); + + return CreateLiftedValueTypeResult (rc, ltype); + } + + if (expr != null) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = left; + lifted.Right = expr; + return lifted.Resolve (rc); + } + } else if (rtype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (rtype).IsEnum) { + Nullable.Unwrap unwrap = null; + if (left.IsNull || right.IsNull) { + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) + left = Convert.ImplicitConversion (rc, left, rtype, left.Location); + + if ((oper & Operator.RelationalMask) != 0) + return CreateLiftedValueTypeResult (rc, rtype); + + if ((oper & Operator.BitwiseMask) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, this); + + if (right.IsNull) + return CreateLiftedValueTypeResult (rc, left.Type); + + // Equality operators are valid between E? and null + expr = left; + unwrap = new Nullable.Unwrap (right); + } else { + expr = Convert.ImplicitConversion (rc, left, Nullable.NullableInfo.GetUnderlyingType (rtype), loc); + if (expr == null) + return null; + } + + if (expr != null) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = expr; + lifted.Right = right; + lifted.UnwrapRight = unwrap; + return lifted.Resolve (rc); + } + } else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) { + Nullable.Unwrap unwrap = null; + if (right.IsNull || left.IsNull) { + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) + right = Convert.ImplicitConversion (rc, right, ltype, right.Location); + + if ((oper & Operator.RelationalMask) != 0) + return CreateLiftedValueTypeResult (rc, ltype); + + if ((oper & Operator.BitwiseMask) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, this); + + if (left.IsNull) + return CreateLiftedValueTypeResult (rc, right.Type); + + // Equality operators are valid between E? and null + expr = right; + unwrap = new Nullable.Unwrap (left); + } else { + expr = Convert.ImplicitConversion (rc, right, Nullable.NullableInfo.GetUnderlyingType (ltype), loc); + if (expr == null) + return null; + } + + if (expr != null) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = left; + lifted.UnwrapLeft = unwrap; + lifted.Right = expr; + return lifted.Resolve (rc); + } + } + } + + return null; + } + + static Expression ConvertEnumOperandToUnderlyingType (ResolveContext rc, Expression expr, bool liftType) + { + TypeSpec underlying_type; + if (expr.Type.IsNullableType) { + var nt = Nullable.NullableInfo.GetUnderlyingType (expr.Type); + if (nt.IsEnum) + underlying_type = EnumSpec.GetUnderlyingType (nt); + else + underlying_type = nt; + } else if (expr.Type.IsEnum) { + underlying_type = EnumSpec.GetUnderlyingType (expr.Type); + } else { + underlying_type = expr.Type; + } + + switch (underlying_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + underlying_type = rc.BuiltinTypes.Int; + break; + } + + if (expr.Type.IsNullableType || liftType) + underlying_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { underlying_type }); + + if (expr.Type == underlying_type) + return expr; + + return EmptyCast.Create (expr, underlying_type); + } + + Expression ResolveEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype) + { + // + // U operator - (E e, E f) + // E operator - (E e, U x) // Internal decomposition operator + // E operator - (U x, E e) // Internal decomposition operator + // + // E operator + (E e, U x) + // E operator + (U x, E e) + // + + TypeSpec enum_type; + + if (lenum) + enum_type = ltype; + else if (renum) + enum_type = rtype; + else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) + enum_type = ltype; + else + enum_type = rtype; + + Expression expr; + if (!enum_type.IsNullableType) { + expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, false), false); + if (expr != null) { + if (oper == Operator.Subtraction) + expr = ConvertEnumSubtractionResult (rc, expr); + else + expr = ConvertEnumAdditionalResult (expr, enum_type); + + enum_conversion = GetEnumResultCast (expr.Type); + + return expr; + } + + enum_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { enum_type }); + } + + expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, true), false); + if (expr != null) { + if (oper == Operator.Subtraction) + expr = ConvertEnumSubtractionResult (rc, expr); + else + expr = ConvertEnumAdditionalResult (expr, enum_type); + + enum_conversion = GetEnumResultCast (expr.Type); + } + + return expr; + } + + static Expression ConvertEnumAdditionalResult (Expression expr, TypeSpec enumType) + { + return EmptyCast.Create (expr, enumType); + } + + Expression ConvertEnumSubtractionResult (ResolveContext rc, Expression expr) + { + // + // Enumeration subtraction has different result type based on + // best overload + // + TypeSpec result_type; + if (left.Type == right.Type) { + var c = right as EnumConstant; + if (c != null && c.IsZeroInteger && !right.Type.IsEnum) { + // + // LAMESPEC: This is quite unexpected for expression E - 0 the return type is + // E which is not what expressions E - 1 or 0 - E return + // + result_type = left.Type; + } else { + result_type = left.Type.IsNullableType ? + Nullable.NullableInfo.GetEnumUnderlyingType (rc.Module, left.Type) : + EnumSpec.GetUnderlyingType (left.Type); + } + } else { + if (IsEnumOrNullableEnum (left.Type)) { + result_type = left.Type; + } else { + result_type = right.Type; + } + + if (expr is Nullable.LiftedBinaryOperator && !result_type.IsNullableType) + result_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { result_type }); + } + + return EmptyCast.Create (expr, result_type); + } + + public static ConvCast.Mode GetEnumResultCast (TypeSpec type) + { + if (type.IsNullableType) + type = Nullable.NullableInfo.GetUnderlyingType (type); + + if (type.IsEnum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return ConvCast.Mode.I4_I1; + case BuiltinTypeSpec.Type.Byte: + return ConvCast.Mode.I4_U1; + case BuiltinTypeSpec.Type.Short: + return ConvCast.Mode.I4_I2; + case BuiltinTypeSpec.Type.UShort: + return ConvCast.Mode.I4_U2; + } + + return 0; + } + + // + // Equality operators rules + // + Expression ResolveEquality (ResolveContext ec, TypeSpec l, TypeSpec r, bool primitives_only) + { + Expression result; + type = ec.BuiltinTypes.Bool; + bool no_arg_conv = false; + + if (!primitives_only) { + + // + // a, Both operands are reference-type values or the value null + // b, One operand is a value of type T where T is a type-parameter and + // the other operand is the value null. Furthermore T does not have the + // value type constraint + // + // LAMESPEC: Very confusing details in the specification, basically any + // reference like type-parameter is allowed + // + var tparam_l = l as TypeParameterSpec; + var tparam_r = r as TypeParameterSpec; + if (tparam_l != null) { + if (right is NullLiteral) { + if (tparam_l.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType) + return null; + + left = new BoxedCast (left, ec.BuiltinTypes.Object); + return this; + } + + if (!tparam_l.IsReferenceType) + return null; + + l = tparam_l.GetEffectiveBase (); + left = new BoxedCast (left, l); + } else if (left is NullLiteral && tparam_r == null) { + if (TypeSpec.IsReferenceType (r)) + return this; + + if (r.Kind == MemberKind.InternalCompilerType) + return null; + } + + if (tparam_r != null) { + if (left is NullLiteral) { + if (tparam_r.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType) + return null; + + right = new BoxedCast (right, ec.BuiltinTypes.Object); + return this; + } + + if (!tparam_r.IsReferenceType) + return null; + + r = tparam_r.GetEffectiveBase (); + right = new BoxedCast (right, r); + } else if (right is NullLiteral) { + if (TypeSpec.IsReferenceType (l)) + return this; + + if (l.Kind == MemberKind.InternalCompilerType) + return null; + } + + // + // LAMESPEC: method groups can be compared when they convert to other side delegate + // + if (l.IsDelegate) { + if (right.eclass == ExprClass.MethodGroup) { + result = Convert.ImplicitConversion (ec, right, l, loc); + if (result == null) + return null; + + right = result; + r = l; + } else if (r.IsDelegate && l != r) { + return null; + } + } else if (left.eclass == ExprClass.MethodGroup && r.IsDelegate) { + result = Convert.ImplicitConversionRequired (ec, left, r, loc); + if (result == null) + return null; + + left = result; + l = r; + } else { + no_arg_conv = l == r && !l.IsStruct; + } + } + + // + // bool operator != (string a, string b) + // bool operator == (string a, string b) + // + // bool operator != (Delegate a, Delegate b) + // bool operator == (Delegate a, Delegate b) + // + // bool operator != (bool a, bool b) + // bool operator == (bool a, bool b) + // + // LAMESPEC: Reference equality comparison can apply to value/reference types when + // they implement an implicit conversion to any of types above. This does + // not apply when both operands are of same reference type + // + if (r.BuiltinType != BuiltinTypeSpec.Type.Object && l.BuiltinType != BuiltinTypeSpec.Type.Object) { + result = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryEquality, no_arg_conv); + if (result != null) + return result; + + // + // Now try lifted version of predefined operators + // + if (no_arg_conv && !l.IsNullableType) { + // + // Optimizes cases which won't match + // + } else { + result = ResolveOperatorPredefined (ec, ec.Module.OperatorsBinaryEqualityLifted, no_arg_conv); + if (result != null) + return result; + } + + // + // The == and != operators permit one operand to be a value of a nullable + // type and the other to be the null literal, even if no predefined or user-defined + // operator (in unlifted or lifted form) exists for the operation. + // + if ((l.IsNullableType && right.IsNull) || (r.IsNullableType && left.IsNull)) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = left; + lifted.Right = right; + return lifted.Resolve (ec); + } + } + + // + // bool operator != (object a, object b) + // bool operator == (object a, object b) + // + // An explicit reference conversion exists from the + // type of either operand to the type of the other operand. + // + + // Optimize common path + if (l == r) { + return l.Kind == MemberKind.InternalCompilerType || l.Kind == MemberKind.Struct ? null : this; + } + + if (!Convert.ExplicitReferenceConversionExists (l, r) && + !Convert.ExplicitReferenceConversionExists (r, l)) + return null; + + // Reject allowed explicit conversions like int->object + if (!TypeSpec.IsReferenceType (l) || !TypeSpec.IsReferenceType (r)) + return null; + + if (l.BuiltinType == BuiltinTypeSpec.Type.String || l.BuiltinType == BuiltinTypeSpec.Type.Delegate || l.IsDelegate || MemberCache.GetUserOperator (l, CSharp.Operator.OpType.Equality, false) != null) + ec.Report.Warning (253, 2, loc, + "Possible unintended reference comparison. Consider casting the right side expression to type `{0}' to get value comparison", + l.GetSignatureForError ()); + + if (r.BuiltinType == BuiltinTypeSpec.Type.String || r.BuiltinType == BuiltinTypeSpec.Type.Delegate || r.IsDelegate || MemberCache.GetUserOperator (r, CSharp.Operator.OpType.Equality, false) != null) + ec.Report.Warning (252, 2, loc, + "Possible unintended reference comparison. Consider casting the left side expression to type `{0}' to get value comparison", + r.GetSignatureForError ()); + + return this; + } + + + Expression ResolveOperatorPointer (ResolveContext ec, TypeSpec l, TypeSpec r) + { + // + // bool operator == (void* x, void* y); + // bool operator != (void* x, void* y); + // bool operator < (void* x, void* y); + // bool operator > (void* x, void* y); + // bool operator <= (void* x, void* y); + // bool operator >= (void* x, void* y); + // + if ((oper & Operator.ComparisonMask) != 0) { + Expression temp; + if (!l.IsPointer) { + temp = Convert.ImplicitConversion (ec, left, r, left.Location); + if (temp == null) + return null; + left = temp; + } + + if (!r.IsPointer) { + temp = Convert.ImplicitConversion (ec, right, l, right.Location); + if (temp == null) + return null; + right = temp; + } + + type = ec.BuiltinTypes.Bool; + return this; + } + + return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryUnsafe, false); + } + + // + // Build-in operators method overloading + // + Expression ResolveOperatorPredefined (ResolveContext ec, PredefinedOperator [] operators, bool primitives_only) + { + PredefinedOperator best_operator = null; + TypeSpec l = left.Type; + TypeSpec r = right.Type; + Operator oper_mask = oper & ~Operator.ValuesOnlyMask; + + foreach (PredefinedOperator po in operators) { + if ((po.OperatorsMask & oper_mask) == 0) + continue; + + if (primitives_only) { + if (!po.IsPrimitiveApplicable (l, r)) + continue; + } else { + if (!po.IsApplicable (ec, left, right)) + continue; + } + + if (best_operator == null) { + best_operator = po; + if (primitives_only) + break; + + continue; + } + + best_operator = po.ResolveBetterOperator (ec, best_operator); + + if (best_operator == null) { + ec.Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'", + OperName (oper), l.GetSignatureForError (), r.GetSignatureForError ()); + + best_operator = po; + break; + } + } + + if (best_operator == null) + return null; + + return best_operator.ConvertResult (ec, this); + } + + // + // Optimize & constant expressions with 0 value + // + Expression OptimizeAndOperation (Expression expr) + { + Constant rc = right as Constant; + Constant lc = left as Constant; + if ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue)) { + // + // The result is a constant with side-effect + // + Constant side_effect = rc == null ? + new SideEffectConstant (lc, right, loc) : + new SideEffectConstant (rc, left, loc); + + return ReducedExpression.Create (side_effect, expr); + } + + return expr; + } + + // + // Value types can be compared with the null literal because of the lifting + // language rules. However the result is always true or false. + // + public Expression CreateLiftedValueTypeResult (ResolveContext rc, TypeSpec valueType) + { + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + type = rc.BuiltinTypes.Bool; + return this; + } + + // FIXME: Handle side effect constants + Constant c = new BoolConstant (rc.BuiltinTypes, Oper == Operator.Inequality, loc); + + if ((Oper & Operator.EqualityMask) != 0) { + rc.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is always `{1}'", + valueType.GetSignatureForError (), c.GetValueAsLiteral ()); + } else { + rc.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'", + valueType.GetSignatureForError (), c.GetValueAsLiteral ()); + } + + return c; + } + + // + // Performs user-operator overloading + // + Expression ResolveUserOperator (ResolveContext rc, Expression left, Expression right) + { + Expression oper_expr; + + var op = ConvertBinaryToUserOperator (oper); + var l = left.Type; + if (l.IsNullableType) + l = Nullable.NullableInfo.GetUnderlyingType (l); + var r = right.Type; + if (r.IsNullableType) + r = Nullable.NullableInfo.GetUnderlyingType (r); + + IList left_operators = MemberCache.GetUserOperator (l, op, false); + IList right_operators = null; + + if (l != r) { + right_operators = MemberCache.GetUserOperator (r, op, false); + if (right_operators == null && left_operators == null) + return null; + } else if (left_operators == null) { + return null; + } + + Arguments args = new Arguments (2); + Argument larg = new Argument (left); + args.Add (larg); + Argument rarg = new Argument (right); + args.Add (rarg); + + // + // User-defined operator implementations always take precedence + // over predefined operator implementations + // + if (left_operators != null && right_operators != null) { + left_operators = CombineUserOperators (left_operators, right_operators); + } else if (right_operators != null) { + left_operators = right_operators; + } + + const OverloadResolver.Restrictions restr = OverloadResolver.Restrictions.ProbingOnly | + OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded; + + var res = new OverloadResolver (left_operators, restr, loc); + + var oper_method = res.ResolveOperator (rc, ref args); + if (oper_method == null) { + // + // Logical && and || cannot be lifted + // + if ((oper & Operator.LogicalMask) != 0) + return null; + + // + // Apply lifted user operators only for liftable types. Implicit conversion + // to nullable types is not allowed + // + if (!IsLiftedOperatorApplicable ()) + return null; + + // TODO: Cache the result in module container + var lifted_methods = CreateLiftedOperators (rc, left_operators); + if (lifted_methods == null) + return null; + + res = new OverloadResolver (lifted_methods, restr | OverloadResolver.Restrictions.ProbingOnly, loc); + + oper_method = res.ResolveOperator (rc, ref args); + if (oper_method == null) + return null; + + MethodSpec best_original = null; + foreach (MethodSpec ms in left_operators) { + if (ms.MemberDefinition == oper_method.MemberDefinition) { + best_original = ms; + break; + } + } + + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + // + // Expression trees use lifted notation in this case + // + this.left = Convert.ImplicitConversion (rc, left, oper_method.Parameters.Types[0], left.Location); + this.right = Convert.ImplicitConversion (rc, right, oper_method.Parameters.Types[1], left.Location); + } + + var ptypes = best_original.Parameters.Types; + + if (left.IsNull || right.IsNull) { + // + // The lifted operator produces a null value if one or both operands are null + // + if ((oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) { + type = oper_method.ReturnType; + return Nullable.LiftedNull.CreateFromExpression (rc, this); + } + + // + // The lifted operator produces the value false if one or both operands are null for + // relational operators. + // + if ((oper & Operator.RelationalMask) != 0) { + // + // CSC BUG: This should be different warning, csc reports CS0458 with bool? which is wrong + // because return type is actually bool + // + return CreateLiftedValueTypeResult (rc, left.IsNull ? ptypes [1] : ptypes [0]); + } + + if ((oper & Operator.EqualityMask) != 0 && ((left.IsNull && !right.Type.IsNullableType) || !left.Type.IsNullableType)) { + return CreateLiftedValueTypeResult (rc, left.IsNull ? ptypes [1] : ptypes [0]); + } + } + + type = oper_method.ReturnType; + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.UserOperator = best_original; + + if (left.Type.IsNullableType && !ptypes[0].IsNullableType) { + lifted.UnwrapLeft = new Nullable.Unwrap (left); + } + + if (right.Type.IsNullableType && !ptypes[1].IsNullableType) { + lifted.UnwrapRight = new Nullable.Unwrap (right); + } + + lifted.Left = Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? left, ptypes[0], left.Location); + lifted.Right = Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? right, ptypes[1], right.Location); + + return lifted.Resolve (rc); + } + + if ((oper & Operator.LogicalMask) != 0) { + // TODO: CreateExpressionTree is allocated every time + oper_expr = new ConditionalLogicalOperator (oper_method, args, CreateExpressionTree, + oper == Operator.LogicalAnd, loc).Resolve (rc); + } else { + oper_expr = new UserOperatorCall (oper_method, args, CreateExpressionTree, loc); + } + + this.left = larg.Expr; + this.right = rarg.Expr; + + return oper_expr; + } + + bool IsLiftedOperatorApplicable () + { + if (left.Type.IsNullableType) { + if ((oper & Operator.EqualityMask) != 0) + return !right.IsNull; + + return true; + } + + if (right.Type.IsNullableType) { + if ((oper & Operator.EqualityMask) != 0) + return !left.IsNull; + + return true; + } + + if (TypeSpec.IsValueType (left.Type)) + return right.IsNull; + + if (TypeSpec.IsValueType (right.Type)) + return left.IsNull; + + return false; + } + + List CreateLiftedOperators (ResolveContext rc, IList operators) + { + var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec; + if (nullable_type == null) + return null; + + // + // Lifted operators permit predefined and user-defined operators that operate + // on non-nullable value types to also be used with nullable forms of those types. + // Lifted operators are constructed from predefined and user-defined operators + // that meet certain requirements + // + List lifted = null; + foreach (MethodSpec oper in operators) { + TypeSpec rt; + if ((Oper & Operator.ComparisonMask) != 0) { + // + // Result type must be of type bool for lifted comparison operators + // + rt = oper.ReturnType; + if (rt.BuiltinType != BuiltinTypeSpec.Type.Bool) + continue; + } else { + if (!TypeSpec.IsNonNullableValueType (oper.ReturnType)) + continue; + + rt = null; + } + + var ptypes = oper.Parameters.Types; + if (!TypeSpec.IsNonNullableValueType (ptypes [0]) || !TypeSpec.IsNonNullableValueType (ptypes [1])) + continue; + + // + // LAMESPEC: I am not sure why but for equality operators to be lifted + // both types have to match + // + if ((Oper & Operator.EqualityMask) != 0 && ptypes [0] != ptypes [1]) + continue; + + if (lifted == null) + lifted = new List (); + + // + // The lifted form is constructed by adding a single ? modifier to each operand and + // result type except for comparison operators where return type is bool + // + if (rt == null) + rt = nullable_type.MakeGenericType (rc.Module, new[] { oper.ReturnType }); + + var parameters = ParametersCompiled.CreateFullyResolved ( + nullable_type.MakeGenericType (rc.Module, new [] { ptypes[0] }), + nullable_type.MakeGenericType (rc.Module, new [] { ptypes[1] })); + + var lifted_op = new MethodSpec (oper.Kind, oper.DeclaringType, oper.MemberDefinition, + rt, parameters, oper.Modifiers); + + lifted.Add (lifted_op); + } + + return lifted; + } + + // + // Merge two sets of user operators into one, they are mostly distinguish + // except when they share base type and it contains an operator + // + static IList CombineUserOperators (IList left, IList right) + { + var combined = new List (left.Count + right.Count); + combined.AddRange (left); + foreach (var r in right) { + bool same = false; + foreach (var l in left) { + if (l.DeclaringType == r.DeclaringType) { + same = true; + break; + } + } + + if (!same) + combined.Add (r); + } + + return combined; + } + + void CheckOutOfRangeComparison (ResolveContext ec, Constant c, TypeSpec type) + { + if (c is IntegralConstant || c is CharConstant) { + try { + c.ConvertExplicitly (true, type); + } catch (OverflowException) { + ec.Report.Warning (652, 2, loc, + "A comparison between a constant and a variable is useless. The constant is out of the range of the variable type `{0}'", + type.GetSignatureForError ()); + } + } + } + + /// + /// EmitBranchable is called from Statement.EmitBoolExpression in the + /// context of a conditional bool expression. This function will return + /// false if it is was possible to use EmitBranchable, or true if it was. + /// + /// The expression's code is generated, and we will generate a branch to `target' + /// if the resulting expression value is equal to isTrue + /// + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) { + left = left.EmitToField (ec); + + if ((oper & Operator.LogicalMask) == 0) { + right = right.EmitToField (ec); + } + } + + // + // This is more complicated than it looks, but its just to avoid + // duplicated tests: basically, we allow ==, !=, >, <, >= and <= + // but on top of that we want for == and != to use a special path + // if we are comparing against null + // + if ((oper & Operator.EqualityMask) != 0 && (left is Constant || right is Constant)) { + bool my_on_true = oper == Operator.Inequality ? on_true : !on_true; + + // + // put the constant on the rhs, for simplicity + // + if (left is Constant) { + Expression swap = right; + right = left; + left = swap; + } + + // + // brtrue/brfalse works with native int only + // + if (((Constant) right).IsZeroInteger && right.Type.BuiltinType != BuiltinTypeSpec.Type.Long && right.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) { + left.EmitBranchable (ec, target, my_on_true); + return; + } + if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) { + // right is a boolean, and it's not 'false' => it is 'true' + left.EmitBranchable (ec, target, !my_on_true); + return; + } + + } else if (oper == Operator.LogicalAnd) { + + if (on_true) { + Label tests_end = ec.DefineLabel (); + + left.EmitBranchable (ec, tests_end, false); + right.EmitBranchable (ec, target, true); + ec.MarkLabel (tests_end); + } else { + // + // This optimizes code like this + // if (true && i > 4) + // + if (!(left is Constant)) + left.EmitBranchable (ec, target, false); + + if (!(right is Constant)) + right.EmitBranchable (ec, target, false); + } + + return; + + } else if (oper == Operator.LogicalOr){ + if (on_true) { + left.EmitBranchable (ec, target, true); + right.EmitBranchable (ec, target, true); + + } else { + Label tests_end = ec.DefineLabel (); + left.EmitBranchable (ec, tests_end, true); + right.EmitBranchable (ec, target, false); + ec.MarkLabel (tests_end); + } + + return; + + } else if ((oper & Operator.ComparisonMask) == 0) { + base.EmitBranchable (ec, target, on_true); + return; + } + + left.Emit (ec); + right.Emit (ec); + + TypeSpec t = left.Type; + bool is_float = IsFloat (t); + bool is_unsigned = is_float || IsUnsigned (t); + + switch (oper){ + case Operator.Equality: + if (on_true) + ec.Emit (OpCodes.Beq, target); + else + ec.Emit (OpCodes.Bne_Un, target); + break; + + case Operator.Inequality: + if (on_true) + ec.Emit (OpCodes.Bne_Un, target); + else + ec.Emit (OpCodes.Beq, target); + break; + + case Operator.LessThan: + if (on_true) + if (is_unsigned && !is_float) + ec.Emit (OpCodes.Blt_Un, target); + else + ec.Emit (OpCodes.Blt, target); + else + if (is_unsigned) + ec.Emit (OpCodes.Bge_Un, target); + else + ec.Emit (OpCodes.Bge, target); + break; + + case Operator.GreaterThan: + if (on_true) + if (is_unsigned && !is_float) + ec.Emit (OpCodes.Bgt_Un, target); + else + ec.Emit (OpCodes.Bgt, target); + else + if (is_unsigned) + ec.Emit (OpCodes.Ble_Un, target); + else + ec.Emit (OpCodes.Ble, target); + break; + + case Operator.LessThanOrEqual: + if (on_true) + if (is_unsigned && !is_float) + ec.Emit (OpCodes.Ble_Un, target); + else + ec.Emit (OpCodes.Ble, target); + else + if (is_unsigned) + ec.Emit (OpCodes.Bgt_Un, target); + else + ec.Emit (OpCodes.Bgt, target); + break; + + + case Operator.GreaterThanOrEqual: + if (on_true) + if (is_unsigned && !is_float) + ec.Emit (OpCodes.Bge_Un, target); + else + ec.Emit (OpCodes.Bge, target); + else + if (is_unsigned) + ec.Emit (OpCodes.Blt_Un, target); + else + ec.Emit (OpCodes.Blt, target); + break; + default: + throw new InternalErrorException (oper.ToString ()); + } + } + + public override void Emit (EmitContext ec) + { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) { + left = left.EmitToField (ec); + + if ((oper & Operator.LogicalMask) == 0) { + right = right.EmitToField (ec); + } + } + + // + // Handle short-circuit operators differently + // than the rest + // + if ((oper & Operator.LogicalMask) != 0) { + Label load_result = ec.DefineLabel (); + Label end = ec.DefineLabel (); + + bool is_or = oper == Operator.LogicalOr; + left.EmitBranchable (ec, load_result, is_or); + right.Emit (ec); + ec.Emit (OpCodes.Br_S, end); + + ec.MarkLabel (load_result); + ec.EmitInt (is_or ? 1 : 0); + ec.MarkLabel (end); + return; + } + + // + // Optimize zero-based operations which cannot be optimized at expression level + // + if (oper == Operator.Subtraction) { + var lc = left as IntegralConstant; + if (lc != null && lc.IsDefaultValue) { + right.Emit (ec); + ec.Emit (OpCodes.Neg); + return; + } + } + + EmitOperator (ec, left, right); + } + + public void EmitOperator (EmitContext ec, Expression left, Expression right) + { + left.Emit (ec); + right.Emit (ec); + + EmitOperatorOpcode (ec, oper, left.Type, right); + + // + // Emit result enumerable conversion this way because it's quite complicated get it + // to resolved tree because expression tree cannot see it. + // + if (enum_conversion != 0) + ConvCast.Emit (ec, enum_conversion); + } + + public override void EmitSideEffect (EmitContext ec) + { + if ((oper & Operator.LogicalMask) != 0 || + (ec.HasSet (EmitContext.Options.CheckedScope) && (oper == Operator.Multiply || oper == Operator.Addition || oper == Operator.Subtraction))) { + base.EmitSideEffect (ec); + } else { + left.EmitSideEffect (ec); + right.EmitSideEffect (ec); + } + } + + public override Expression EmitToField (EmitContext ec) + { + if ((oper & Operator.LogicalMask) == 0) { + var await_expr = left as Await; + if (await_expr != null && right.IsSideEffectFree) { + await_expr.Statement.EmitPrologue (ec); + left = await_expr.Statement.GetResultExpression (ec); + return this; + } + + await_expr = right as Await; + if (await_expr != null && left.IsSideEffectFree) { + await_expr.Statement.EmitPrologue (ec); + right = await_expr.Statement.GetResultExpression (ec); + return this; + } + } + + return base.EmitToField (ec); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Binary target = (Binary) t; + + target.left = left.Clone (clonectx); + target.right = right.Clone (clonectx); + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (4); + + MemberAccess sle = new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc); + + CSharpBinderFlags flags = 0; + if (ec.HasSet (ResolveContext.Options.CheckedScope)) + flags = CSharpBinderFlags.CheckedContext; + + if ((oper & Operator.LogicalMask) != 0) + flags |= CSharpBinderFlags.BinaryOperationLogical; + + binder_args.Add (new Argument (new EnumConstant (new IntLiteral (ec.BuiltinTypes, (int) flags, loc), ec.Module.PredefinedTypes.BinderFlags.Resolve ()))); + binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), GetOperatorExpressionTypeName (), loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + return new Invocation (new MemberAccess (new TypeExpression (ec.Module.PredefinedTypes.Binder.TypeSpec, loc), "BinaryOperation", loc), binder_args); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return CreateExpressionTree (ec, null); + } + + public Expression CreateExpressionTree (ResolveContext ec, Expression method) + { + string method_name; + bool lift_arg = false; + + switch (oper) { + case Operator.Addition: + if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type)) + method_name = "AddChecked"; + else + method_name = "Add"; + break; + case Operator.BitwiseAnd: + method_name = "And"; + break; + case Operator.BitwiseOr: + method_name = "Or"; + break; + case Operator.Division: + method_name = "Divide"; + break; + case Operator.Equality: + method_name = "Equal"; + lift_arg = true; + break; + case Operator.ExclusiveOr: + method_name = "ExclusiveOr"; + break; + case Operator.GreaterThan: + method_name = "GreaterThan"; + lift_arg = true; + break; + case Operator.GreaterThanOrEqual: + method_name = "GreaterThanOrEqual"; + lift_arg = true; + break; + case Operator.Inequality: + method_name = "NotEqual"; + lift_arg = true; + break; + case Operator.LeftShift: + method_name = "LeftShift"; + break; + case Operator.LessThan: + method_name = "LessThan"; + lift_arg = true; + break; + case Operator.LessThanOrEqual: + method_name = "LessThanOrEqual"; + lift_arg = true; + break; + case Operator.LogicalAnd: + method_name = "AndAlso"; + break; + case Operator.LogicalOr: + method_name = "OrElse"; + break; + case Operator.Modulus: + method_name = "Modulo"; + break; + case Operator.Multiply: + if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type)) + method_name = "MultiplyChecked"; + else + method_name = "Multiply"; + break; + case Operator.RightShift: + method_name = "RightShift"; + break; + case Operator.Subtraction: + if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type)) + method_name = "SubtractChecked"; + else + method_name = "Subtract"; + break; + + default: + throw new InternalErrorException ("Unknown expression tree binary operator " + oper); + } + + Arguments args = new Arguments (2); + args.Add (new Argument (left.CreateExpressionTree (ec))); + args.Add (new Argument (right.CreateExpressionTree (ec))); + if (method != null) { + if (lift_arg) + args.Add (new Argument (new BoolLiteral (ec.BuiltinTypes, false, loc))); + + args.Add (new Argument (method)); + } + + return CreateExpressionFactoryCall (ec, method_name, args); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + // + // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string + // b, c, d... may be strings or objects. + // + public class StringConcat : Expression + { + Arguments arguments; + + StringConcat (Location loc) + { + this.loc = loc; + arguments = new Arguments (2); + } + + public override bool ContainsEmitWithAwait () + { + return arguments.ContainsEmitWithAwait (); + } + + public static StringConcat Create (ResolveContext rc, Expression left, Expression right, Location loc) + { + if (left.eclass == ExprClass.Unresolved || right.eclass == ExprClass.Unresolved) + throw new ArgumentException (); + + var s = new StringConcat (loc); + s.type = rc.BuiltinTypes.String; + s.eclass = ExprClass.Value; + + s.Append (rc, left); + s.Append (rc, right); + return s; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Argument arg = arguments [0]; + return CreateExpressionAddCall (ec, arg, arg.CreateExpressionTree (ec), 1); + } + + // + // Creates nested calls tree from an array of arguments used for IL emit + // + Expression CreateExpressionAddCall (ResolveContext ec, Argument left, Expression left_etree, int pos) + { + Arguments concat_args = new Arguments (2); + Arguments add_args = new Arguments (3); + + concat_args.Add (left); + add_args.Add (new Argument (left_etree)); + + concat_args.Add (arguments [pos]); + add_args.Add (new Argument (arguments [pos].CreateExpressionTree (ec))); + + var methods = GetConcatMethodCandidates (); + if (methods == null) + return null; + + var res = new OverloadResolver (methods, OverloadResolver.Restrictions.NoBaseMembers, loc); + var method = res.ResolveMember (ec, ref concat_args); + if (method == null) + return null; + + add_args.Add (new Argument (new TypeOfMethod (method, loc))); + + Expression expr = CreateExpressionFactoryCall (ec, "Add", add_args); + if (++pos == arguments.Count) + return expr; + + left = new Argument (new EmptyExpression (method.ReturnType)); + return CreateExpressionAddCall (ec, left, expr, pos); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + void Append (ResolveContext rc, Expression operand) + { + // + // Constant folding + // + StringConstant sc = operand as StringConstant; + if (sc != null) { + if (arguments.Count != 0) { + Argument last_argument = arguments [arguments.Count - 1]; + StringConstant last_expr_constant = last_argument.Expr as StringConstant; + if (last_expr_constant != null) { + last_argument.Expr = new StringConstant (rc.BuiltinTypes, last_expr_constant.Value + sc.Value, sc.Location); + return; + } + } + } else { + // + // Multiple (3+) concatenation are resolved as multiple StringConcat instances + // + StringConcat concat_oper = operand as StringConcat; + if (concat_oper != null) { + arguments.AddRange (concat_oper.arguments); + return; + } + } + + arguments.Add (new Argument (operand)); + } + + IList GetConcatMethodCandidates () + { + return MemberCache.FindMembers (type, "Concat", true); + } + + public override void Emit (EmitContext ec) + { + // Optimize by removing any extra null arguments, they are no-op + for (int i = 0; i < arguments.Count; ++i) { + if (arguments[i].Expr is NullConstant) + arguments.RemoveAt (i--); + } + + var members = GetConcatMethodCandidates (); + var res = new OverloadResolver (members, OverloadResolver.Restrictions.NoBaseMembers, loc); + var method = res.ResolveMember (new ResolveContext (ec.MemberContext), ref arguments); + if (method != null) { + var call = new CallEmitter (); + call.EmitPredefined (ec, method, arguments, false); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + arguments.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + if (arguments.Count != 2) + throw new NotImplementedException ("arguments.Count != 2"); + + var concat = typeof (string).GetMethod ("Concat", new[] { typeof (object), typeof (object) }); + return SLE.Expression.Add (arguments[0].Expr.MakeExpression (ctx), arguments[1].Expr.MakeExpression (ctx), concat); + } + } + + // + // User-defined conditional logical operator + // + public class ConditionalLogicalOperator : UserOperatorCall + { + readonly bool is_and; + Expression oper_expr; + + public ConditionalLogicalOperator (MethodSpec oper, Arguments arguments, Func expr_tree, bool is_and, Location loc) + : base (oper, arguments, expr_tree, loc) + { + this.is_and = is_and; + eclass = ExprClass.Unresolved; + } + + protected override Expression DoResolve (ResolveContext ec) + { + AParametersCollection pd = oper.Parameters; + if (!TypeSpecComparer.IsEqual (type, pd.Types[0]) || !TypeSpecComparer.IsEqual (type, pd.Types[1])) { + ec.Report.Error (217, loc, + "A user-defined operator `{0}' must have each parameter type and return type of the same type in order to be applicable as a short circuit operator", + oper.GetSignatureForError ()); + return null; + } + + Expression left_dup = new EmptyExpression (type); + Expression op_true = GetOperatorTrue (ec, left_dup, loc); + Expression op_false = GetOperatorFalse (ec, left_dup, loc); + if (op_true == null || op_false == null) { + ec.Report.Error (218, loc, + "The type `{0}' must have operator `true' and operator `false' defined when `{1}' is used as a short circuit operator", + type.GetSignatureForError (), oper.GetSignatureForError ()); + return null; + } + + oper_expr = is_and ? op_false : op_true; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + Label end_target = ec.DefineLabel (); + + // + // Emit and duplicate left argument + // + bool right_contains_await = ec.HasSet (BuilderContext.Options.AsyncBody) && arguments[1].Expr.ContainsEmitWithAwait (); + if (right_contains_await) { + arguments[0] = arguments[0].EmitToField (ec, false); + arguments[0].Expr.Emit (ec); + } else { + arguments[0].Expr.Emit (ec); + ec.Emit (OpCodes.Dup); + arguments.RemoveAt (0); + } + + oper_expr.EmitBranchable (ec, end_target, true); + + base.Emit (ec); + + if (right_contains_await) { + // + // Special handling when right expression contains await and left argument + // could not be left on stack before logical branch + // + Label skip_left_load = ec.DefineLabel (); + ec.Emit (OpCodes.Br_S, skip_left_load); + ec.MarkLabel (end_target); + arguments[0].Expr.Emit (ec); + ec.MarkLabel (skip_left_load); + } else { + ec.MarkLabel (end_target); + } + } + } + + public class PointerArithmetic : Expression { + Expression left, right; + readonly Binary.Operator op; + + // + // We assume that `l' is always a pointer + // + public PointerArithmetic (Binary.Operator op, Expression l, Expression r, TypeSpec t, Location loc) + { + type = t; + this.loc = loc; + left = l; + right = r; + this.op = op; + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return null; + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Variable; + + var pc = left.Type as PointerContainer; + if (pc != null && pc.Element.Kind == MemberKind.Void) { + Error_VoidPointerOperation (ec); + return null; + } + + return this; + } + + public override void Emit (EmitContext ec) + { + TypeSpec op_type = left.Type; + + // It must be either array or fixed buffer + TypeSpec element; + if (TypeManager.HasElementType (op_type)) { + element = TypeManager.GetElementType (op_type); + } else { + FieldExpr fe = left as FieldExpr; + if (fe != null) + element = ((FixedFieldSpec) (fe.Spec)).ElementType; + else + element = op_type; + } + + int size = BuiltinTypeSpec.GetSize(element); + TypeSpec rtype = right.Type; + + if ((op & Binary.Operator.SubtractionMask) != 0 && rtype.IsPointer){ + // + // handle (pointer - pointer) + // + left.Emit (ec); + right.Emit (ec); + ec.Emit (OpCodes.Sub); + + if (size != 1){ + if (size == 0) + ec.Emit (OpCodes.Sizeof, element); + else + ec.EmitInt (size); + ec.Emit (OpCodes.Div); + } + ec.Emit (OpCodes.Conv_I8); + } else { + // + // handle + and - on (pointer op int) + // + Constant left_const = left as Constant; + if (left_const != null) { + // + // Optimize ((T*)null) pointer operations + // + if (left_const.IsDefaultValue) { + left = EmptyExpression.Null; + } else { + left_const = null; + } + } + + left.Emit (ec); + + var right_const = right as Constant; + if (right_const != null) { + // + // Optimize 0-based arithmetic + // + if (right_const.IsDefaultValue) + return; + + if (size != 0) + right = new IntConstant (ec.BuiltinTypes, size, right.Location); + else + right = new SizeOf (new TypeExpression (element, right.Location), right.Location); + + // TODO: Should be the checks resolve context sensitive? + ResolveContext rc = new ResolveContext (ec.MemberContext, ResolveContext.Options.UnsafeScope); + right = new Binary (Binary.Operator.Multiply, right, right_const).Resolve (rc); + if (right == null) + return; + } + + right.Emit (ec); + switch (rtype.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + ec.Emit (OpCodes.Conv_I); + break; + case BuiltinTypeSpec.Type.UInt: + ec.Emit (OpCodes.Conv_U); + break; + } + + if (right_const == null && size != 1){ + if (size == 0) + ec.Emit (OpCodes.Sizeof, element); + else + ec.EmitInt (size); + if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long || rtype.BuiltinType == BuiltinTypeSpec.Type.ULong) + ec.Emit (OpCodes.Conv_I8); + + Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype, right); + } + + if (left_const == null) { + if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long) + ec.Emit (OpCodes.Conv_I); + else if (rtype.BuiltinType == BuiltinTypeSpec.Type.ULong) + ec.Emit (OpCodes.Conv_U); + + Binary.EmitOperatorOpcode (ec, op, op_type, right); + } + } + } + } + + // + // A boolean-expression is an expression that yields a result + // of type bool + // + public class BooleanExpression : ShimExpression + { + public BooleanExpression (Expression expr) + : base (expr) + { + this.loc = expr.Location; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + // TODO: We should emit IsTrue (v4) instead of direct user operator + // call but that would break csc compatibility + return base.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + // A boolean-expression is required to be of a type + // that can be implicitly converted to bool or of + // a type that implements operator true + + expr = expr.Resolve (ec); + if (expr == null) + return null; + + Assign ass = expr as Assign; + if (ass != null && ass.Source is Constant) { + ec.Report.Warning (665, 3, loc, + "Assignment in conditional expression is always constant. Did you mean to use `==' instead ?"); + } + + if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) + return expr; + + if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + return DynamicUnaryConversion.CreateIsTrue (ec, args, loc).Resolve (ec); + } + + type = ec.BuiltinTypes.Bool; + Expression converted = Convert.ImplicitConversion (ec, expr, type, loc); + if (converted != null) + return converted; + + // + // If no implicit conversion to bool exists, try using `operator true' + // + converted = GetOperatorTrue (ec, expr, loc); + if (converted == null) { + expr.Error_ValueCannotBeConverted (ec, type, false); + return null; + } + + return converted; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class BooleanExpressionFalse : Unary + { + public BooleanExpressionFalse (Expression expr) + : base (Operator.LogicalNot, expr, expr.Location) + { + } + + protected override Expression ResolveOperator (ResolveContext ec, Expression expr) + { + return GetOperatorFalse (ec, expr, loc) ?? base.ResolveOperator (ec, expr); + } + } + + /// + /// Implements the ternary conditional operator (?:) + /// + public class Conditional : Expression { + Expression expr, true_expr, false_expr; + + public Conditional (Expression expr, Expression true_expr, Expression false_expr, Location loc) + { + this.expr = expr; + this.true_expr = true_expr; + this.false_expr = false_expr; + this.loc = loc; + } + + #region Properties + + public Expression Expr { + get { + return expr; + } + } + + public Expression TrueExpr { + get { + return true_expr; + } + } + + public Expression FalseExpr { + get { + return false_expr; + } + } + + #endregion + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait () || true_expr.ContainsEmitWithAwait () || false_expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (3); + args.Add (new Argument (expr.CreateExpressionTree (ec))); + args.Add (new Argument (true_expr.CreateExpressionTree (ec))); + args.Add (new Argument (false_expr.CreateExpressionTree (ec))); + return CreateExpressionFactoryCall (ec, "Condition", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + true_expr = true_expr.Resolve (ec); + false_expr = false_expr.Resolve (ec); + + if (true_expr == null || false_expr == null || expr == null) + return null; + + eclass = ExprClass.Value; + TypeSpec true_type = true_expr.Type; + TypeSpec false_type = false_expr.Type; + type = true_type; + + // + // First, if an implicit conversion exists from true_expr + // to false_expr, then the result type is of type false_expr.Type + // + if (!TypeSpecComparer.IsEqual (true_type, false_type)) { + Expression conv = Convert.ImplicitConversion (ec, true_expr, false_type, loc); + if (conv != null && true_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) { + // + // Check if both can convert implicitly to each other's type + // + type = false_type; + + if (false_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) { + var conv_false_expr = Convert.ImplicitConversion (ec, false_expr, true_type, loc); + // + // LAMESPEC: There seems to be hardcoded promotition to int type when + // both sides are numeric constants and one side is int constant and + // other side is numeric constant convertible to int. + // + // var res = condition ? (short)1 : 1; + // + // Type of res is int even if according to the spec the conversion is + // ambiguous because 1 literal can be converted to short. + // + if (conv_false_expr != null) { + if (conv_false_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Int && conv is Constant) { + type = true_type; + conv_false_expr = null; + } else if (type.BuiltinType == BuiltinTypeSpec.Type.Int && conv_false_expr is Constant) { + conv_false_expr = null; + } + } + + if (conv_false_expr != null) { + ec.Report.Error (172, true_expr.Location, + "Type of conditional expression cannot be determined as `{0}' and `{1}' convert implicitly to each other", + true_type.GetSignatureForError (), false_type.GetSignatureForError ()); + } + } + + true_expr = conv; + if (true_expr.Type != type) + true_expr = EmptyCast.Create (true_expr, type); + } else if ((conv = Convert.ImplicitConversion (ec, false_expr, true_type, loc)) != null) { + false_expr = conv; + } else { + ec.Report.Error (173, true_expr.Location, + "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'", + true_type.GetSignatureForError (), false_type.GetSignatureForError ()); + return null; + } + } + + Constant c = expr as Constant; + if (c != null) { + bool is_false = c.IsDefaultValue; + + // + // Don't issue the warning for constant expressions + // + if (!(is_false ? true_expr is Constant : false_expr is Constant)) { + // CSC: Missing warning + Warning_UnreachableExpression (ec, is_false ? true_expr.Location : false_expr.Location); + } + + return ReducedExpression.Create ( + is_false ? false_expr : true_expr, this, + false_expr is Constant && true_expr is Constant).Resolve (ec); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + Label false_target = ec.DefineLabel (); + Label end_target = ec.DefineLabel (); + + expr.EmitBranchable (ec, false_target, false); + true_expr.Emit (ec); + + // + // Verifier doesn't support interface merging. When there are two types on + // the stack without common type hint and the common type is an interface. + // Use temporary local to give verifier hint on what type to unify the stack + // + if (type.IsInterface && true_expr is EmptyCast && false_expr is EmptyCast) { + var temp = ec.GetTemporaryLocal (type); + ec.Emit (OpCodes.Stloc, temp); + ec.Emit (OpCodes.Ldloc, temp); + ec.FreeTemporaryLocal (temp, type); + } + + ec.Emit (OpCodes.Br, end_target); + ec.MarkLabel (false_target); + false_expr.Emit (ec); + ec.MarkLabel (end_target); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysisConditional (fc); + var expr_true = fc.DefiniteAssignmentOnTrue; + var expr_false = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_true); + true_expr.FlowAnalysis (fc); + var true_fc = fc.DefiniteAssignment; + + fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_false); + false_expr.FlowAnalysis (fc); + + fc.DefiniteAssignment &= true_fc; + } + + public override void FlowAnalysisConditional (FlowAnalysisContext fc) + { + expr.FlowAnalysisConditional (fc); + var expr_true = fc.DefiniteAssignmentOnTrue; + var expr_false = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_true); + true_expr.FlowAnalysisConditional (fc); + var true_fc = fc.DefiniteAssignment; + var true_da_true = fc.DefiniteAssignmentOnTrue; + var true_da_false = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_false); + false_expr.FlowAnalysisConditional (fc); + + fc.DefiniteAssignment &= true_fc; + fc.DefiniteAssignmentOnTrue = true_da_true & fc.DefiniteAssignmentOnTrue; + fc.DefiniteAssignmentOnFalse = true_da_false & fc.DefiniteAssignmentOnFalse; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Conditional target = (Conditional) t; + + target.expr = expr.Clone (clonectx); + target.true_expr = true_expr.Clone (clonectx); + target.false_expr = false_expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation, IVariableReference + { + LocalTemporary temp; + + #region Abstract + public abstract HoistedVariable GetHoistedVariable (AnonymousExpression ae); + public abstract void SetHasAddressTaken (); + + public abstract bool IsLockedByStatement { get; set; } + + public abstract bool IsFixed { get; } + public abstract bool IsRef { get; } + public abstract string Name { get; } + + // + // Variable IL data, it has to be protected to encapsulate hoisted variables + // + protected abstract ILocalVariable Variable { get; } + + // + // Variable flow-analysis data + // + public abstract VariableInfo VariableInfo { get; } + #endregion + + public virtual void AddressOf (EmitContext ec, AddressOp mode) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) { + hv.AddressOf (ec, mode); + return; + } + + Variable.EmitAddressOf (ec); + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) + return hv.CreateExpressionTree (); + + Arguments arg = new Arguments (1); + arg.Add (new Argument (this)); + return CreateExpressionFactoryCall (ec, "Constant", arg); + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + if (IsLockedByStatement) { + rc.Report.Warning (728, 2, loc, + "Possibly incorrect assignment to `{0}' which is the argument to a using or lock statement", + Name); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public override void EmitSideEffect (EmitContext ec) + { + // do nothing + } + + // + // This method is used by parameters that are references, that are + // being passed as references: we only want to pass the pointer (that + // is already stored in the parameter, not the address of the pointer, + // and not the value of the variable). + // + public void EmitLoad (EmitContext ec) + { + Variable.Emit (ec); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) { + hv.Emit (ec, leave_copy); + return; + } + + EmitLoad (ec); + + if (IsRef) { + // + // If we are a reference, we loaded on the stack a pointer + // Now lets load the real value + // + ec.EmitLoadFromPtr (type); + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + + if (IsRef) { + temp = new LocalTemporary (Type); + temp.Store (ec); + } + } + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, + bool prepare_for_load) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) { + hv.EmitAssign (ec, source, leave_copy, prepare_for_load); + return; + } + + New n_source = source as New; + if (n_source != null) { + if (!n_source.Emit (ec, this)) { + if (leave_copy) { + EmitLoad (ec); + if (IsRef) + ec.EmitLoadFromPtr (type); + } + return; + } + } else { + if (IsRef) + EmitLoad (ec); + + source.Emit (ec); + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + if (IsRef) { + temp = new LocalTemporary (Type); + temp.Store (ec); + } + } + + if (IsRef) + ec.EmitStoreFromPtr (type); + else + Variable.EmitAssign (ec); + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } + } + + public override Expression EmitToField (EmitContext ec) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) { + return hv.EmitToField (ec); + } + + return base.EmitToField (ec); + } + + public HoistedVariable GetHoistedVariable (ResolveContext rc) + { + return GetHoistedVariable (rc.CurrentAnonymousMethod); + } + + public HoistedVariable GetHoistedVariable (EmitContext ec) + { + return GetHoistedVariable (ec.CurrentAnonymousMethod); + } + + public override string GetSignatureForError () + { + return Name; + } + + public bool IsHoisted { + get { return GetHoistedVariable ((AnonymousExpression) null) != null; } + } + } + + // + // Resolved reference to a local variable + // + public class LocalVariableReference : VariableReference + { + public LocalVariable local_info; + + public LocalVariableReference (LocalVariable li, Location l) + { + this.local_info = li; + loc = l; + } + + public override VariableInfo VariableInfo { + get { return local_info.VariableInfo; } + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + return local_info.HoistedVariant; + } + + #region Properties + + // + // A local variable is always fixed + // + public override bool IsFixed { + get { + return true; + } + } + + public override bool IsLockedByStatement { + get { + return local_info.IsLocked; + } + set { + local_info.IsLocked = value; + } + } + + public override bool IsRef { + get { return false; } + } + + public override string Name { + get { return local_info.Name; } + } + + #endregion + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + VariableInfo variable_info = VariableInfo; + if (variable_info == null) + return; + + if (fc.IsDefinitelyAssigned (variable_info)) + return; + + fc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name); + variable_info.SetAssigned (fc.DefiniteAssignment, true); + } + + public override void SetHasAddressTaken () + { + local_info.SetHasAddressTaken (); + } + + void DoResolveBase (ResolveContext ec) + { + // + // If we are referencing a variable from the external block + // flag it for capturing + // + if (ec.MustCaptureVariable (local_info)) { + if (local_info.AddressTaken) { + AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc); + } else if (local_info.IsFixed) { + ec.Report.Error (1764, loc, + "Cannot use fixed local `{0}' inside an anonymous method, lambda expression or query expression", + GetSignatureForError ()); + } + + if (ec.IsVariableCapturingRequired) { + AnonymousMethodStorey storey = local_info.Block.Explicit.CreateAnonymousMethodStorey (ec); + storey.CaptureLocalVariable (ec, local_info); + } + } + + eclass = ExprClass.Variable; + type = local_info.Type; + } + + protected override Expression DoResolve (ResolveContext ec) + { + local_info.SetIsUsed (); + + DoResolveBase (ec); + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression rhs) + { + // + // Don't be too pedantic when variable is used as out param or for some broken code + // which uses property/indexer access to run some initialization + // + if (rhs == EmptyExpression.OutAccess || rhs.eclass == ExprClass.PropertyAccess || rhs.eclass == ExprClass.IndexerAccess) + local_info.SetIsUsed (); + + if (local_info.IsReadonly && !ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.UsingInitializerScope)) { + if (rhs == EmptyExpression.LValueMemberAccess) { + // CS1654 already reported + } else { + int code; + string msg; + if (rhs == EmptyExpression.OutAccess) { + code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'"; + } else if (rhs == EmptyExpression.LValueMemberOutAccess) { + code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'"; + } else if (rhs == EmptyExpression.UnaryAddress) { + code = 459; msg = "Cannot take the address of {1} `{0}'"; + } else { + code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'"; + } + ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ()); + } + } + + if (eclass == ExprClass.Unresolved) + DoResolveBase (ec); + + return base.DoResolveLValue (ec, rhs); + } + + public override int GetHashCode () + { + return local_info.GetHashCode (); + } + + public override bool Equals (object obj) + { + LocalVariableReference lvr = obj as LocalVariableReference; + if (lvr == null) + return false; + + return local_info == lvr.local_info; + } + + protected override ILocalVariable Variable { + get { return local_info; } + } + + public override string ToString () + { + return String.Format ("{0} ({1}:{2})", GetType (), Name, loc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// This represents a reference to a parameter in the intermediate + /// representation. + /// + public class ParameterReference : VariableReference + { + protected ParametersBlock.ParameterInfo pi; + + public ParameterReference (ParametersBlock.ParameterInfo pi, Location loc) + { + this.pi = pi; + this.loc = loc; + } + + #region Properties + + public override bool IsLockedByStatement { + get { + return pi.IsLocked; + } + set { + pi.IsLocked = value; + } + } + + public override bool IsRef { + get { return (pi.Parameter.ModFlags & Parameter.Modifier.RefOutMask) != 0; } + } + + bool HasOutModifier { + get { return (pi.Parameter.ModFlags & Parameter.Modifier.OUT) != 0; } + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + return pi.Parameter.HoistedVariant; + } + + // + // A ref or out parameter is classified as a moveable variable, even + // if the argument given for the parameter is a fixed variable + // + public override bool IsFixed { + get { return !IsRef; } + } + + public override string Name { + get { return Parameter.Name; } + } + + public Parameter Parameter { + get { return pi.Parameter; } + } + + public override VariableInfo VariableInfo { + get { return pi.VariableInfo; } + } + + protected override ILocalVariable Variable { + get { return Parameter; } + } + + #endregion + + public override void AddressOf (EmitContext ec, AddressOp mode) + { + // + // ParameterReferences might already be a reference + // + if (IsRef) { + EmitLoad (ec); + return; + } + + base.AddressOf (ec, mode); + } + + public override void SetHasAddressTaken () + { + Parameter.HasAddressTaken = true; + } + + bool DoResolveBase (ResolveContext ec) + { + if (eclass != ExprClass.Unresolved) + return true; + + type = pi.ParameterType; + eclass = ExprClass.Variable; + + // + // If we are referencing a parameter from the external block + // flag it for capturing + // + if (ec.MustCaptureVariable (pi)) { + if (Parameter.HasAddressTaken) + AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc); + + if (IsRef) { + ec.Report.Error (1628, loc, + "Parameter `{0}' cannot be used inside `{1}' when using `ref' or `out' modifier", + Name, ec.CurrentAnonymousMethod.ContainerType); + } + + if (ec.IsVariableCapturingRequired && !pi.Block.ParametersBlock.IsExpressionTree) { + AnonymousMethodStorey storey = pi.Block.Explicit.CreateAnonymousMethodStorey (ec); + storey.CaptureParameter (ec, pi, this); + } + } + + return true; + } + + public override int GetHashCode () + { + return Name.GetHashCode (); + } + + public override bool Equals (object obj) + { + ParameterReference pr = obj as ParameterReference; + if (pr == null) + return false; + + return Name == pr.Name; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + // Nothing to clone + return; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) + return hv.CreateExpressionTree (); + + return Parameter.ExpressionTreeVariableReference (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (!DoResolveBase (ec)) + return null; + + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (!DoResolveBase (ec)) + return null; + + if (Parameter.HoistedVariant != null) + Parameter.HoistedVariant.IsAssigned = true; + + return base.DoResolveLValue (ec, right_side); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + VariableInfo variable_info = VariableInfo; + if (variable_info == null) + return; + + if (fc.IsDefinitelyAssigned (variable_info)) + return; + + fc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name); + fc.SetVariableAssigned (variable_info); + } + } + + /// + /// Invocation of methods or delegates. + /// + public class Invocation : ExpressionStatement + { + public class Predefined : Invocation + { + public Predefined (MethodGroupExpr expr, Arguments arguments) + : base (expr, arguments) + { + this.mg = expr; + } + + protected override MethodGroupExpr DoResolveOverload (ResolveContext rc) + { + if (!rc.IsObsolete) { + var member = mg.BestCandidate; + ObsoleteAttribute oa = member.GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, member.GetSignatureForError (), loc, rc.Report); + } + + return mg; + } + } + + protected Arguments arguments; + protected Expression expr; + protected MethodGroupExpr mg; + bool conditional_access_receiver; + + public Invocation (Expression expr, Arguments arguments) + { + this.expr = expr; + this.arguments = arguments; + if (expr != null) { + loc = expr.Location; + } + } + + #region Properties + public Arguments Arguments { + get { + return arguments; + } + } + + public Expression Exp { + get { + return expr; + } + } + + public MethodGroupExpr MethodGroup { + get { + return mg; + } + } + + public override Location StartLocation { + get { + return expr.StartLocation; + } + } + + #endregion + + public override MethodGroupExpr CanReduceLambda (AnonymousMethodBody body) + { + if (MethodGroup == null) + return null; + + var candidate = MethodGroup.BestCandidate; + if (candidate == null || !(candidate.IsStatic || Exp is This)) + return null; + + var args_count = arguments == null ? 0 : arguments.Count; + if (args_count != body.Parameters.Count) + return null; + + var lambda_parameters = body.Block.Parameters.FixedParameters; + for (int i = 0; i < args_count; ++i) { + var pr = arguments[i].Expr as ParameterReference; + if (pr == null) + return null; + + if (lambda_parameters[i] != pr.Parameter) + return null; + + if ((lambda_parameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (pr.Parameter.ModFlags & Parameter.Modifier.RefOutMask)) + return null; + } + + var emg = MethodGroup as ExtensionMethodGroupExpr; + if (emg != null) { + var mg = MethodGroupExpr.CreatePredefined (candidate, candidate.DeclaringType, MethodGroup.Location); + if (candidate.IsGeneric) { + var targs = new TypeExpression [candidate.Arity]; + for (int i = 0; i < targs.Length; ++i) { + targs[i] = new TypeExpression (candidate.TypeArguments[i], MethodGroup.Location); + } + + mg.SetTypeArguments (null, new TypeArguments (targs)); + } + + return mg; + } + + return MethodGroup; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Invocation target = (Invocation) t; + + if (arguments != null) + target.arguments = arguments.Clone (clonectx); + + target.expr = expr.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + if (arguments != null && arguments.ContainsEmitWithAwait ()) + return true; + + return mg.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Expression instance = mg.IsInstance ? + mg.InstanceExpression.CreateExpressionTree (ec) : + new NullLiteral (loc); + + var args = Arguments.CreateForExpressionTree (ec, arguments, + instance, + mg.CreateExpressionTree (ec)); + + return CreateExpressionFactoryCall (ec, "Call", args); + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) { + if (expr.HasConditionalAccess ()) { + conditional_access_receiver = true; + using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) { + return DoResolveInvocation (rc); + } + } + } + + return DoResolveInvocation (rc); + } + + Expression DoResolveInvocation (ResolveContext ec) + { + Expression member_expr; + var atn = expr as ATypeNameExpression; + if (atn != null) { + member_expr = atn.LookupNameExpression (ec, MemberLookupRestrictions.InvocableOnly | MemberLookupRestrictions.ReadAccess); + if (member_expr != null) { + var name_of = member_expr as NameOf; + if (name_of != null) { + return name_of.ResolveOverload (ec, arguments); + } + + member_expr = member_expr.Resolve (ec); + } + } else { + member_expr = expr.Resolve (ec); + } + + if (member_expr == null) + return null; + + // + // Next, evaluate all the expressions in the argument list + // + bool dynamic_arg = false; + if (arguments != null) + arguments.Resolve (ec, out dynamic_arg); + + TypeSpec expr_type = member_expr.Type; + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return DoResolveDynamic (ec, member_expr); + + mg = member_expr as MethodGroupExpr; + Expression invoke = null; + + if (mg == null) { + if (expr_type != null && expr_type.IsDelegate) { + invoke = new DelegateInvocation (member_expr, arguments, conditional_access_receiver, loc); + invoke = invoke.Resolve (ec); + if (invoke == null || !dynamic_arg) + return invoke; + } else { + if (member_expr is RuntimeValueExpression) { + ec.Report.Error (Report.RuntimeErrorId, loc, "Cannot invoke a non-delegate type `{0}'", + member_expr.Type.GetSignatureForError ()); + return null; + } + + MemberExpr me = member_expr as MemberExpr; + if (me == null) { + member_expr.Error_UnexpectedKind (ec, ResolveFlags.MethodGroup, loc); + return null; + } + + ec.Report.Error (1955, loc, "The member `{0}' cannot be used as method or delegate", + member_expr.GetSignatureForError ()); + return null; + } + } + + if (invoke == null) { + mg = DoResolveOverload (ec); + if (mg == null) + return null; + } + + if (dynamic_arg) + return DoResolveDynamic (ec, member_expr); + + var method = mg.BestCandidate; + type = mg.BestCandidateReturnType; + if (conditional_access_receiver) + type = LiftMemberType (ec, type); + + if (arguments == null && method.DeclaringType.BuiltinType == BuiltinTypeSpec.Type.Object && method.Name == Destructor.MetadataName) { + if (mg.IsBase) + ec.Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor"); + else + ec.Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available"); + return null; + } + + IsSpecialMethodInvocation (ec, method, loc); + + eclass = ExprClass.Value; + return this; + } + + protected virtual Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr) + { + Arguments args; + DynamicMemberBinder dmb = memberExpr as DynamicMemberBinder; + if (dmb != null) { + args = dmb.Arguments; + if (arguments != null) + args.AddRange (arguments); + } else if (mg == null) { + if (arguments == null) + args = new Arguments (1); + else + args = arguments; + + args.Insert (0, new Argument (memberExpr)); + this.expr = null; + } else { + if (mg.IsBase) { + ec.Report.Error (1971, loc, + "The base call to method `{0}' cannot be dynamically dispatched. Consider casting the dynamic arguments or eliminating the base access", + mg.Name); + return null; + } + + if (arguments == null) + args = new Arguments (1); + else + args = arguments; + + MemberAccess ma = expr as MemberAccess; + if (ma != null) { + var inst = mg.InstanceExpression; + var left_type = inst as TypeExpr; + if (left_type != null) { + args.Insert (0, new Argument (new TypeOf (left_type.Type, loc).Resolve (ec), Argument.AType.DynamicTypeName)); + } else if (inst != null) { + // + // Any value type has to be pass as by-ref to get back the same + // instance on which the member was called + // + var mod = inst is IMemoryLocation && TypeSpec.IsValueType (inst.Type) ? + Argument.AType.Ref : Argument.AType.None; + args.Insert (0, new Argument (inst.Resolve (ec), mod)); + } + } else { // is SimpleName + if (ec.IsStatic) { + args.Insert (0, new Argument (new TypeOf (ec.CurrentType, loc).Resolve (ec), Argument.AType.DynamicTypeName)); + } else { + args.Insert (0, new Argument (new This (loc).Resolve (ec))); + } + } + } + + return new DynamicInvocation (expr as ATypeNameExpression, args, loc).Resolve (ec); + } + + protected virtual MethodGroupExpr DoResolveOverload (ResolveContext ec) + { + return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.None); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (mg.IsConditionallyExcluded) + return; + + mg.FlowAnalysis (fc); + + if (arguments != null) + arguments.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + public override string GetSignatureForError () + { + return mg.GetSignatureForError (); + } + + public override bool HasConditionalAccess () + { + return expr.HasConditionalAccess (); + } + + // + // If a member is a method or event, or if it is a constant, field or property of either a delegate type + // or the type dynamic, then the member is invocable + // + public static bool IsMemberInvocable (MemberSpec member) + { + switch (member.Kind) { + case MemberKind.Event: + return true; + case MemberKind.Field: + case MemberKind.Property: + var m = member as IInterfaceMemberSpec; + return m.MemberType.IsDelegate || m.MemberType.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + default: + return false; + } + } + + public static bool IsSpecialMethodInvocation (ResolveContext ec, MethodSpec method, Location loc) + { + if (!method.IsReservedMethod) + return false; + + if (ec.HasSet (ResolveContext.Options.InvokeSpecialName) || ec.CurrentMemberDefinition.IsCompilerGenerated) + return false; + + ec.Report.SymbolRelatedToPreviousError (method); + ec.Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor", + method.GetSignatureForError ()); + + return true; + } + + public override void Emit (EmitContext ec) + { + if (mg.IsConditionallyExcluded) + return; + + if (conditional_access_receiver) + mg.EmitCall (ec, arguments, type, false); + else + mg.EmitCall (ec, arguments, false); + } + + public override void EmitStatement (EmitContext ec) + { + if (mg.IsConditionallyExcluded) + return; + + if (conditional_access_receiver) + mg.EmitCall (ec, arguments, type, true); + else + mg.EmitCall (ec, arguments, true); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return MakeExpression (ctx, mg.InstanceExpression, mg.BestCandidate, arguments); + } + + public static SLE.Expression MakeExpression (BuilderContext ctx, Expression instance, MethodSpec mi, Arguments args) + { +#if STATIC + throw new NotSupportedException (); +#else + var instance_expr = instance == null ? null : instance.MakeExpression (ctx); + return SLE.Expression.Call (instance_expr, (MethodInfo) mi.GetMetaInfo (), Arguments.MakeExpression (args, ctx)); +#endif + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Implements simple new expression + // + public class New : ExpressionStatement, IMemoryLocation + { + protected Arguments arguments; + + // + // During bootstrap, it contains the RequestedType, + // but if `type' is not null, it *might* contain a NewDelegate + // (because of field multi-initialization) + // + protected Expression RequestedType; + + protected MethodSpec method; + + public New (Expression requested_type, Arguments arguments, Location l) + { + RequestedType = requested_type; + this.arguments = arguments; + loc = l; + } + + #region Properties + public Arguments Arguments { + get { + return arguments; + } + } + + public Expression TypeRequested { + get { + return RequestedType; + } + } + + // + // Returns true for resolved `new S()' + // + public bool IsDefaultStruct { + get { + return arguments == null && type.IsStruct && GetType () == typeof (New); + } + } + + public Expression TypeExpression { + get { + return RequestedType; + } + } + + #endregion + + /// + /// Converts complex core type syntax like 'new int ()' to simple constant + /// + public static Constant Constantify (TypeSpec t, Location loc) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + return new IntConstant (t, 0, loc); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (t, 0, loc); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (t, 0, loc); + case BuiltinTypeSpec.Type.UShort: + return new UShortConstant (t, 0, loc); + case BuiltinTypeSpec.Type.SByte: + return new SByteConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Byte: + return new ByteConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Char: + return new CharConstant (t, '\0', loc); + case BuiltinTypeSpec.Type.Bool: + return new BoolConstant (t, false, loc); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (t, 0, loc); + } + + if (t.IsEnum) + return new EnumConstant (Constantify (EnumSpec.GetUnderlyingType (t), loc), t); + + if (t.IsNullableType) + return Nullable.LiftedNull.Create (t, loc); + + return null; + } + + public override bool ContainsEmitWithAwait () + { + return arguments != null && arguments.ContainsEmitWithAwait (); + } + + // + // Checks whether the type is an interface that has the + // [ComImport, CoClass] attributes and must be treated + // specially + // + public Expression CheckComImport (ResolveContext ec) + { + if (!type.IsInterface) + return null; + + // + // Turn the call into: + // (the-interface-stated) (new class-referenced-in-coclassattribute ()) + // + var real_class = type.MemberDefinition.GetAttributeCoClass (); + if (real_class == null) + return null; + + New proxy = new New (new TypeExpression (real_class, loc), arguments, loc); + Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc); + return cast.Resolve (ec); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args; + if (method == null) { + args = new Arguments (1); + args.Add (new Argument (new TypeOf (type, loc))); + } else { + args = Arguments.CreateForExpressionTree (ec, + arguments, new TypeOfMethod (method, loc)); + } + + return CreateExpressionFactoryCall (ec, "New", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = RequestedType.ResolveAsType (ec); + if (type == null) + return null; + + eclass = ExprClass.Value; + + if (type.IsPointer) { + ec.Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression", + type.GetSignatureForError ()); + return null; + } + + if (arguments == null) { + Constant c = Constantify (type, RequestedType.Location); + if (c != null) + return ReducedExpression.Create (c, this); + } + + if (type.IsDelegate) { + return (new NewDelegate (type, arguments, loc)).Resolve (ec); + } + + var tparam = type as TypeParameterSpec; + if (tparam != null) { + // + // Check whether the type of type parameter can be constructed. BaseType can be a struct for method overrides + // where type parameter constraint is inflated to struct + // + if ((tparam.SpecialConstraint & (SpecialConstraint.Struct | SpecialConstraint.Constructor)) == 0 && !TypeSpec.IsValueType (tparam)) { + ec.Report.Error (304, loc, + "Cannot create an instance of the variable type `{0}' because it does not have the new() constraint", + type.GetSignatureForError ()); + } + + if ((arguments != null) && (arguments.Count != 0)) { + ec.Report.Error (417, loc, + "`{0}': cannot provide arguments when creating an instance of a variable type", + type.GetSignatureForError ()); + } + + return this; + } + + if (type.IsStatic) { + ec.Report.SymbolRelatedToPreviousError (type); + ec.Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", type.GetSignatureForError ()); + return null; + } + + if (type.IsInterface || type.IsAbstract){ + if (!TypeManager.IsGenericType (type)) { + RequestedType = CheckComImport (ec); + if (RequestedType != null) + return RequestedType; + } + + ec.Report.SymbolRelatedToPreviousError (type); + ec.Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", type.GetSignatureForError ()); + return null; + } + + // + // Any struct always defines parameterless constructor + // + if (type.IsStruct && arguments == null) + return this; + + bool dynamic; + if (arguments != null) { + arguments.Resolve (ec, out dynamic); + } else { + dynamic = false; + } + + method = ConstructorLookup (ec, type, ref arguments, loc); + + if (dynamic) { + arguments.Insert (0, new Argument (new TypeOf (type, loc).Resolve (ec), Argument.AType.DynamicTypeName)); + return new DynamicConstructorBinder (type, arguments, loc).Resolve (ec); + } + + return this; + } + + bool DoEmitTypeParameter (EmitContext ec) + { + var m = ec.Module.PredefinedMembers.ActivatorCreateInstance.Resolve (loc); + if (m == null) + return true; + + var ctor_factory = m.MakeGenericMethod (ec.MemberContext, type); + var tparam = (TypeParameterSpec) type; + + if (tparam.IsReferenceType) { + ec.Emit (OpCodes.Call, ctor_factory); + return true; + } + + // Allow DoEmit() to be called multiple times. + // We need to create a new LocalTemporary each time since + // you can't share LocalBuilders among ILGeneators. + LocalTemporary temp = new LocalTemporary (type); + + Label label_activator = ec.DefineLabel (); + Label label_end = ec.DefineLabel (); + + temp.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Initobj, type); + + temp.Emit (ec); + ec.Emit (OpCodes.Box, type); + ec.Emit (OpCodes.Brfalse, label_activator); + + temp.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Initobj, type); + temp.Emit (ec); + temp.Release (ec); + ec.Emit (OpCodes.Br_S, label_end); + + ec.MarkLabel (label_activator); + + ec.Emit (OpCodes.Call, ctor_factory); + ec.MarkLabel (label_end); + return true; + } + + // + // This Emit can be invoked in two contexts: + // * As a mechanism that will leave a value on the stack (new object) + // * As one that wont (init struct) + // + // If we are dealing with a ValueType, we have a few + // situations to deal with: + // + // * The target is a ValueType, and we have been provided + // the instance (this is easy, we are being assigned). + // + // * The target of New is being passed as an argument, + // to a boxing operation or a function that takes a + // ValueType. + // + // In this case, we need to create a temporary variable + // that is the argument of New. + // + // Returns whether a value is left on the stack + // + // *** Implementation note *** + // + // To benefit from this optimization, each assignable expression + // has to manually cast to New and call this Emit. + // + // TODO: It's worth to implement it for arrays and fields + // + public virtual bool Emit (EmitContext ec, IMemoryLocation target) + { + bool is_value_type = TypeSpec.IsValueType (type); + VariableReference vr = target as VariableReference; + + if (target != null && is_value_type && (vr != null || method == null)) { + target.AddressOf (ec, AddressOp.Store); + } else if (vr != null && vr.IsRef) { + vr.EmitLoad (ec); + } + + if (arguments != null) { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && (arguments.Count > (this is NewInitialize ? 0 : 1)) && arguments.ContainsEmitWithAwait ()) + arguments = arguments.Emit (ec, false, true); + + arguments.Emit (ec); + } + + if (is_value_type) { + if (method == null) { + ec.Emit (OpCodes.Initobj, type); + return false; + } + + if (vr != null) { + ec.MarkCallEntry (loc); + ec.Emit (OpCodes.Call, method); + return false; + } + } + + if (type is TypeParameterSpec) + return DoEmitTypeParameter (ec); + + ec.MarkCallEntry (loc); + ec.Emit (OpCodes.Newobj, method); + return true; + } + + public override void Emit (EmitContext ec) + { + LocalTemporary v = null; + if (method == null && TypeSpec.IsValueType (type)) { + // TODO: Use temporary variable from pool + v = new LocalTemporary (type); + } + + if (!Emit (ec, v)) + v.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + LocalTemporary v = null; + if (method == null && TypeSpec.IsValueType (type)) { + // TODO: Use temporary variable from pool + v = new LocalTemporary (type); + } + + if (Emit (ec, v)) + ec.Emit (OpCodes.Pop); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (arguments != null) + arguments.FlowAnalysis (fc); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + EmitAddressOf (ec, mode); + } + + protected virtual IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp mode) + { + LocalTemporary value_target = new LocalTemporary (type); + + if (type is TypeParameterSpec) { + DoEmitTypeParameter (ec); + value_target.Store (ec); + value_target.AddressOf (ec, mode); + return value_target; + } + + value_target.AddressOf (ec, AddressOp.Store); + + if (method == null) { + ec.Emit (OpCodes.Initobj, type); + } else { + if (arguments != null) + arguments.Emit (ec); + + ec.Emit (OpCodes.Call, method); + } + + value_target.AddressOf (ec, mode); + return value_target; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + New target = (New) t; + + target.RequestedType = RequestedType.Clone (clonectx); + if (arguments != null){ + target.arguments = arguments.Clone (clonectx); + } + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.New ((ConstructorInfo) method.GetMetaInfo (), Arguments.MakeExpression (arguments, ctx)); +#endif + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Array initializer expression, the expression is allowed in + // variable or field initialization only which makes it tricky as + // the type has to be infered based on the context either from field + // type or variable type (think of multiple declarators) + // + public class ArrayInitializer : Expression + { + List elements; + BlockVariable variable; + + public ArrayInitializer (List init, Location loc) + { + elements = init; + this.loc = loc; + } + + public ArrayInitializer (int count, Location loc) + : this (new List (count), loc) + { + } + + public ArrayInitializer (Location loc) + : this (4, loc) + { + } + + #region Properties + + public int Count { + get { return elements.Count; } + } + + public List Elements { + get { + return elements; + } + } + + public Expression this [int index] { + get { + return elements [index]; + } + } + + public BlockVariable VariableDeclaration { + get { + return variable; + } + set { + variable = value; + } + } + #endregion + + public void Add (Expression expr) + { + elements.Add (expr); + } + + public override bool ContainsEmitWithAwait () + { + throw new NotSupportedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + var target = (ArrayInitializer) t; + + target.elements = new List (elements.Count); + foreach (var element in elements) + target.elements.Add (element.Clone (clonectx)); + } + + protected override Expression DoResolve (ResolveContext rc) + { + var current_field = rc.CurrentMemberDefinition as FieldBase; + TypeExpression type; + if (current_field != null && rc.CurrentAnonymousMethod == null) { + type = new TypeExpression (current_field.MemberType, current_field.Location); + } else if (variable != null) { + if (variable.TypeExpression is VarExpr) { + rc.Report.Error (820, loc, "An implicitly typed local variable declarator cannot use an array initializer"); + return EmptyExpression.Null; + } + + type = new TypeExpression (variable.Variable.Type, variable.Variable.Location); + } else { + throw new NotImplementedException ("Unexpected array initializer context"); + } + + return new ArrayCreation (type, this).Resolve (rc); + } + + public override void Emit (EmitContext ec) + { + throw new InternalErrorException ("Missing Resolve call"); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + throw new InternalErrorException ("Missing Resolve call"); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// 14.5.10.2: Represents an array creation expression. + /// + /// + /// + /// There are two possible scenarios here: one is an array creation + /// expression that specifies the dimensions and optionally the + /// initialization data and the other which does not need dimensions + /// specified but where initialization data is mandatory. + /// + public class ArrayCreation : Expression + { + FullNamedExpression requested_base_type; + ArrayInitializer initializers; + + // + // The list of Argument types. + // This is used to construct the `newarray' or constructor signature + // + protected List arguments; + + protected TypeSpec array_element_type; + int num_arguments; + protected int dimensions; + protected readonly ComposedTypeSpecifier rank; + Expression first_emit; + LocalTemporary first_emit_temp; + + protected List array_data; + + Dictionary bounds; + +#if STATIC + // The number of constants in array initializers + int const_initializers_count; + bool only_constant_initializers; +#endif + public ArrayCreation (FullNamedExpression requested_base_type, List exprs, ComposedTypeSpecifier rank, ArrayInitializer initializers, Location l) + : this (requested_base_type, rank, initializers, l) + { + arguments = exprs; + num_arguments = arguments.Count; + } + + // + // For expressions like int[] foo = new int[] { 1, 2, 3 }; + // + public ArrayCreation (FullNamedExpression requested_base_type, ComposedTypeSpecifier rank, ArrayInitializer initializers, Location loc) + { + this.requested_base_type = requested_base_type; + this.rank = rank; + this.initializers = initializers; + this.loc = loc; + + if (rank != null) + num_arguments = rank.Dimension; + } + + // + // For compiler generated single dimensional arrays only + // + public ArrayCreation (FullNamedExpression requested_base_type, ArrayInitializer initializers, Location loc) + : this (requested_base_type, ComposedTypeSpecifier.SingleDimension, initializers, loc) + { + } + + // + // For expressions like int[] foo = { 1, 2, 3 }; + // + public ArrayCreation (FullNamedExpression requested_base_type, ArrayInitializer initializers) + : this (requested_base_type, null, initializers, initializers.Location) + { + } + + public ComposedTypeSpecifier Rank { + get { + return this.rank; + } + } + + public FullNamedExpression TypeExpression { + get { + return this.requested_base_type; + } + } + + public ArrayInitializer Initializers { + get { + return this.initializers; + } + } + + public List Arguments { + get { return this.arguments; } + } + + bool CheckIndices (ResolveContext ec, ArrayInitializer probe, int idx, bool specified_dims, int child_bounds) + { + if (initializers != null && bounds == null) { + // + // We use this to store all the data values in the order in which we + // will need to store them in the byte blob later + // + array_data = new List (probe.Count); + bounds = new Dictionary (); + } + + if (specified_dims) { + Expression a = arguments [idx]; + a = a.Resolve (ec); + if (a == null) + return false; + + a = ConvertExpressionToArrayIndex (ec, a); + if (a == null) + return false; + + arguments[idx] = a; + + if (initializers != null) { + Constant c = a as Constant; + if (c == null && a is ArrayIndexCast) + c = ((ArrayIndexCast) a).Child as Constant; + + if (c == null) { + ec.Report.Error (150, a.Location, "A constant value is expected"); + return false; + } + + int value; + try { + value = System.Convert.ToInt32 (c.GetValue ()); + } catch { + ec.Report.Error (150, a.Location, "A constant value is expected"); + return false; + } + + // TODO: probe.Count does not fit ulong in + if (value != probe.Count) { + ec.Report.Error (847, loc, "An array initializer of length `{0}' was expected", value.ToString ()); + return false; + } + + bounds[idx] = value; + } + } + + if (initializers == null) + return true; + + for (int i = 0; i < probe.Count; ++i) { + var o = probe [i]; + if (o is ArrayInitializer) { + var sub_probe = o as ArrayInitializer; + if (idx + 1 >= dimensions){ + ec.Report.Error (623, loc, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead"); + return false; + } + + // When we don't have explicitly specified dimensions, record whatever dimension we first encounter at each level + if (!bounds.ContainsKey(idx + 1)) + bounds[idx + 1] = sub_probe.Count; + + if (bounds[idx + 1] != sub_probe.Count) { + ec.Report.Error(847, sub_probe.Location, "An array initializer of length `{0}' was expected", bounds[idx + 1].ToString()); + return false; + } + + bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims, child_bounds - 1); + if (!ret) + return false; + } else if (child_bounds > 1) { + ec.Report.Error (846, o.Location, "A nested array initializer was expected"); + } else { + Expression element = ResolveArrayElement (ec, o); + if (element == null) + continue; +#if STATIC + // Initializers with the default values can be ignored + Constant c = element as Constant; + if (c != null) { + if (!c.IsDefaultInitializer (array_element_type)) { + ++const_initializers_count; + } + } else { + only_constant_initializers = false; + } +#endif + array_data.Add (element); + } + } + + return true; + } + + public override bool ContainsEmitWithAwait () + { + foreach (var arg in arguments) { + if (arg.ContainsEmitWithAwait ()) + return true; + } + + return InitializersContainAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args; + + if (array_data == null) { + args = new Arguments (arguments.Count + 1); + args.Add (new Argument (new TypeOf (array_element_type, loc))); + foreach (Expression a in arguments) + args.Add (new Argument (a.CreateExpressionTree (ec))); + + return CreateExpressionFactoryCall (ec, "NewArrayBounds", args); + } + + if (dimensions > 1) { + ec.Report.Error (838, loc, "An expression tree cannot contain a multidimensional array initializer"); + return null; + } + + args = new Arguments (array_data == null ? 1 : array_data.Count + 1); + args.Add (new Argument (new TypeOf (array_element_type, loc))); + if (array_data != null) { + for (int i = 0; i < array_data.Count; ++i) { + Expression e = array_data [i]; + args.Add (new Argument (e.CreateExpressionTree (ec))); + } + } + + return CreateExpressionFactoryCall (ec, "NewArrayInit", args); + } + + void UpdateIndices (ResolveContext rc) + { + int i = 0; + for (var probe = initializers; probe != null;) { + Expression e = new IntConstant (rc.BuiltinTypes, probe.Count, Location.Null); + arguments.Add (e); + bounds[i++] = probe.Count; + + if (probe.Count > 0 && probe [0] is ArrayInitializer) { + probe = (ArrayInitializer) probe[0]; + } else if (dimensions > i) { + continue; + } else { + return; + } + } + } + + protected override void Error_NegativeArrayIndex (ResolveContext ec, Location loc) + { + ec.Report.Error (248, loc, "Cannot create an array with a negative size"); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + foreach (var arg in arguments) + arg.FlowAnalysis (fc); + + if (array_data != null) { + foreach (var ad in array_data) + ad.FlowAnalysis (fc); + } + } + + bool InitializersContainAwait () + { + if (array_data == null) + return false; + + foreach (var expr in array_data) { + if (expr.ContainsEmitWithAwait ()) + return true; + } + + return false; + } + + protected virtual Expression ResolveArrayElement (ResolveContext ec, Expression element) + { + element = element.Resolve (ec); + if (element == null) + return null; + + if (element is CompoundAssign.TargetExpression) { + if (first_emit != null) + throw new InternalErrorException ("Can only handle one mutator at a time"); + first_emit = element; + element = first_emit_temp = new LocalTemporary (element.Type); + } + + return Convert.ImplicitConversionRequired ( + ec, element, array_element_type, loc); + } + + protected bool ResolveInitializers (ResolveContext ec) + { +#if STATIC + only_constant_initializers = true; +#endif + + if (arguments != null) { + bool res = true; + for (int i = 0; i < arguments.Count; ++i) { + res &= CheckIndices (ec, initializers, i, true, dimensions); + if (initializers != null) + break; + } + + return res; + } + + arguments = new List (); + + if (!CheckIndices (ec, initializers, 0, false, dimensions)) + return false; + + UpdateIndices (ec); + + return true; + } + + // + // Resolved the type of the array + // + bool ResolveArrayType (ResolveContext ec) + { + // + // Lookup the type + // + FullNamedExpression array_type_expr; + if (num_arguments > 0) { + array_type_expr = new ComposedCast (requested_base_type, rank); + } else { + array_type_expr = requested_base_type; + } + + type = array_type_expr.ResolveAsType (ec); + if (array_type_expr == null) + return false; + + var ac = type as ArrayContainer; + if (ac == null) { + ec.Report.Error (622, loc, "Can only use array initializer expressions to assign to array types. Try using a new expression instead"); + return false; + } + + array_element_type = ac.Element; + dimensions = ac.Rank; + + return true; + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (type != null) + return this; + + if (!ResolveArrayType (ec)) + return null; + + // + // validate the initializers and fill in any missing bits + // + if (!ResolveInitializers (ec)) + return null; + + eclass = ExprClass.Value; + return this; + } + + byte [] MakeByteBlob () + { + int factor; + byte [] data; + byte [] element; + int count = array_data.Count; + + TypeSpec element_type = array_element_type; + if (element_type.IsEnum) + element_type = EnumSpec.GetUnderlyingType (element_type); + + factor = BuiltinTypeSpec.GetSize (element_type); + if (factor == 0) + throw new Exception ("unrecognized type in MakeByteBlob: " + element_type); + + data = new byte [(count * factor + 3) & ~3]; + int idx = 0; + + for (int i = 0; i < count; ++i) { + var c = array_data[i] as Constant; + if (c == null) { + idx += factor; + continue; + } + + object v = c.GetValue (); + + switch (element_type.BuiltinType) { + case BuiltinTypeSpec.Type.Long: + long lval = (long) v; + + for (int j = 0; j < factor; ++j) { + data[idx + j] = (byte) (lval & 0xFF); + lval = (lval >> 8); + } + break; + case BuiltinTypeSpec.Type.ULong: + ulong ulval = (ulong) v; + + for (int j = 0; j < factor; ++j) { + data[idx + j] = (byte) (ulval & 0xFF); + ulval = (ulval >> 8); + } + break; + case BuiltinTypeSpec.Type.Float: + var fval = SingleConverter.SingleToInt32Bits((float) v); + + data[idx] = (byte) (fval & 0xff); + data[idx + 1] = (byte) ((fval >> 8) & 0xff); + data[idx + 2] = (byte) ((fval >> 16) & 0xff); + data[idx + 3] = (byte) (fval >> 24); + break; + case BuiltinTypeSpec.Type.Double: + element = BitConverter.GetBytes ((double) v); + + for (int j = 0; j < factor; ++j) + data[idx + j] = element[j]; + + // FIXME: Handle the ARM float format. + if (!BitConverter.IsLittleEndian) + System.Array.Reverse (data, idx, 8); + break; + case BuiltinTypeSpec.Type.Char: + int chval = (int) ((char) v); + + data[idx] = (byte) (chval & 0xff); + data[idx + 1] = (byte) (chval >> 8); + break; + case BuiltinTypeSpec.Type.Short: + int sval = (int) ((short) v); + + data[idx] = (byte) (sval & 0xff); + data[idx + 1] = (byte) (sval >> 8); + break; + case BuiltinTypeSpec.Type.UShort: + int usval = (int) ((ushort) v); + + data[idx] = (byte) (usval & 0xff); + data[idx + 1] = (byte) (usval >> 8); + break; + case BuiltinTypeSpec.Type.Int: + int val = (int) v; + + data[idx] = (byte) (val & 0xff); + data[idx + 1] = (byte) ((val >> 8) & 0xff); + data[idx + 2] = (byte) ((val >> 16) & 0xff); + data[idx + 3] = (byte) (val >> 24); + break; + case BuiltinTypeSpec.Type.UInt: + uint uval = (uint) v; + + data[idx] = (byte) (uval & 0xff); + data[idx + 1] = (byte) ((uval >> 8) & 0xff); + data[idx + 2] = (byte) ((uval >> 16) & 0xff); + data[idx + 3] = (byte) (uval >> 24); + break; + case BuiltinTypeSpec.Type.SByte: + data[idx] = (byte) (sbyte) v; + break; + case BuiltinTypeSpec.Type.Byte: + data[idx] = (byte) v; + break; + case BuiltinTypeSpec.Type.Bool: + data[idx] = (byte) ((bool) v ? 1 : 0); + break; + case BuiltinTypeSpec.Type.Decimal: + int[] bits = Decimal.GetBits ((decimal) v); + int p = idx; + + // FIXME: For some reason, this doesn't work on the MS runtime. + int[] nbits = new int[4]; + nbits[0] = bits[3]; + nbits[1] = bits[2]; + nbits[2] = bits[0]; + nbits[3] = bits[1]; + + for (int j = 0; j < 4; j++) { + data[p++] = (byte) (nbits[j] & 0xff); + data[p++] = (byte) ((nbits[j] >> 8) & 0xff); + data[p++] = (byte) ((nbits[j] >> 16) & 0xff); + data[p++] = (byte) (nbits[j] >> 24); + } + break; + default: + throw new Exception ("Unrecognized type in MakeByteBlob: " + element_type); + } + + idx += factor; + } + + return data; + } + +#if NET_4_0 || MOBILE_DYNAMIC + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + var initializers = new SLE.Expression [array_data.Count]; + for (var i = 0; i < initializers.Length; i++) { + if (array_data [i] == null) + initializers [i] = SLE.Expression.Default (array_element_type.GetMetaInfo ()); + else + initializers [i] = array_data [i].MakeExpression (ctx); + } + + return SLE.Expression.NewArrayInit (array_element_type.GetMetaInfo (), initializers); +#endif + } +#endif +#if STATIC + // + // Emits the initializers for the array + // + void EmitStaticInitializers (EmitContext ec, FieldExpr stackArray) + { + var m = ec.Module.PredefinedMembers.RuntimeHelpersInitializeArray.Resolve (loc); + if (m == null) + return; + + // + // First, the static data + // + byte [] data = MakeByteBlob (); + var fb = ec.CurrentTypeDefinition.Module.MakeStaticData (data, loc); + + if (stackArray == null) { + ec.Emit (OpCodes.Dup); + } else { + stackArray.Emit (ec); + } + + ec.Emit (OpCodes.Ldtoken, fb); + ec.Emit (OpCodes.Call, m); + } +#endif + + // + // Emits pieces of the array that can not be computed at compile + // time (variables and string locations). + // + // This always expect the top value on the stack to be the array + // + void EmitDynamicInitializers (EmitContext ec, bool emitConstants, StackFieldExpr stackArray) + { + int dims = bounds.Count; + var current_pos = new int [dims]; + + for (int i = 0; i < array_data.Count; i++){ + + Expression e = array_data [i]; + var c = e as Constant; + + // Constant can be initialized via StaticInitializer + if (c == null || (c != null && emitConstants && !c.IsDefaultInitializer (array_element_type))) { + + var etype = e.Type; + + if (stackArray != null) { + if (e.ContainsEmitWithAwait ()) { + e = e.EmitToField (ec); + } + + stackArray.EmitLoad (ec); + } else { + ec.Emit (OpCodes.Dup); + } + + for (int idx = 0; idx < dims; idx++) + ec.EmitInt (current_pos [idx]); + + // + // If we are dealing with a struct, get the + // address of it, so we can store it. + // + if (dims == 1 && etype.IsStruct && !BuiltinTypeSpec.IsPrimitiveType (etype)) + ec.Emit (OpCodes.Ldelema, etype); + + e.Emit (ec); + + ec.EmitArrayStore ((ArrayContainer) type); + } + + // + // Advance counter + // + for (int j = dims - 1; j >= 0; j--){ + current_pos [j]++; + if (current_pos [j] < bounds [j]) + break; + current_pos [j] = 0; + } + } + + if (stackArray != null) + stackArray.PrepareCleanup (ec); + } + + public override void Emit (EmitContext ec) + { + var await_field = EmitToFieldSource (ec); + if (await_field != null) + await_field.Emit (ec); + } + + protected sealed override FieldExpr EmitToFieldSource (EmitContext ec) + { + if (first_emit != null) { + first_emit.Emit (ec); + first_emit_temp.Store (ec); + } + + StackFieldExpr await_stack_field; + if (ec.HasSet (BuilderContext.Options.AsyncBody) && InitializersContainAwait ()) { + await_stack_field = ec.GetTemporaryField (type); + ec.EmitThis (); + } else { + await_stack_field = null; + } + + EmitExpressionsList (ec, arguments); + + ec.EmitArrayNew ((ArrayContainer) type); + + if (initializers == null) + return await_stack_field; + + if (await_stack_field != null) + await_stack_field.EmitAssignFromStack (ec); + +#if STATIC + // + // Emit static initializer for arrays which contain more than 2 items and + // the static initializer will initialize at least 25% of array values or there + // is more than 10 items to be initialized + // + // NOTE: const_initializers_count does not contain default constant values. + // + if (const_initializers_count > 2 && (array_data.Count > 10 || const_initializers_count * 4 > (array_data.Count)) && + (BuiltinTypeSpec.IsPrimitiveType (array_element_type) || array_element_type.IsEnum)) { + EmitStaticInitializers (ec, await_stack_field); + + if (!only_constant_initializers) + EmitDynamicInitializers (ec, false, await_stack_field); + } else +#endif + { + EmitDynamicInitializers (ec, true, await_stack_field); + } + + if (first_emit_temp != null) + first_emit_temp.Release (ec); + + return await_stack_field; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // no multi dimensional or jagged arrays + if (arguments.Count != 1 || array_element_type.IsArray) { + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + return; + } + + // No array covariance, except for array -> object + if (type != targetType) { + if (targetType.BuiltinType != BuiltinTypeSpec.Type.Object) { + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + return; + } + + if (enc.Encode (type) == AttributeEncoder.EncodedTypeProperties.DynamicType) { + Attribute.Error_AttributeArgumentIsDynamic (rc, loc); + return; + } + } + + // Single dimensional array of 0 size + if (array_data == null) { + IntConstant ic = arguments[0] as IntConstant; + if (ic == null || !ic.IsDefaultValue) { + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + } else { + enc.Encode (0); + } + + return; + } + + enc.Encode (array_data.Count); + foreach (var element in array_data) { + element.EncodeAttributeValue (rc, enc, array_element_type, parameterType); + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + ArrayCreation target = (ArrayCreation) t; + + if (requested_base_type != null) + target.requested_base_type = (FullNamedExpression)requested_base_type.Clone (clonectx); + + if (arguments != null){ + target.arguments = new List (arguments.Count); + foreach (Expression e in arguments) + target.arguments.Add (e.Clone (clonectx)); + } + + if (initializers != null) + target.initializers = (ArrayInitializer) initializers.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Represents an implicitly typed array epxression + // + class ImplicitlyTypedArrayCreation : ArrayCreation + { + TypeInferenceContext best_type_inference; + + public ImplicitlyTypedArrayCreation (ComposedTypeSpecifier rank, ArrayInitializer initializers, Location loc) + : base (null, rank, initializers, loc) + { + } + + public ImplicitlyTypedArrayCreation (ArrayInitializer initializers, Location loc) + : base (null, initializers, loc) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (type != null) + return this; + + dimensions = rank.Dimension; + + best_type_inference = new TypeInferenceContext (); + + if (!ResolveInitializers (ec)) + return null; + + best_type_inference.FixAllTypes (ec); + array_element_type = best_type_inference.InferredTypeArguments[0]; + best_type_inference = null; + + if (array_element_type == null || + array_element_type == InternalType.NullLiteral || array_element_type == InternalType.MethodGroup || array_element_type == InternalType.AnonymousMethod || + arguments.Count != rank.Dimension) { + ec.Report.Error (826, loc, + "The type of an implicitly typed array cannot be inferred from the initializer. Try specifying array type explicitly"); + return null; + } + + // + // At this point we found common base type for all initializer elements + // but we have to be sure that all static initializer elements are of + // same type + // + UnifyInitializerElement (ec); + + type = ArrayContainer.MakeType (ec.Module, array_element_type, dimensions); + eclass = ExprClass.Value; + return this; + } + + // + // Converts static initializer only + // + void UnifyInitializerElement (ResolveContext ec) + { + for (int i = 0; i < array_data.Count; ++i) { + Expression e = array_data[i]; + if (e != null) + array_data [i] = Convert.ImplicitConversion (ec, e, array_element_type, Location.Null); + } + } + + protected override Expression ResolveArrayElement (ResolveContext ec, Expression element) + { + element = element.Resolve (ec); + if (element != null) + best_type_inference.AddCommonTypeBound (element.Type); + + return element; + } + } + + sealed class CompilerGeneratedThis : This + { + public CompilerGeneratedThis (TypeSpec type, Location loc) + : base (loc) + { + this.type = type; + } + + protected override Expression DoResolve (ResolveContext rc) + { + eclass = ExprClass.Variable; + + var block = rc.CurrentBlock; + if (block != null) { + var top = block.ParametersBlock.TopBlock; + if (top.ThisVariable != null) + variable_info = top.ThisVariable.VariableInfo; + + } + + return this; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return DoResolve (rc); + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + return null; + } + } + + /// + /// Represents the `this' construct + /// + + public class This : VariableReference + { + sealed class ThisVariable : ILocalVariable + { + public static readonly ILocalVariable Instance = new ThisVariable (); + + public void Emit (EmitContext ec) + { + ec.EmitThis (); + } + + public void EmitAssign (EmitContext ec) + { + throw new InvalidOperationException (); + } + + public void EmitAddressOf (EmitContext ec) + { + ec.EmitThis (); + } + } + + protected VariableInfo variable_info; + + public This (Location loc) + { + this.loc = loc; + } + + #region Properties + + public override string Name { + get { return "this"; } + } + + public override bool IsLockedByStatement { + get { + return false; + } + set { + } + } + + public override bool IsRef { + get { return type.IsStruct; } + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + protected override ILocalVariable Variable { + get { return ThisVariable.Instance; } + } + + public override VariableInfo VariableInfo { + get { return variable_info; } + } + + public override bool IsFixed { + get { return false; } + } + + #endregion + + void CheckStructThisDefiniteAssignment (FlowAnalysisContext fc) + { + // + // It's null for all cases when we don't need to check `this' + // definitive assignment + // + if (variable_info == null) + return; + + if (fc.IsDefinitelyAssigned (variable_info)) + return; + + fc.Report.Error (188, loc, "The `this' object cannot be used before all of its fields are assigned to"); + } + + protected virtual void Error_ThisNotAvailable (ResolveContext ec) + { + if (ec.IsStatic && !ec.HasSet (ResolveContext.Options.ConstantScope)) { + ec.Report.Error (26, loc, "Keyword `this' is not valid in a static property, static method, or static field initializer"); + } else if (ec.CurrentAnonymousMethod != null) { + ec.Report.Error (1673, loc, + "Anonymous methods inside structs cannot access instance members of `this'. " + + "Consider copying `this' to a local variable outside the anonymous method and using the local instead"); + } else { + ec.Report.Error (27, loc, "Keyword `this' is not available in the current context"); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + CheckStructThisDefiniteAssignment (fc); + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + if (ae == null) + return null; + + AnonymousMethodStorey storey = ae.Storey; + return storey != null ? storey.HoistedThis : null; + } + + public static bool IsThisAvailable (ResolveContext ec, bool ignoreAnonymous) + { + if (ec.IsStatic || ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.BaseInitializer | ResolveContext.Options.ConstantScope)) + return false; + + if (ignoreAnonymous || ec.CurrentAnonymousMethod == null) + return true; + + if (ec.CurrentType.IsStruct && !(ec.CurrentAnonymousMethod is StateMachineInitializer)) + return false; + + return true; + } + + public virtual void ResolveBase (ResolveContext ec) + { + eclass = ExprClass.Variable; + type = ec.CurrentType; + + if (!IsThisAvailable (ec, false)) { + Error_ThisNotAvailable (ec); + return; + } + + var block = ec.CurrentBlock; + if (block != null) { + var top = block.ParametersBlock.TopBlock; + if (top.ThisVariable != null) + variable_info = top.ThisVariable.VariableInfo; + + AnonymousExpression am = ec.CurrentAnonymousMethod; + if (am != null && ec.IsVariableCapturingRequired && !block.Explicit.HasCapturedThis) { + // + // Hoisted this is almost like hoisted variable but not exactly. When + // there is no variable hoisted we can simply emit an instance method + // without lifting this into a storey. Unfotunatelly this complicates + // things in other cases because we don't know where this will be hoisted + // until top-level block is fully resolved + // + top.AddThisReferenceFromChildrenBlock (block.Explicit); + am.SetHasThisAccess (); + } + } + } + + protected override Expression DoResolve (ResolveContext ec) + { + ResolveBase (ec); + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (eclass == ExprClass.Unresolved) + ResolveBase (ec); + + if (type.IsClass){ + if (right_side == EmptyExpression.UnaryAddress) + ec.Report.Error (459, loc, "Cannot take the address of `this' because it is read-only"); + else if (right_side == EmptyExpression.OutAccess) + ec.Report.Error (1605, loc, "Cannot pass `this' as a ref or out argument because it is read-only"); + else + ec.Report.Error (1604, loc, "Cannot assign to `this' because it is read-only"); + } + + return this; + } + + public override int GetHashCode() + { + throw new NotImplementedException (); + } + + public override bool Equals (object obj) + { + This t = obj as This; + if (t == null) + return false; + + return true; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + + public override void SetHasAddressTaken () + { + // Nothing + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Represents the `__arglist' construct + /// + public class ArglistAccess : Expression + { + public ArglistAccess (Location loc) + { + this.loc = loc; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + // nothing. + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Variable; + type = ec.Module.PredefinedTypes.RuntimeArgumentHandle.Resolve (); + + if (ec.HasSet (ResolveContext.Options.FieldInitializerScope) || !ec.CurrentBlock.ParametersBlock.Parameters.HasArglist) { + ec.Report.Error (190, loc, + "The __arglist construct is valid only within a variable argument method"); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Arglist); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Represents the `__arglist (....)' construct + /// + public class Arglist : Expression + { + Arguments arguments; + + public Arglist (Location loc) + : this (null, loc) + { + } + + public Arglist (Arguments args, Location l) + { + arguments = args; + loc = l; + } + + public Arguments Arguments { + get { + return arguments; + } + } + + public MetaType[] ArgumentTypes { + get { + if (arguments == null) + return MetaType.EmptyTypes; + + var retval = new MetaType[arguments.Count]; + for (int i = 0; i < retval.Length; i++) + retval[i] = arguments[i].Expr.Type.GetMetaInfo (); + + return retval; + } + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (1952, loc, "An expression tree cannot contain a method with variable arguments"); + return null; + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Variable; + type = InternalType.Arglist; + if (arguments != null) { + bool dynamic; // Can be ignored as there is always only 1 overload + arguments.Resolve (ec, out dynamic); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + if (arguments != null) + arguments.Emit (ec); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Arglist target = (Arglist) t; + + if (arguments != null) + target.arguments = arguments.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class RefValueExpr : ShimExpression, IAssignMethod + { + FullNamedExpression texpr; + + public FullNamedExpression FullNamedExpression { + get { return texpr;} + } + + public RefValueExpr (Expression expr, FullNamedExpression texpr, Location loc) + : base (expr) + { + this.texpr = texpr; + this.loc = loc; + } + + public FullNamedExpression TypeExpression { + get { + return texpr; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.Resolve (rc); + type = texpr.ResolveAsType (rc); + if (expr == null || type == null) + return null; + + expr = Convert.ImplicitConversionRequired (rc, expr, rc.Module.PredefinedTypes.TypedReference.Resolve (), loc); + eclass = ExprClass.Value; + return this; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return DoResolve (rc); + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + ec.Emit (OpCodes.Refanyval, type); + ec.EmitLoadFromPtr (type); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + expr.Emit (ec); + ec.Emit (OpCodes.Refanyval, type); + source.Emit (ec); + + LocalTemporary temporary = null; + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temporary = new LocalTemporary (source.Type); + temporary.Store (ec); + } + + ec.EmitStoreFromPtr (type); + + if (temporary != null) { + temporary.Emit (ec); + temporary.Release (ec); + } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class RefTypeExpr : ShimExpression + { + public RefTypeExpr (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.Resolve (rc); + if (expr == null) + return null; + + expr = Convert.ImplicitConversionRequired (rc, expr, rc.Module.PredefinedTypes.TypedReference.Resolve (), loc); + if (expr == null) + return null; + + type = rc.BuiltinTypes.Type; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + ec.Emit (OpCodes.Refanytype); + var m = ec.Module.PredefinedMembers.TypeGetTypeFromHandle.Resolve (loc); + if (m != null) + ec.Emit (OpCodes.Call, m); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class MakeRefExpr : ShimExpression + { + public MakeRefExpr (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.ResolveLValue (rc, EmptyExpression.LValueMemberAccess); + type = rc.Module.PredefinedTypes.TypedReference.Resolve (); + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + ((IMemoryLocation) expr).AddressOf (ec, AddressOp.Load); + ec.Emit (OpCodes.Mkrefany, expr.Type); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements the typeof operator + /// + public class TypeOf : Expression { + FullNamedExpression QueriedType; + TypeSpec typearg; + + public TypeOf (FullNamedExpression queried_type, Location l) + { + QueriedType = queried_type; + loc = l; + } + + // + // Use this constructor for any compiler generated typeof expression + // + public TypeOf (TypeSpec type, Location loc) + { + this.typearg = type; + this.loc = loc; + } + + #region Properties + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public TypeSpec TypeArgument { + get { + return typearg; + } + } + + public FullNamedExpression TypeExpression { + get { + return QueriedType; + } + } + + #endregion + + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + TypeOf target = (TypeOf) t; + if (QueriedType != null) + target.QueriedType = (FullNamedExpression) QueriedType.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc))); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (eclass != ExprClass.Unresolved) + return this; + + if (typearg == null) { + // + // Pointer types are allowed without explicit unsafe, they are just tokens + // + using (ec.Set (ResolveContext.Options.UnsafeScope)) { + typearg = QueriedType.ResolveAsType (ec, true); + } + + if (typearg == null) + return null; + + if (typearg.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + ec.Report.Error (1962, QueriedType.Location, + "The typeof operator cannot be used on the dynamic type"); + } + } + + type = ec.BuiltinTypes.Type; + + // Even though what is returned is a type object, it's treated as a value by the compiler. + // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'. + eclass = ExprClass.Value; + return this; + } + + static bool ContainsDynamicType (TypeSpec type) + { + if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + + var element_container = type as ElementTypeSpec; + if (element_container != null) + return ContainsDynamicType (element_container.Element); + + foreach (var t in type.TypeArguments) { + if (ContainsDynamicType (t)) { + return true; + } + } + + return false; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // Target type is not System.Type therefore must be object + // and we need to use different encoding sequence + if (targetType != type) + enc.Encode (type); + + if (typearg is InflatedTypeSpec) { + var gt = typearg; + do { + if (InflatedTypeSpec.ContainsTypeParameter (gt)) { + rc.Module.Compiler.Report.Error (416, loc, "`{0}': an attribute argument cannot use type parameters", + typearg.GetSignatureForError ()); + return; + } + + gt = gt.DeclaringType; + } while (gt != null); + } + + if (ContainsDynamicType (typearg)) { + Attribute.Error_AttributeArgumentIsDynamic (rc, loc); + return; + } + + enc.EncodeTypeName (typearg); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldtoken, typearg); + var m = ec.Module.PredefinedMembers.TypeGetTypeFromHandle.Resolve (loc); + if (m != null) + ec.Emit (OpCodes.Call, m); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + sealed class TypeOfMethod : TypeOfMember + { + public TypeOfMethod (MethodSpec method, Location loc) + : base (method, loc) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (member.IsConstructor) { + type = ec.Module.PredefinedTypes.ConstructorInfo.Resolve (); + } else { + type = ec.Module.PredefinedTypes.MethodInfo.Resolve (); + } + + if (type == null) + return null; + + return base.DoResolve (ec); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldtoken, member); + + base.Emit (ec); + ec.Emit (OpCodes.Castclass, type); + } + + protected override PredefinedMember GetTypeFromHandle (EmitContext ec) + { + return ec.Module.PredefinedMembers.MethodInfoGetMethodFromHandle; + } + + protected override PredefinedMember GetTypeFromHandleGeneric (EmitContext ec) + { + return ec.Module.PredefinedMembers.MethodInfoGetMethodFromHandle2; + } + } + + abstract class TypeOfMember : Expression where T : MemberSpec + { + protected readonly T member; + + protected TypeOfMember (T member, Location loc) + { + this.member = member; + this.loc = loc; + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (type, loc))); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + bool is_generic = member.DeclaringType.IsGenericOrParentIsGeneric; + PredefinedMember p; + if (is_generic) { + p = GetTypeFromHandleGeneric (ec); + ec.Emit (OpCodes.Ldtoken, member.DeclaringType); + } else { + p = GetTypeFromHandle (ec); + } + + var mi = p.Resolve (loc); + if (mi != null) + ec.Emit (OpCodes.Call, mi); + } + + protected abstract PredefinedMember GetTypeFromHandle (EmitContext ec); + protected abstract PredefinedMember GetTypeFromHandleGeneric (EmitContext ec); + } + + sealed class TypeOfField : TypeOfMember + { + public TypeOfField (FieldSpec field, Location loc) + : base (field, loc) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = ec.Module.PredefinedTypes.FieldInfo.Resolve (); + if (type == null) + return null; + + return base.DoResolve (ec); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldtoken, member); + base.Emit (ec); + } + + protected override PredefinedMember GetTypeFromHandle (EmitContext ec) + { + return ec.Module.PredefinedMembers.FieldInfoGetFieldFromHandle; + } + + protected override PredefinedMember GetTypeFromHandleGeneric (EmitContext ec) + { + return ec.Module.PredefinedMembers.FieldInfoGetFieldFromHandle2; + } + } + + /// + /// Implements the sizeof expression + /// + public class SizeOf : Expression { + readonly Expression texpr; + TypeSpec type_queried; + + public SizeOf (Expression queried_type, Location l) + { + this.texpr = queried_type; + loc = l; + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public Expression TypeExpression { + get { + return texpr; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return null; + } + + protected override Expression DoResolve (ResolveContext ec) + { + type_queried = texpr.ResolveAsType (ec); + if (type_queried == null) + return null; + + if (type_queried.IsEnum) + type_queried = EnumSpec.GetUnderlyingType (type_queried); + + int size_of = BuiltinTypeSpec.GetSize (type_queried); + if (size_of > 0) { + return new IntConstant (ec.BuiltinTypes, size_of, loc); + } + + if (!TypeManager.VerifyUnmanaged (ec.Module, type_queried, loc)){ + return null; + } + + if (!ec.IsUnsafe) { + ec.Report.Error (233, loc, + "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)", + type_queried.GetSignatureForError ()); + } + + type = ec.BuiltinTypes.Int; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Sizeof, type_queried); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements the qualified-alias-member (::) expression. + /// + public class QualifiedAliasMember : MemberAccess + { + public readonly string alias; + public static readonly string GlobalAlias = "global"; + + public QualifiedAliasMember (string alias, string identifier, Location l) + : base (null, identifier, l) + { + this.alias = alias; + } + + public QualifiedAliasMember (string alias, string identifier, TypeArguments targs, Location l) + : base (null, identifier, targs, l) + { + this.alias = alias; + } + + public QualifiedAliasMember (string alias, string identifier, int arity, Location l) + : base (null, identifier, arity, l) + { + this.alias = alias; + } + + public string Alias { + get { + return alias; + } + } + + public FullNamedExpression CreateExpressionFromAlias (IMemberContext mc) + { + if (alias == GlobalAlias) + return new NamespaceExpression (mc.Module.GlobalRootNamespace, loc); + + int errors = mc.Module.Compiler.Report.Errors; + var expr = mc.LookupNamespaceAlias (alias); + if (expr == null) { + if (errors == mc.Module.Compiler.Report.Errors) + mc.Module.Compiler.Report.Error (432, loc, "Alias `{0}' not found", alias); + + return null; + } + + return expr; + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + expr = CreateExpressionFromAlias (mc); + if (expr == null) + return null; + + return base.ResolveAsTypeOrNamespace (mc, allowUnboundTypeArguments); + } + + protected override Expression DoResolve (ResolveContext rc) + { + return ResolveAsTypeOrNamespace (rc, false); + } + + public override string GetSignatureForError () + { + string name = Name; + if (targs != null) { + name = Name + "<" + targs.GetSignatureForError () + ">"; + } + + return alias + "::" + name; + } + + public override bool HasConditionalAccess () + { + return false; + } + + public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions) + { + if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) { + rc.Module.Compiler.Report.Error (687, loc, + "The namespace alias qualifier `::' cannot be used to invoke a method. Consider using `.' instead", + GetSignatureForError ()); + + return null; + } + + return DoResolve (rc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements the member access expression + /// + public class MemberAccess : ATypeNameExpression + { + protected Expression expr; + + public MemberAccess (Expression expr, string id) + : base (id, expr.Location) + { + this.expr = expr; + } + + public MemberAccess (Expression expr, string identifier, Location loc) + : base (identifier, loc) + { + this.expr = expr; + } + + public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc) + : base (identifier, args, loc) + { + this.expr = expr; + } + + public MemberAccess (Expression expr, string identifier, int arity, Location loc) + : base (identifier, arity, loc) + { + this.expr = expr; + } + + public Expression LeftExpression { + get { + return expr; + } + } + + public override Location StartLocation { + get { + return expr == null ? loc : expr.StartLocation; + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + var e = LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess); + if (e != null) + e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type | ResolveFlags.MethodGroup); + + return e; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression rhs) + { + var e = LookupNameExpression (rc, MemberLookupRestrictions.None); + + if (e is TypeExpr) { + e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc); + return null; + } + + if (e != null) + e = e.ResolveLValue (rc, rhs); + + return e; + } + + protected virtual void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type) + { + if (type == InternalType.NullLiteral && rc.IsRuntimeBinder) + rc.Report.Error (Report.RuntimeErrorId, loc, "Cannot perform member binding on `null' value"); + else + expr.Error_OperatorCannotBeApplied (rc, loc, ".", type); + } + + public override bool HasConditionalAccess () + { + return LeftExpression.HasConditionalAccess (); + } + + public static bool IsValidDotExpression (TypeSpec type) + { + const MemberKind dot_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.Delegate | MemberKind.Enum | + MemberKind.Interface | MemberKind.TypeParameter | MemberKind.ArrayType; + + return (type.Kind & dot_kinds) != 0 || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + } + + public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions) + { + var sn = expr as SimpleName; + const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type; + + if (sn != null) { + expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity); + + // + // Resolve expression which does have type set as we need expression type + // with disable flow analysis as we don't know whether left side expression + // is used as variable or type + // + if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess || expr is EventExpr) { + expr = expr.Resolve (rc); + } else if (expr is TypeParameterExpr) { + expr.Error_UnexpectedKind (rc, flags, sn.Location); + expr = null; + } + } else { + using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) { + expr = expr.Resolve (rc, flags); + } + } + + if (expr == null) + return null; + + var ns = expr as NamespaceExpression; + if (ns != null) { + var retval = ns.LookupTypeOrNamespace (rc, Name, Arity, LookupMode.Normal, loc); + + if (retval == null) { + ns.Error_NamespaceDoesNotExist (rc, Name, Arity); + return null; + } + + if (HasTypeArguments) + return new GenericTypeExpr (retval.Type, targs, loc); + + return retval; + } + + MemberExpr me; + TypeSpec expr_type = expr.Type; + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + me = expr as MemberExpr; + if (me != null) + me.ResolveInstanceExpression (rc, null); + + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + return new DynamicMemberBinder (Name, args, loc); + } + + var cma = this as ConditionalMemberAccess; + if (cma != null) { + if (!IsNullPropagatingValid (expr.Type)) { + expr.Error_OperatorCannotBeApplied (rc, loc, "?", expr.Type); + return null; + } + + if (expr_type.IsNullableType) { + expr = Nullable.Unwrap.Create (expr, true).Resolve (rc); + expr_type = expr.Type; + } + } + + if (!IsValidDotExpression (expr_type)) { + Error_OperatorCannotBeApplied (rc, expr_type); + return null; + } + + var lookup_arity = Arity; + bool errorMode = false; + Expression member_lookup; + while (true) { + member_lookup = MemberLookup (rc, errorMode, expr_type, Name, lookup_arity, restrictions, loc); + if (member_lookup == null) { + // + // Try to look for extension method when member lookup failed + // + if (MethodGroupExpr.IsExtensionMethodArgument (expr)) { + var methods = rc.LookupExtensionMethod (expr_type, Name, lookup_arity); + if (methods != null) { + var emg = new ExtensionMethodGroupExpr (methods, expr, loc); + if (HasTypeArguments) { + if (!targs.Resolve (rc)) + return null; + + emg.SetTypeArguments (rc, targs); + } + + if (cma != null) + emg.ConditionalAccess = true; + + // TODO: it should really skip the checks bellow + return emg.Resolve (rc); + } + } + } + + if (errorMode) { + if (member_lookup == null) { + var dep = expr_type.GetMissingDependencies (); + if (dep != null) { + ImportedTypeDefinition.Error_MissingDependency (rc, dep, loc); + } else if (expr is TypeExpr) { + base.Error_TypeDoesNotContainDefinition (rc, expr_type, Name); + } else { + Error_TypeDoesNotContainDefinition (rc, expr_type, Name); + } + + return null; + } + + if (member_lookup is MethodGroupExpr || member_lookup is PropertyExpr) { + // Leave it to overload resolution to report correct error + } else if (!(member_lookup is TypeExpr)) { + // TODO: rc.SymbolRelatedToPreviousError + ErrorIsInaccesible (rc, member_lookup.GetSignatureForError (), loc); + } + break; + } + + if (member_lookup != null) + break; + + lookup_arity = 0; + restrictions &= ~MemberLookupRestrictions.InvocableOnly; + errorMode = true; + } + + TypeExpr texpr = member_lookup as TypeExpr; + if (texpr != null) { + if (!(expr is TypeExpr) && (sn == null || expr.ProbeIdenticalTypeName (rc, expr, sn) == expr)) { + rc.Report.Error (572, loc, "`{0}': cannot reference a type through an expression. Consider using `{1}' instead", + Name, texpr.GetSignatureForError ()); + } + + if (!texpr.Type.IsAccessible (rc)) { + rc.Report.SymbolRelatedToPreviousError (member_lookup.Type); + ErrorIsInaccesible (rc, member_lookup.Type.GetSignatureForError (), loc); + return null; + } + + if (HasTypeArguments) { + return new GenericTypeExpr (member_lookup.Type, targs, loc); + } + + return member_lookup; + } + + me = member_lookup as MemberExpr; + + if (sn != null && me.IsStatic && (expr = me.ProbeIdenticalTypeName (rc, expr, sn)) != expr) { + sn = null; + } + + if (cma != null) { + me.ConditionalAccess = true; + } + + me = me.ResolveMemberAccess (rc, expr, sn); + + if (Arity > 0) { + if (!targs.Resolve (rc)) + return null; + + me.SetTypeArguments (rc, targs); + } + + return me; + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext rc, bool allowUnboundTypeArguments) + { + FullNamedExpression fexpr = expr as FullNamedExpression; + if (fexpr == null) { + expr.ResolveAsType (rc); + return null; + } + + FullNamedExpression expr_resolved = fexpr.ResolveAsTypeOrNamespace (rc, allowUnboundTypeArguments); + + if (expr_resolved == null) + return null; + + var ns = expr_resolved as NamespaceExpression; + if (ns != null) { + FullNamedExpression retval = ns.LookupTypeOrNamespace (rc, Name, Arity, LookupMode.Normal, loc); + + if (retval == null) { + ns.Error_NamespaceDoesNotExist (rc, Name, Arity); + } else if (Arity > 0) { + if (HasTypeArguments) { + retval = new GenericTypeExpr (retval.Type, targs, loc); + if (retval.ResolveAsType (rc) == null) + return null; + } else { + if (!allowUnboundTypeArguments) + Error_OpenGenericTypeIsNotAllowed (rc); + + retval = new GenericOpenTypeExpr (retval.Type, loc); + } + } + + return retval; + } + + var tnew_expr = expr_resolved.ResolveAsType (rc); + if (tnew_expr == null) + return null; + + TypeSpec expr_type = tnew_expr; + if (TypeManager.IsGenericParameter (expr_type)) { + rc.Module.Compiler.Report.Error (704, loc, "A nested type cannot be specified through a type parameter `{0}'", + tnew_expr.GetSignatureForError ()); + return null; + } + + var qam = this as QualifiedAliasMember; + if (qam != null) { + rc.Module.Compiler.Report.Error (431, loc, + "Alias `{0}' cannot be used with `::' since it denotes a type. Consider replacing `::' with `.'", + qam.Alias); + + } + + TypeSpec nested = null; + while (expr_type != null) { + nested = MemberCache.FindNestedType (expr_type, Name, Arity); + if (nested == null) { + if (expr_type == tnew_expr) { + Error_IdentifierNotFound (rc, expr_type); + return null; + } + + expr_type = tnew_expr; + nested = MemberCache.FindNestedType (expr_type, Name, Arity); + ErrorIsInaccesible (rc, nested.GetSignatureForError (), loc); + break; + } + + if (nested.IsAccessible (rc)) + break; + + // + // Keep looking after inaccessible candidate but only if + // we are not in same context as the definition itself + // + if (expr_type.MemberDefinition == rc.CurrentMemberDefinition) + break; + + expr_type = expr_type.BaseType; + } + + TypeExpr texpr; + if (Arity > 0) { + if (HasTypeArguments) { + texpr = new GenericTypeExpr (nested, targs, loc); + } else { + if (!allowUnboundTypeArguments || expr_resolved is GenericTypeExpr) // && HasTypeArguments + Error_OpenGenericTypeIsNotAllowed (rc); + + texpr = new GenericOpenTypeExpr (nested, loc); + } + } else if (expr_resolved is GenericOpenTypeExpr) { + texpr = new GenericOpenTypeExpr (nested, loc); + } else { + texpr = new TypeExpression (nested, loc); + } + + if (texpr.ResolveAsType (rc) == null) + return null; + + return texpr; + } + + public void Error_IdentifierNotFound (IMemberContext rc, TypeSpec expr_type) + { + var nested = MemberCache.FindNestedType (expr_type, Name, -System.Math.Max (1, Arity)); + + if (nested != null) { + Error_TypeArgumentsCannotBeUsed (rc, nested, expr.Location); + return; + } + + var any_other_member = MemberLookup (rc, false, expr_type, Name, 0, MemberLookupRestrictions.None, loc); + if (any_other_member != null) { + Error_UnexpectedKind (rc, any_other_member, "type", any_other_member.ExprClassName, loc); + return; + } + + rc.Module.Compiler.Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'", + Name, expr_type.GetSignatureForError ()); + } + + protected override void Error_InvalidExpressionStatement (Report report, Location loc) + { + base.Error_InvalidExpressionStatement (report, LeftExpression.Location); + } + + protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name) + { + if (ec.Module.Compiler.Settings.Version > LanguageVersion.ISO_2 && !ec.IsRuntimeBinder && MethodGroupExpr.IsExtensionMethodArgument (expr)) { + ec.Report.SymbolRelatedToPreviousError (type); + + var cand = ec.Module.GlobalRootNamespace.FindExtensionMethodNamespaces (ec, name, Arity); + string missing; + // a using directive or an assembly reference + if (cand != null) { + missing = "`" + string.Join ("' or `", cand.ToArray ()) + "' using directive"; + } else { + missing = "an assembly reference"; + } + + ec.Report.Error (1061, loc, + "Type `{0}' does not contain a definition for `{1}' and no extension method `{1}' of type `{0}' could be found. Are you missing {2}?", + type.GetSignatureForError (), name, missing); + return; + } + + base.Error_TypeDoesNotContainDefinition (ec, type, name); + } + + public override string GetSignatureForError () + { + return expr.GetSignatureForError () + "." + base.GetSignatureForError (); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + MemberAccess target = (MemberAccess) t; + + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ConditionalMemberAccess : MemberAccess + { + public ConditionalMemberAccess (Expression expr, string identifier, TypeArguments args, Location loc) + : base (expr, identifier, args, loc) + { + } + + public override bool HasConditionalAccess () + { + return true; + } + } + + /// + /// Implements checked expressions + /// + public class CheckedExpr : Expression { + + public Expression Expr; + + public CheckedExpr (Expression e, Location l) + { + Expr = e; + loc = l; + } + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, true)) + return Expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, true)) + Expr = Expr.Resolve (ec); + + if (Expr == null) + return null; + + if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression || Expr is DefaultValueExpression) + return Expr; + + eclass = Expr.eclass; + type = Expr.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + using (ec.With (EmitContext.Options.CheckedScope, true)) + Expr.Emit (ec); + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + using (ec.With (EmitContext.Options.CheckedScope, true)) + Expr.EmitBranchable (ec, target, on_true); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + Expr.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + using (ctx.With (BuilderContext.Options.CheckedScope, true)) { + return Expr.MakeExpression (ctx); + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CheckedExpr target = (CheckedExpr) t; + + target.Expr = Expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements the unchecked expression + /// + public class UnCheckedExpr : Expression { + + public Expression Expr; + + public UnCheckedExpr (Expression e, Location l) + { + Expr = e; + loc = l; + } + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, false)) + return Expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, false)) + Expr = Expr.Resolve (ec); + + if (Expr == null) + return null; + + if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression || Expr is DefaultValueExpression) + return Expr; + + eclass = Expr.eclass; + type = Expr.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + using (ec.With (EmitContext.Options.CheckedScope, false)) + Expr.Emit (ec); + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + using (ec.With (EmitContext.Options.CheckedScope, false)) + Expr.EmitBranchable (ec, target, on_true); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + Expr.FlowAnalysis (fc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + UnCheckedExpr target = (UnCheckedExpr) t; + + target.Expr = Expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// An Element Access expression. + /// + /// During semantic analysis these are transformed into + /// IndexerAccess, ArrayAccess or a PointerArithmetic. + /// + public class ElementAccess : Expression + { + public Arguments Arguments; + public Expression Expr; + + public ElementAccess (Expression e, Arguments args, Location loc) + { + Expr = e; + this.loc = loc; + this.Arguments = args; + } + + public bool ConditionalAccess { get; set; } + + public override Location StartLocation { + get { + return Expr.StartLocation; + } + } + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait () || Arguments.ContainsEmitWithAwait (); + } + + // + // We perform some simple tests, and then to "split" the emit and store + // code we create an instance of a different class, and return that. + // + Expression CreateAccessExpression (ResolveContext ec, bool conditionalAccessReceiver) + { + Expr = Expr.Resolve (ec); + if (Expr == null) + return null; + + type = Expr.Type; + + if (ConditionalAccess && !IsNullPropagatingValid (type)) { + Error_OperatorCannotBeApplied (ec, loc, "?", type); + return null; + } + + if (type.IsArray) + return new ArrayAccess (this, loc) { + ConditionalAccess = ConditionalAccess, + ConditionalAccessReceiver = conditionalAccessReceiver + }; + + if (type.IsPointer) + return MakePointerAccess (ec, type); + + FieldExpr fe = Expr as FieldExpr; + if (fe != null) { + var ff = fe.Spec as FixedFieldSpec; + if (ff != null) { + return MakePointerAccess (ec, ff.ElementType); + } + } + + var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false); + if (indexers != null || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + var indexer = new IndexerExpr (indexers, type, this) { + ConditionalAccess = ConditionalAccess + }; + + if (conditionalAccessReceiver) + indexer.SetConditionalAccessReceiver (); + + return indexer; + } + + Error_CannotApplyIndexing (ec, type, loc); + + return null; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = Arguments.CreateForExpressionTree (ec, Arguments, + Expr.CreateExpressionTree (ec)); + + return CreateExpressionFactoryCall (ec, "ArrayIndex", args); + } + + public static void Error_CannotApplyIndexing (ResolveContext rc, TypeSpec type, Location loc) + { + if (type != InternalType.ErrorType) { + rc.Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'", + type.GetSignatureForError ()); + } + } + + public override bool HasConditionalAccess () + { + return ConditionalAccess || Expr.HasConditionalAccess (); + } + + Expression MakePointerAccess (ResolveContext rc, TypeSpec type) + { + if (Arguments.Count != 1){ + rc.Report.Error (196, loc, "A pointer must be indexed by only one value"); + return null; + } + + var arg = Arguments[0]; + if (arg is NamedArgument) + Error_NamedArgument ((NamedArgument) arg, rc.Report); + + var index = arg.Expr.Resolve (rc); + if (index == null) + return null; + + index = ConvertExpressionToArrayIndex (rc, index, true); + + Expression p = new PointerArithmetic (Binary.Operator.Addition, Expr, index, type, loc); + return new Indirection (p, loc); + } + + protected override Expression DoResolve (ResolveContext rc) + { + Expression expr; + if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) { + if (HasConditionalAccess ()) { + using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) { + expr = CreateAccessExpression (rc, true); + if (expr == null) + return null; + + return expr.Resolve (rc); + } + } + } + + expr = CreateAccessExpression (rc, false); + if (expr == null) + return null; + + return expr.Resolve (rc); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression rhs) + { + var res = CreateAccessExpression (ec, false); + if (res == null) + return null; + + return res.ResolveLValue (ec, rhs); + } + + public override void Emit (EmitContext ec) + { + throw new Exception ("Should never be reached"); + } + + public static void Error_NamedArgument (NamedArgument na, Report Report) + { + Report.Error (1742, na.Location, "An element access expression cannot use named argument"); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + Expr.FlowAnalysis (fc); + + if (ConditionalAccess) + fc.BranchConditionalAccessDefiniteAssignment (); + + Arguments.FlowAnalysis (fc); + } + + public override string GetSignatureForError () + { + return Expr.GetSignatureForError (); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + ElementAccess target = (ElementAccess) t; + + target.Expr = Expr.Clone (clonectx); + if (Arguments != null) + target.Arguments = Arguments.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements array access + /// + public class ArrayAccess : Expression, IDynamicAssign, IMemoryLocation { + // + // Points to our "data" repository + // + ElementAccess ea; + + LocalTemporary temp; + bool prepared; + bool? has_await_args; + + public ArrayAccess (ElementAccess ea_data, Location l) + { + ea = ea_data; + loc = l; + } + + public bool ConditionalAccess { get; set; } + + public bool ConditionalAccessReceiver { get; set; } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + var ac = (ArrayContainer) ea.Expr.Type; + + if (!has_await_args.HasValue && ec.HasSet (BuilderContext.Options.AsyncBody) && ea.Arguments.ContainsEmitWithAwait ()) { + LoadInstanceAndArguments (ec, false, true); + } + + LoadInstanceAndArguments (ec, false, false); + + if (ac.Element.IsGenericParameter && mode == AddressOp.Load) + ec.Emit (OpCodes.Readonly); + + ec.EmitArrayAddress (ac); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (ConditionalAccess) + Error_NullShortCircuitInsideExpressionTree (ec); + + return ea.CreateExpressionTree (ec); + } + + public override bool ContainsEmitWithAwait () + { + return ea.ContainsEmitWithAwait (); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (ConditionalAccess) + throw new NotSupportedException ("null propagating operator assignment"); + + return DoResolve (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + // dynamic is used per argument in ConvertExpressionToArrayIndex case + bool dynamic; + ea.Arguments.Resolve (ec, out dynamic); + + var ac = ea.Expr.Type as ArrayContainer; + int rank = ea.Arguments.Count; + if (ac.Rank != rank) { + ec.Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'", + rank.ToString (), ac.Rank.ToString ()); + return null; + } + + type = ac.Element; + if (type.IsPointer && !ec.IsUnsafe) { + UnsafeError (ec, ea.Location); + } + + if (ConditionalAccessReceiver) + type = LiftMemberType (ec, type); + + foreach (Argument a in ea.Arguments) { + var na = a as NamedArgument; + if (na != null) + ElementAccess.Error_NamedArgument (na, ec.Report); + + a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr); + } + + eclass = ExprClass.Variable; + + return this; + } + + protected override void Error_NegativeArrayIndex (ResolveContext ec, Location loc) + { + ec.Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)"); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + ea.FlowAnalysis (fc); + } + + // + // Load the array arguments into the stack. + // + void LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait) + { + if (prepareAwait) { + ea.Expr = ea.Expr.EmitToField (ec); + } else { + var ie = new InstanceEmitter (ea.Expr, false); + ie.Emit (ec, ConditionalAccess); + + if (duplicateArguments) { + ec.Emit (OpCodes.Dup); + + var copy = new LocalTemporary (ea.Expr.Type); + copy.Store (ec); + ea.Expr = copy; + } + } + + var dup_args = ea.Arguments.Emit (ec, duplicateArguments, prepareAwait); + if (dup_args != null) + ea.Arguments = dup_args; + } + + public void Emit (EmitContext ec, bool leave_copy) + { + if (prepared) { + ec.EmitLoadFromPtr (type); + } else { + if (!has_await_args.HasValue && ec.HasSet (BuilderContext.Options.AsyncBody) && ea.Arguments.ContainsEmitWithAwait ()) { + LoadInstanceAndArguments (ec, false, true); + } + + if (ConditionalAccessReceiver) + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + + var ac = (ArrayContainer) ea.Expr.Type; + LoadInstanceAndArguments (ec, false, false); + ec.EmitArrayLoad (ac); + + if (ConditionalAccessReceiver) + ec.CloseConditionalAccess (type.IsNullableType && type != ac.Element ? type : null); + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (this.type); + temp.Store (ec); + } + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + var ac = (ArrayContainer) ea.Expr.Type; + TypeSpec t = source.Type; + + has_await_args = ec.HasSet (BuilderContext.Options.AsyncBody) && (ea.Arguments.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ()); + + // + // When we are dealing with a struct, get the address of it to avoid value copy + // Same cannot be done for reference type because array covariance and the + // check in ldelema requires to specify the type of array element stored at the index + // + if (t.IsStruct && ((isCompound && !(source is DynamicExpressionStatement)) || !BuiltinTypeSpec.IsPrimitiveType (t))) { + LoadInstanceAndArguments (ec, false, has_await_args.Value); + + if (has_await_args.Value) { + if (source.ContainsEmitWithAwait ()) { + source = source.EmitToField (ec); + isCompound = false; + prepared = true; + } + + LoadInstanceAndArguments (ec, isCompound, false); + } else { + prepared = true; + } + + ec.EmitArrayAddress (ac); + + if (isCompound) { + ec.Emit (OpCodes.Dup); + prepared = true; + } + } else { + LoadInstanceAndArguments (ec, isCompound, has_await_args.Value); + + if (has_await_args.Value) { + if (source.ContainsEmitWithAwait ()) + source = source.EmitToField (ec); + + LoadInstanceAndArguments (ec, false, false); + } + } + + source.Emit (ec); + + if (isCompound) { + var lt = ea.Expr as LocalTemporary; + if (lt != null) + lt.Release (ec); + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (this.type); + temp.Store (ec); + } + + if (prepared) { + ec.EmitStoreFromPtr (t); + } else { + ec.EmitArrayStore (ac); + } + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } + } + + public override Expression EmitToField (EmitContext ec) + { + // + // Have to be specialized for arrays to get access to + // underlying element. Instead of another result copy we + // need direct access to element + // + // Consider: + // + // CallRef (ref a[await Task.Factory.StartNew (() => 1)]); + // + ea.Expr = ea.Expr.EmitToField (ec); + ea.Arguments = ea.Arguments.Emit (ec, false, true); + return this; + } + + public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { +#if NET_4_0 || MOBILE_DYNAMIC + return SLE.Expression.ArrayAccess (ea.Expr.MakeExpression (ctx), MakeExpressionArguments (ctx)); +#else + throw new NotImplementedException (); +#endif + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return SLE.Expression.ArrayIndex (ea.Expr.MakeExpression (ctx), MakeExpressionArguments (ctx)); + } + + SLE.Expression[] MakeExpressionArguments (BuilderContext ctx) + { + using (ctx.With (BuilderContext.Options.CheckedScope, true)) { + return Arguments.MakeExpression (ea.Arguments, ctx); + } + } + } + + // + // Indexer access expression + // + class IndexerExpr : PropertyOrIndexerExpr, OverloadResolver.IBaseMembersProvider + { + IList indexers; + Arguments arguments; + TypeSpec queried_type; + + public IndexerExpr (IList indexers, TypeSpec queriedType, ElementAccess ea) + : this (indexers, queriedType, ea.Expr, ea.Arguments, ea.Location) + { + } + + public IndexerExpr (IList indexers, TypeSpec queriedType, Expression instance, Arguments args, Location loc) + : base (loc) + { + this.indexers = indexers; + this.queried_type = queriedType; + this.InstanceExpression = instance; + this.arguments = args; + } + + #region Properties + + protected override Arguments Arguments { + get { + return arguments; + } + set { + arguments = value; + } + } + + protected override TypeSpec DeclaringType { + get { + return best_candidate.DeclaringType; + } + } + + public override bool IsInstance { + get { + return true; + } + } + + public override bool IsStatic { + get { + return false; + } + } + + public override string KindName { + get { return "indexer"; } + } + + public override string Name { + get { + return "this"; + } + } + + #endregion + + public override bool ContainsEmitWithAwait () + { + return base.ContainsEmitWithAwait () || arguments.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (ConditionalAccess) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + + Arguments args = Arguments.CreateForExpressionTree (ec, arguments, + InstanceExpression.CreateExpressionTree (ec), + new TypeOfMethod (Getter, loc)); + + return CreateExpressionFactoryCall (ec, "Call", args); + } + + public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + LocalTemporary await_source_arg = null; + + if (isCompound) { + emitting_compound_assignment = true; + if (source is DynamicExpressionStatement) { + Emit (ec, false); + } else { + source.Emit (ec); + } + emitting_compound_assignment = false; + + if (has_await_arguments) { + await_source_arg = new LocalTemporary (Type); + await_source_arg.Store (ec); + + arguments.Add (new Argument (await_source_arg)); + + if (leave_copy) { + temp = await_source_arg; + } + + has_await_arguments = false; + } else { + arguments = null; + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (Type); + temp.Store (ec); + } + } + } else { + if (leave_copy) { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && (arguments.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ())) { + source = source.EmitToField (ec); + } else { + temp = new LocalTemporary (Type); + source.Emit (ec); + temp.Store (ec); + source = temp; + } + } + + arguments.Add (new Argument (source)); + } + + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpression; + if (arguments == null) + call.InstanceExpressionOnStack = true; + + call.Emit (ec, Setter, arguments, loc); + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } else if (leave_copy) { + source.Emit (ec); + } + + if (await_source_arg != null) { + await_source_arg.Release (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + arguments.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + public override string GetSignatureForError () + { + return best_candidate.GetSignatureForError (); + } + + public override SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { +#if STATIC + throw new NotSupportedException (); +#else + var value = new[] { source.MakeExpression (ctx) }; + var args = Arguments.MakeExpression (arguments, ctx).Concat (value); +#if NET_4_0 || MOBILE_DYNAMIC + return SLE.Expression.Block ( + SLE.Expression.Call (InstanceExpression.MakeExpression (ctx), (MethodInfo) Setter.GetMetaInfo (), args), + value [0]); +#else + return args.First (); +#endif +#endif + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + var args = Arguments.MakeExpression (arguments, ctx); + return SLE.Expression.Call (InstanceExpression.MakeExpression (ctx), (MethodInfo) Getter.GetMetaInfo (), args); +#endif + } + + protected override Expression OverloadResolve (ResolveContext rc, Expression right_side) + { + if (best_candidate != null) + return this; + + eclass = ExprClass.IndexerAccess; + + bool dynamic; + arguments.Resolve (rc, out dynamic); + + if (indexers == null && InstanceExpression.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + dynamic = true; + } else { + var res = new OverloadResolver (indexers, OverloadResolver.Restrictions.None, loc); + res.BaseMembersProvider = this; + res.InstanceQualifier = this; + + // TODO: Do I need 2 argument sets? + best_candidate = res.ResolveMember (rc, ref arguments); + if (best_candidate != null) + type = res.BestCandidateReturnType; + else if (!res.BestCandidateIsDynamic) + return null; + } + + // + // It has dynamic arguments + // + if (dynamic) { + Arguments args = new Arguments (arguments.Count + 1); + if (IsBase) { + rc.Report.Error (1972, loc, + "The indexer base access cannot be dynamically dispatched. Consider casting the dynamic arguments or eliminating the base access"); + } else { + args.Add (new Argument (InstanceExpression)); + } + args.AddRange (arguments); + + best_candidate = null; + return new DynamicIndexBinder (args, loc); + } + + // + // Try to avoid resolving left expression again + // + if (right_side != null) + ResolveInstanceExpression (rc, right_side); + + return this; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + IndexerExpr target = (IndexerExpr) t; + + if (arguments != null) + target.arguments = arguments.Clone (clonectx); + } + + public void SetConditionalAccessReceiver () + { + conditional_access_receiver = true; + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "indexer", GetSignatureForError (), loc); + } + + #region IBaseMembersProvider Members + + IList OverloadResolver.IBaseMembersProvider.GetBaseMembers (TypeSpec baseType) + { + return baseType == null ? null : MemberCache.FindMembers (baseType, MemberCache.IndexerNameAlias, false); + } + + IParametersMember OverloadResolver.IBaseMembersProvider.GetOverrideMemberParameters (MemberSpec member) + { + if (queried_type == member.DeclaringType) + return null; + + var filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, MemberKind.Indexer, ((IndexerSpec) member).Parameters, null); + return MemberCache.FindMember (queried_type, filter, BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as IParametersMember; + } + + MethodGroupExpr OverloadResolver.IBaseMembersProvider.LookupExtensionMethod (ResolveContext rc) + { + return null; + } + + #endregion + } + + // + // A base access expression + // + public class BaseThis : This + { + public BaseThis (Location loc) + : base (loc) + { + } + + public BaseThis (TypeSpec type, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Variable; + } + + #region Properties + + public override string Name { + get { + return "base"; + } + } + + #endregion + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (831, loc, "An expression tree may not contain a base access"); + return base.CreateExpressionTree (ec); + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + if (type == ec.Module.Compiler.BuiltinTypes.ValueType) { + var context_type = ec.CurrentType; + ec.Emit (OpCodes.Ldobj, context_type); + ec.Emit (OpCodes.Box, context_type); + } + } + + protected override void Error_ThisNotAvailable (ResolveContext ec) + { + if (ec.IsStatic) { + ec.Report.Error (1511, loc, "Keyword `base' is not available in a static method"); + } else { + ec.Report.Error (1512, loc, "Keyword `base' is not available in the current context"); + } + } + + public override void ResolveBase (ResolveContext ec) + { + base.ResolveBase (ec); + type = ec.CurrentType.BaseType; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// This class exists solely to pass the Type around and to be a dummy + /// that can be passed to the conversion functions (this is used by + /// foreach implementation to typecast the object return value from + /// get_Current into the proper type. All code has been generated and + /// we only care about the side effect conversions to be performed + /// + /// This is also now used as a placeholder where a no-action expression + /// is needed (the `New' class). + /// + public class EmptyExpression : Expression + { + sealed class OutAccessExpression : EmptyExpression + { + public OutAccessExpression (TypeSpec t) + : base (t) + { + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + rc.Report.Error (206, right_side.Location, + "A property, indexer or dynamic member access may not be passed as `ref' or `out' parameter"); + + return null; + } + } + + public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression UnaryAddress = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression EventAddition = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression EventSubtraction = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression MissingValue = new EmptyExpression (InternalType.FakeInternalType); + public static readonly Expression Null = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression OutAccess = new OutAccessExpression (InternalType.FakeInternalType); + + public EmptyExpression (TypeSpec t) + { + type = t; + eclass = ExprClass.Value; + loc = Location.Null; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + // nothing, as we only exist to not do anything. + } + + public override void EmitSideEffect (EmitContext ec) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + sealed class EmptyAwaitExpression : EmptyExpression + { + public EmptyAwaitExpression (TypeSpec type) + : base (type) + { + } + + public override bool ContainsEmitWithAwait () + { + return true; + } + } + + // + // Empty statement expression + // + public sealed class EmptyExpressionStatement : ExpressionStatement + { + public static readonly EmptyExpressionStatement Instance = new EmptyExpressionStatement (); + + private EmptyExpressionStatement () + { + loc = Location.Null; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return null; + } + + public override void EmitStatement (EmitContext ec) + { + // Do nothing + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Value; + type = ec.BuiltinTypes.Object; + return this; + } + + public override void Emit (EmitContext ec) + { + // Do nothing + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ErrorExpression : EmptyExpression + { + public static readonly ErrorExpression Instance = new ErrorExpression (); + public readonly int ErrorCode; + public readonly string Error; + + private ErrorExpression () + : base (InternalType.ErrorType) + { + } + + ErrorExpression (int errorCode, Location location, string error) + : base (InternalType.ErrorType) + { + this.ErrorCode = errorCode; + base.loc = location; + this.Error = error; + } + + public static ErrorExpression Create (int errorCode, Location location, string error) + { + return new ErrorExpression (errorCode, location, error); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return this; + } + + public override void Error_ValueAssignment (ResolveContext rc, Expression rhs) + { + } + + public override void Error_UnexpectedKind (ResolveContext ec, ResolveFlags flags, Location loc) + { + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + } + + public override void Error_OperatorCannotBeApplied (ResolveContext rc, Location loc, string oper, TypeSpec t) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class UserCast : Expression { + MethodSpec method; + Expression source; + + public UserCast (MethodSpec method, Expression source, Location l) + { + if (source == null) + throw new ArgumentNullException ("source"); + + this.method = method; + this.source = source; + type = method.ReturnType; + loc = l; + } + + public Expression Source { + get { + return source; + } + } + + public override bool ContainsEmitWithAwait () + { + return source.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (3); + args.Add (new Argument (source.CreateExpressionTree (ec))); + args.Add (new Argument (new TypeOf (type, loc))); + args.Add (new Argument (new TypeOfMethod (method, loc))); + return CreateExpressionFactoryCall (ec, "Convert", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + ObsoleteAttribute oa = method.GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, GetSignatureForError (), loc, ec.Report); + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + source.Emit (ec); + ec.MarkCallEntry (loc); + ec.Emit (OpCodes.Call, method); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + source.FlowAnalysis (fc); + } + + public override string GetSignatureForError () + { + return TypeManager.CSharpSignature (method); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Convert (source.MakeExpression (ctx), type.GetMetaInfo (), (MethodInfo) method.GetMetaInfo ()); +#endif + } + } + + // + // Holds additional type specifiers like ?, *, [] + // + public class ComposedTypeSpecifier + { + public static readonly ComposedTypeSpecifier SingleDimension = new ComposedTypeSpecifier (1, Location.Null); + + public readonly int Dimension; + public readonly Location Location; + + public ComposedTypeSpecifier (int specifier, Location loc) + { + this.Dimension = specifier; + this.Location = loc; + } + + #region Properties + public bool IsNullable { + get { + return Dimension == -1; + } + } + + public bool IsPointer { + get { + return Dimension == -2; + } + } + + public ComposedTypeSpecifier Next { get; set; } + + #endregion + + public static ComposedTypeSpecifier CreateArrayDimension (int dimension, Location loc) + { + return new ComposedTypeSpecifier (dimension, loc); + } + + public static ComposedTypeSpecifier CreateNullable (Location loc) + { + return new ComposedTypeSpecifier (-1, loc); + } + + public static ComposedTypeSpecifier CreatePointer (Location loc) + { + return new ComposedTypeSpecifier (-2, loc); + } + + public string GetSignatureForError () + { + string s = + IsPointer ? "*" : + IsNullable ? "?" : + ArrayContainer.GetPostfixSignature (Dimension); + + return Next != null ? s + Next.GetSignatureForError () : s; + } + } + + // + // This class is used to "construct" the type during a typecast + // operation. Since the Type.GetType class in .NET can parse + // the type specification, we just use this to construct the type + // one bit at a time. + // + public class ComposedCast : TypeExpr { + FullNamedExpression left; + ComposedTypeSpecifier spec; + + public FullNamedExpression Left { + get { return this.left; } + } + + public ComposedTypeSpecifier Spec { + get { + return this.spec; + } + } + + public ComposedCast (FullNamedExpression left, ComposedTypeSpecifier spec) + { + if (spec == null) + throw new ArgumentNullException ("spec"); + + this.left = left; + this.spec = spec; + this.loc = left.Location; + } + + public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments) + { + type = left.ResolveAsType (ec); + if (type == null) + return null; + + eclass = ExprClass.Type; + + var single_spec = spec; + + if (single_spec.IsNullable) { + type = new Nullable.NullableType (type, loc).ResolveAsType (ec); + if (type == null) + return null; + + single_spec = single_spec.Next; + } else if (single_spec.IsPointer) { + if (!TypeManager.VerifyUnmanaged (ec.Module, type, loc)) + return null; + + if (!ec.IsUnsafe) { + UnsafeError (ec.Module.Compiler.Report, loc); + } + + do { + type = PointerContainer.MakeType (ec.Module, type); + single_spec = single_spec.Next; + } while (single_spec != null && single_spec.IsPointer); + } + + if (single_spec != null && single_spec.Dimension > 0) { + if (type.IsSpecialRuntimeType) { + ec.Module.Compiler.Report.Error (611, loc, "Array elements cannot be of type `{0}'", type.GetSignatureForError ()); + } else if (type.IsStatic) { + ec.Module.Compiler.Report.SymbolRelatedToPreviousError (type); + ec.Module.Compiler.Report.Error (719, loc, "Array elements cannot be of static type `{0}'", + type.GetSignatureForError ()); + } else { + MakeArray (ec.Module, single_spec); + } + } + + return type; + } + + void MakeArray (ModuleContainer module, ComposedTypeSpecifier spec) + { + if (spec.Next != null) + MakeArray (module, spec.Next); + + type = ArrayContainer.MakeType (module, type, spec.Dimension); + } + + public override string GetSignatureForError () + { + return left.GetSignatureForError () + spec.GetSignatureForError (); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + class FixedBufferPtr : Expression + { + readonly Expression array; + + public FixedBufferPtr (Expression array, TypeSpec array_type, Location l) + { + this.type = array_type; + this.array = array; + this.loc = l; + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return null; + } + + public override void Emit(EmitContext ec) + { + array.Emit (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = PointerContainer.MakeType (ec.Module, type); + eclass = ExprClass.Value; + return this; + } + } + + + // + // This class is used to represent the address of an array, used + // only by the Fixed statement, this generates "&a [0]" construct + // for fixed (char *pa = a) + // + class ArrayPtr : FixedBufferPtr + { + public ArrayPtr (Expression array, TypeSpec array_type, Location l): + base (array, array_type, l) + { + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + ec.EmitInt (0); + ec.Emit (OpCodes.Ldelema, ((PointerContainer) type).Element); + } + } + + // + // Encapsulates a conversion rules required for array indexes + // + public class ArrayIndexCast : TypeCast + { + public ArrayIndexCast (Expression expr, TypeSpec returnType) + : base (expr, returnType) + { + if (expr.Type == returnType) // int -> int + throw new ArgumentException ("unnecessary array index conversion"); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + using (ec.Set (ResolveContext.Options.CheckedScope)) { + return base.CreateExpressionTree (ec); + } + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + + switch (child.Type.BuiltinType) { + case BuiltinTypeSpec.Type.UInt: + ec.Emit (OpCodes.Conv_U); + break; + case BuiltinTypeSpec.Type.Long: + ec.Emit (OpCodes.Conv_Ovf_I); + break; + case BuiltinTypeSpec.Type.ULong: + ec.Emit (OpCodes.Conv_Ovf_I_Un); + break; + default: + throw new InternalErrorException ("Cannot emit cast to unknown array element type", type); + } + } + } + + // + // Implements the `stackalloc' keyword + // + public class StackAlloc : Expression { + TypeSpec otype; + Expression t; + Expression count; + + public StackAlloc (Expression type, Expression count, Location l) + { + t = type; + this.count = count; + loc = l; + } + + public Expression TypeExpression { + get { + return this.t; + } + } + + public Expression CountExpression { + get { + return this.count; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + count = count.Resolve (ec); + if (count == null) + return null; + + if (count.Type.BuiltinType != BuiltinTypeSpec.Type.UInt){ + count = Convert.ImplicitConversionRequired (ec, count, ec.BuiltinTypes.Int, loc); + if (count == null) + return null; + } + + Constant c = count as Constant; + if (c != null && c.IsNegative) { + ec.Report.Error (247, loc, "Cannot use a negative size with stackalloc"); + } + + if (ec.HasAny (ResolveContext.Options.CatchScope | ResolveContext.Options.FinallyScope)) { + ec.Report.Error (255, loc, "Cannot use stackalloc in finally or catch"); + } + + otype = t.ResolveAsType (ec); + if (otype == null) + return null; + + if (!TypeManager.VerifyUnmanaged (ec.Module, otype, loc)) + return null; + + type = PointerContainer.MakeType (ec.Module, otype); + eclass = ExprClass.Value; + + return this; + } + + public override void Emit (EmitContext ec) + { + int size = BuiltinTypeSpec.GetSize (otype); + + count.Emit (ec); + + if (size == 0) + ec.Emit (OpCodes.Sizeof, otype); + else + ec.EmitInt (size); + + ec.Emit (OpCodes.Mul_Ovf_Un); + ec.Emit (OpCodes.Localloc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + StackAlloc target = (StackAlloc) t; + target.count = count.Clone (clonectx); + target.t = t.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // An object initializer expression + // + public class ElementInitializer : Assign + { + public readonly string Name; + + public ElementInitializer (string name, Expression initializer, Location loc) + : base (null, initializer, loc) + { + this.Name = name; + } + + public bool IsDictionaryInitializer { + get { + return Name == null; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + ElementInitializer target = (ElementInitializer) t; + target.source = source.Clone (clonectx); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + FieldExpr fe = target as FieldExpr; + if (fe != null) + args.Add (new Argument (fe.CreateTypeOfExpression ())); + else + args.Add (new Argument (((PropertyExpr) target).CreateSetterTypeOfExpression (ec))); + + string mname; + Expression arg_expr; + var cinit = source as CollectionOrObjectInitializers; + if (cinit == null) { + mname = "Bind"; + arg_expr = source.CreateExpressionTree (ec); + } else { + mname = cinit.IsEmpty || cinit.Initializers[0] is ElementInitializer ? "MemberBind" : "ListBind"; + arg_expr = cinit.CreateExpressionTree (ec, !cinit.IsEmpty); + } + + args.Add (new Argument (arg_expr)); + return CreateExpressionFactoryCall (ec, mname, args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (source == null) + return EmptyExpressionStatement.Instance; + + if (!ResolveElement (ec)) + return null; + + if (source is CollectionOrObjectInitializers) { + Expression previous = ec.CurrentInitializerVariable; + ec.CurrentInitializerVariable = target; + source = source.Resolve (ec); + ec.CurrentInitializerVariable = previous; + if (source == null) + return null; + + eclass = source.eclass; + type = source.Type; + return this; + } + + return base.DoResolve (ec); + } + + public override void EmitStatement (EmitContext ec) + { + if (source is CollectionOrObjectInitializers) + source.Emit (ec); + else + base.EmitStatement (ec); + } + + protected virtual bool ResolveElement (ResolveContext rc) + { + var t = rc.CurrentInitializerVariable.Type; + if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments args = new Arguments (1); + args.Add (new Argument (rc.CurrentInitializerVariable)); + target = new DynamicMemberBinder (Name, args, loc); + } else { + + var member = MemberLookup (rc, false, t, Name, 0, MemberLookupRestrictions.ExactArity, loc); + if (member == null) { + member = Expression.MemberLookup (rc, true, t, Name, 0, MemberLookupRestrictions.ExactArity, loc); + + if (member != null) { + // TODO: ec.Report.SymbolRelatedToPreviousError (member); + ErrorIsInaccesible (rc, member.GetSignatureForError (), loc); + return false; + } + } + + if (member == null) { + Error_TypeDoesNotContainDefinition (rc, loc, t, Name); + return false; + } + + var me = member as MemberExpr; + if (me is EventExpr) { + me = me.ResolveMemberAccess (rc, null, null); + } else if (!(member is PropertyExpr || member is FieldExpr)) { + rc.Report.Error (1913, loc, + "Member `{0}' cannot be initialized. An object initializer may only be used for fields, or properties", + member.GetSignatureForError ()); + + return false; + } + + if (me.IsStatic) { + rc.Report.Error (1914, loc, + "Static field or property `{0}' cannot be assigned in an object initializer", + me.GetSignatureForError ()); + } + + target = me; + me.InstanceExpression = rc.CurrentInitializerVariable; + } + + return true; + } + } + + // + // A collection initializer expression + // + class CollectionElementInitializer : Invocation + { + public readonly bool IsSingle; + + + public class ElementInitializerArgument : Argument + { + public ElementInitializerArgument (Expression e) + : base (e) + { + } + } + + sealed class AddMemberAccess : MemberAccess + { + public AddMemberAccess (Expression expr, Location loc) + : base (expr, "Add", loc) + { + } + + protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name) + { + if (TypeManager.HasElementType (type)) + return; + + base.Error_TypeDoesNotContainDefinition (ec, type, name); + } + } + + public CollectionElementInitializer (Expression argument) + : base (null, new Arguments (1)) + { + IsSingle = true; + base.arguments.Add (new ElementInitializerArgument (argument)); + this.loc = argument.Location; + } + + public CollectionElementInitializer (List arguments, Location loc) + : base (null, new Arguments (arguments.Count)) + { + IsSingle = false; + foreach (Expression e in arguments) + base.arguments.Add (new ElementInitializerArgument (e)); + + this.loc = loc; + } + + public CollectionElementInitializer (Location loc) + : base (null, null) + { + this.loc = loc; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (mg.CreateExpressionTree (ec))); + + var expr_initializers = new ArrayInitializer (arguments.Count, loc); + foreach (Argument a in arguments) + expr_initializers.Add (a.CreateExpressionTree (ec)); + + args.Add (new Argument (new ArrayCreation ( + CreateExpressionTypeExpression (ec, loc), expr_initializers, loc))); + return CreateExpressionFactoryCall (ec, "ElementInit", args); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CollectionElementInitializer target = (CollectionElementInitializer) t; + if (arguments != null) + target.arguments = arguments.Clone (clonectx); + } + + protected override Expression DoResolve (ResolveContext ec) + { + base.expr = new AddMemberAccess (ec.CurrentInitializerVariable, loc); + + return base.DoResolve (ec); + } + } + + class DictionaryElementInitializer : ElementInitializer + { + readonly Arguments args; + + public DictionaryElementInitializer (List arguments, Expression initializer, Location loc) + : base (null, initializer, loc) + { + this.args = new Arguments (arguments.Count); + foreach (var arg in arguments) + this.args.Add (new Argument (arg)); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (8074, loc, "Expression tree cannot contain a dictionary initializer"); + return null; + } + + protected override bool ResolveElement (ResolveContext rc) + { + var init = rc.CurrentInitializerVariable; + var type = init.Type; + + var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false); + if (indexers == null && type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) { + ElementAccess.Error_CannotApplyIndexing (rc, type, loc); + return false; + } + + target = new IndexerExpr (indexers, type, init, args, loc).Resolve (rc); + return true; + } + } + + // + // A block of object or collection initializers + // + public class CollectionOrObjectInitializers : ExpressionStatement + { + IList initializers; + bool is_collection_initialization; + + public CollectionOrObjectInitializers (Location loc) + : this (new Expression[0], loc) + { + } + + public CollectionOrObjectInitializers (IList initializers, Location loc) + { + this.initializers = initializers; + this.loc = loc; + } + + public bool IsEmpty { + get { + return initializers.Count == 0; + } + } + + public bool IsCollectionInitializer { + get { + return is_collection_initialization; + } + } + + public IList Initializers { + get { + return initializers; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + CollectionOrObjectInitializers t = (CollectionOrObjectInitializers) target; + + t.initializers = new List (initializers.Count); + foreach (var e in initializers) + t.initializers.Add (e.Clone (clonectx)); + } + + public override bool ContainsEmitWithAwait () + { + foreach (var e in initializers) { + if (e.ContainsEmitWithAwait ()) + return true; + } + + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return CreateExpressionTree (ec, false); + } + + public Expression CreateExpressionTree (ResolveContext ec, bool inferType) + { + var expr_initializers = new ArrayInitializer (initializers.Count, loc); + foreach (Expression e in initializers) { + Expression expr = e.CreateExpressionTree (ec); + if (expr != null) + expr_initializers.Add (expr); + } + + if (inferType) + return new ImplicitlyTypedArrayCreation (expr_initializers, loc); + + return new ArrayCreation (new TypeExpression (ec.Module.PredefinedTypes.MemberBinding.Resolve (), loc), expr_initializers, loc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + List element_names = null; + for (int i = 0; i < initializers.Count; ++i) { + Expression initializer = initializers [i]; + ElementInitializer element_initializer = initializer as ElementInitializer; + + if (i == 0) { + if (element_initializer != null) { + element_names = new List (initializers.Count); + if (!element_initializer.IsDictionaryInitializer) + element_names.Add (element_initializer.Name); + } else if (initializer is CompletingExpression) { + initializer.Resolve (ec); + throw new InternalErrorException ("This line should never be reached"); + } else { + var t = ec.CurrentInitializerVariable.Type; + // LAMESPEC: The collection must implement IEnumerable only, no dynamic support + if (!t.ImplementsInterface (ec.BuiltinTypes.IEnumerable, false) && t.BuiltinType != BuiltinTypeSpec.Type.Dynamic) { + ec.Report.Error (1922, loc, "A field or property `{0}' cannot be initialized with a collection " + + "object initializer because type `{1}' does not implement `{2}' interface", + ec.CurrentInitializerVariable.GetSignatureForError (), + ec.CurrentInitializerVariable.Type.GetSignatureForError (), + ec.BuiltinTypes.IEnumerable.GetSignatureForError ()); + return null; + } + is_collection_initialization = true; + } + } else { + if (is_collection_initialization != (element_initializer == null)) { + ec.Report.Error (747, initializer.Location, "Inconsistent `{0}' member declaration", + is_collection_initialization ? "collection initializer" : "object initializer"); + continue; + } + + if (!is_collection_initialization && !element_initializer.IsDictionaryInitializer) { + if (element_names.Contains (element_initializer.Name)) { + ec.Report.Error (1912, element_initializer.Location, + "An object initializer includes more than one member `{0}' initialization", + element_initializer.Name); + } else { + element_names.Add (element_initializer.Name); + } + } + } + + Expression e = initializer.Resolve (ec); + if (e == EmptyExpressionStatement.Instance) + initializers.RemoveAt (i--); + else + initializers [i] = e; + } + + type = ec.CurrentInitializerVariable.Type; + if (is_collection_initialization) { + if (TypeManager.HasElementType (type)) { + ec.Report.Error (1925, loc, "Cannot initialize object of type `{0}' with a collection initializer", + type.GetSignatureForError ()); + } + } + + eclass = ExprClass.Variable; + return this; + } + + public override void Emit (EmitContext ec) + { + EmitStatement (ec); + } + + public override void EmitStatement (EmitContext ec) + { + foreach (ExpressionStatement e in initializers) { + // TODO: need location region + ec.Mark (e.Location); + e.EmitStatement (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + foreach (var initializer in initializers) { + if (initializer != null) + initializer.FlowAnalysis (fc); + } + } + } + + // + // New expression with element/object initializers + // + public class NewInitialize : New + { + // + // This class serves as a proxy for variable initializer target instances. + // A real variable is assigned later when we resolve left side of an + // assignment + // + sealed class InitializerTargetExpression : Expression, IMemoryLocation + { + NewInitialize new_instance; + + public InitializerTargetExpression (NewInitialize newInstance) + { + this.type = newInstance.type; + this.loc = newInstance.loc; + this.eclass = newInstance.eclass; + this.new_instance = newInstance; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + // Should not be reached + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return this; + } + + public override void Emit (EmitContext ec) + { + Expression e = (Expression) new_instance.instance; + e.Emit (ec); + } + + public override Expression EmitToField (EmitContext ec) + { + return (Expression) new_instance.instance; + } + + #region IMemoryLocation Members + + public void AddressOf (EmitContext ec, AddressOp mode) + { + new_instance.instance.AddressOf (ec, mode); + } + + #endregion + } + + CollectionOrObjectInitializers initializers; + IMemoryLocation instance; + DynamicExpressionStatement dynamic; + + public NewInitialize (FullNamedExpression requested_type, Arguments arguments, CollectionOrObjectInitializers initializers, Location l) + : base (requested_type, arguments, l) + { + this.initializers = initializers; + } + + public CollectionOrObjectInitializers Initializers { + get { + return initializers; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + base.CloneTo (clonectx, t); + + NewInitialize target = (NewInitialize) t; + target.initializers = (CollectionOrObjectInitializers) initializers.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + return base.ContainsEmitWithAwait () || initializers.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (base.CreateExpressionTree (ec))); + if (!initializers.IsEmpty) + args.Add (new Argument (initializers.CreateExpressionTree (ec, initializers.IsCollectionInitializer))); + + return CreateExpressionFactoryCall (ec, + initializers.IsCollectionInitializer ? "ListInit" : "MemberInit", + args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression e = base.DoResolve (ec); + if (type == null) + return null; + + if (type.IsDelegate) { + ec.Report.Error (1958, Initializers.Location, + "Object and collection initializers cannot be used to instantiate a delegate"); + } + + Expression previous = ec.CurrentInitializerVariable; + ec.CurrentInitializerVariable = new InitializerTargetExpression (this); + initializers.Resolve (ec); + ec.CurrentInitializerVariable = previous; + + dynamic = e as DynamicExpressionStatement; + if (dynamic != null) + return this; + + return e; + } + + public override void Emit (EmitContext ec) + { + if (method == null && TypeSpec.IsValueType (type) && initializers.Initializers.Count > 1 && ec.HasSet (BuilderContext.Options.AsyncBody) && initializers.ContainsEmitWithAwait ()) { + var fe = ec.GetTemporaryField (type); + + if (!Emit (ec, fe)) + fe.Emit (ec); + + return; + } + + base.Emit (ec); + } + + public override bool Emit (EmitContext ec, IMemoryLocation target) + { + bool left_on_stack; + if (dynamic != null) { + dynamic.Emit (ec); + left_on_stack = true; + } else { + left_on_stack = base.Emit (ec, target); + } + + if (initializers.IsEmpty) + return left_on_stack; + + LocalTemporary temp = null; + + instance = target as LocalTemporary; + if (instance == null) + instance = target as StackFieldExpr; + + if (instance == null) { + if (!left_on_stack) { + VariableReference vr = target as VariableReference; + + // FIXME: This still does not work correctly for pre-set variables + if (vr != null && vr.IsRef) + target.AddressOf (ec, AddressOp.Load); + + ((Expression) target).Emit (ec); + left_on_stack = true; + } + + if (ec.HasSet (BuilderContext.Options.AsyncBody) && initializers.ContainsEmitWithAwait ()) { + instance = new EmptyAwaitExpression (Type).EmitToField (ec) as IMemoryLocation; + } else { + temp = new LocalTemporary (type); + instance = temp; + } + } + + if (left_on_stack && temp != null) + temp.Store (ec); + + initializers.Emit (ec); + + if (left_on_stack) { + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } else { + ((Expression) instance).Emit (ec); + } + } + + return left_on_stack; + } + + protected override IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp Mode) + { + instance = base.EmitAddressOf (ec, Mode); + + if (!initializers.IsEmpty) + initializers.Emit (ec); + + return instance; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + initializers.FlowAnalysis (fc); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class NewAnonymousType : New + { + static readonly AnonymousTypeParameter[] EmptyParameters = new AnonymousTypeParameter[0]; + + List parameters; + readonly TypeContainer parent; + AnonymousTypeClass anonymous_type; + + public NewAnonymousType (List parameters, TypeContainer parent, Location loc) + : base (null, null, loc) + { + this.parameters = parameters; + this.parent = parent; + } + + public List Parameters { + get { + return this.parameters; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + if (parameters == null) + return; + + NewAnonymousType t = (NewAnonymousType) target; + t.parameters = new List (parameters.Count); + foreach (AnonymousTypeParameter atp in parameters) + t.parameters.Add ((AnonymousTypeParameter) atp.Clone (clonectx)); + } + + AnonymousTypeClass CreateAnonymousType (ResolveContext ec, IList parameters) + { + AnonymousTypeClass type = parent.Module.GetAnonymousType (parameters); + if (type != null) + return type; + + type = AnonymousTypeClass.Create (parent, parameters, loc); + if (type == null) + return null; + + int errors = ec.Report.Errors; + type.CreateContainer (); + type.DefineContainer (); + type.Define (); + if ((ec.Report.Errors - errors) == 0) { + parent.Module.AddAnonymousType (type); + type.PrepareEmit (); + } + + return type; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (parameters == null) + return base.CreateExpressionTree (ec); + + var init = new ArrayInitializer (parameters.Count, loc); + foreach (var m in anonymous_type.Members) { + var p = m as Property; + if (p != null) + init.Add (new TypeOfMethod (MemberCache.GetMember (type, p.Get.Spec), loc)); + } + + var ctor_args = new ArrayInitializer (arguments.Count, loc); + foreach (Argument a in arguments) + ctor_args.Add (a.CreateExpressionTree (ec)); + + Arguments args = new Arguments (3); + args.Add (new Argument (new TypeOfMethod (method, loc))); + args.Add (new Argument (new ArrayCreation (CreateExpressionTypeExpression (ec, loc), ctor_args, loc))); + args.Add (new Argument (new ImplicitlyTypedArrayCreation (init, loc))); + + return CreateExpressionFactoryCall (ec, "New", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (ec.HasSet (ResolveContext.Options.ConstantScope)) { + ec.Report.Error (836, loc, "Anonymous types cannot be used in this expression"); + return null; + } + + if (parameters == null) { + anonymous_type = CreateAnonymousType (ec, EmptyParameters); + RequestedType = new TypeExpression (anonymous_type.Definition, loc); + return base.DoResolve (ec); + } + + bool error = false; + arguments = new Arguments (parameters.Count); + var t_args = new TypeSpec [parameters.Count]; + for (int i = 0; i < parameters.Count; ++i) { + Expression e = parameters [i].Resolve (ec); + if (e == null) { + error = true; + continue; + } + + arguments.Add (new Argument (e)); + t_args [i] = e.Type; + } + + if (error) + return null; + + anonymous_type = CreateAnonymousType (ec, parameters); + if (anonymous_type == null) + return null; + + type = anonymous_type.Definition.MakeGenericType (ec.Module, t_args); + method = (MethodSpec) MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly); + eclass = ExprClass.Value; + return this; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class AnonymousTypeParameter : ShimExpression + { + public readonly string Name; + + public AnonymousTypeParameter (Expression initializer, string name, Location loc) + : base (initializer) + { + this.Name = name; + this.loc = loc; + } + + public AnonymousTypeParameter (Parameter parameter) + : base (new SimpleName (parameter.Name, parameter.Location)) + { + this.Name = parameter.Name; + this.loc = parameter.Location; + } + + public override bool Equals (object o) + { + AnonymousTypeParameter other = o as AnonymousTypeParameter; + return other != null && Name == other.Name; + } + + public override int GetHashCode () + { + return Name.GetHashCode (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression e = expr.Resolve (ec); + if (e == null) + return null; + + if (e.eclass == ExprClass.MethodGroup) { + Error_InvalidInitializer (ec, e.ExprClassName); + return null; + } + + type = e.Type; + if (type.Kind == MemberKind.Void || type == InternalType.NullLiteral || type == InternalType.AnonymousMethod || type.IsPointer) { + Error_InvalidInitializer (ec, type.GetSignatureForError ()); + return null; + } + + return e; + } + + protected virtual void Error_InvalidInitializer (ResolveContext ec, string initializer) + { + ec.Report.Error (828, loc, "An anonymous type property `{0}' cannot be initialized with `{1}'", + Name, initializer); + } + } + + public class CatchFilterExpression : BooleanExpression + { + public CatchFilterExpression (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/field.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/field.cs new file mode 100644 index 000000000..265e43620 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/field.cs @@ -0,0 +1,733 @@ +// +// field.cs: All field handlers +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public class FieldDeclarator + { + public FieldDeclarator (SimpleMemberName name, Expression initializer) + { + this.Name = name; + this.Initializer = initializer; + } + + #region Properties + + public SimpleMemberName Name { get; private set; } + public Expression Initializer { get; private set; } + + #endregion + + public virtual FullNamedExpression GetFieldTypeExpression (FieldBase field) + { + return new TypeExpression (field.MemberType, Name.Location); + } + } + + // + // Abstract class for all fields + // + abstract public class FieldBase : MemberBase + { + protected FieldBuilder FieldBuilder; + protected FieldSpec spec; + public Status status; + protected Expression initializer; + protected List declarators; + + [Flags] + public enum Status : byte { + HAS_OFFSET = 4 // Used by FieldMember. + } + + static readonly string[] attribute_targets = new string [] { "field" }; + + protected FieldBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, Attributes attrs) + : base (parent, type, mod, allowed_mod | Modifiers.ABSTRACT, Modifiers.PRIVATE, name, attrs) + { + if ((mod & Modifiers.ABSTRACT) != 0) + Report.Error (681, Location, "The modifier 'abstract' is not valid on fields. Try using a property instead"); + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Field; + } + } + + public Expression Initializer { + get { + return initializer; + } + set { + this.initializer = value; + } + } + + public string Name { + get { + return MemberName.Name; + } + } + + public FieldSpec Spec { + get { + return spec; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + public List Declarators { + get { + return this.declarators; + } + } + #endregion + + public void AddDeclarator (FieldDeclarator declarator) + { + if (declarators == null) + declarators = new List (2); + + declarators.Add (declarator); + + Parent.AddNameToContainer (this, declarator.Name.Value); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.FieldOffset) { + status |= Status.HAS_OFFSET; + + if (!Parent.PartialContainer.HasExplicitLayout) { + Report.Error (636, Location, "The FieldOffset attribute can only be placed on members of types marked with the StructLayout(LayoutKind.Explicit)"); + return; + } + + if ((ModFlags & Modifiers.STATIC) != 0 || this is Const) { + Report.Error (637, Location, "The FieldOffset attribute is not allowed on static or const fields"); + return; + } + } + + if (a.Type == pa.FixedBuffer) { + Report.Error (1716, Location, "Do not use 'System.Runtime.CompilerServices.FixedBuffer' attribute. Use the 'fixed' field modifier instead"); + return; + } + +#if false + if (a.Type == pa.MarshalAs) { + UnmanagedMarshal marshal = a.GetMarshal (this); + if (marshal != null) { + FieldBuilder.SetMarshal (marshal); + } + return; + } +#endif + if ((a.HasSecurityAttribute)) { + a.Error_InvalidSecurityParent (); + return; + } + + if (a.Type == pa.Dynamic) { + a.Error_MisusedDynamicAttribute (); + return; + } + + FieldBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public void SetCustomAttribute (MethodSpec ctor, byte[] data) + { + FieldBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), data); + } + + protected override bool CheckBase () + { + if (!base.CheckBase ()) + return false; + + MemberSpec candidate; + bool overrides = false; + var conflict_symbol = MemberCache.FindBaseMember (this, out candidate, ref overrides); + if (conflict_symbol == null) + conflict_symbol = candidate; + + if (conflict_symbol == null) { + if ((ModFlags & Modifiers.NEW) != 0) { + Report.Warning (109, 4, Location, "The member `{0}' does not hide an inherited member. The new keyword is not required", + GetSignatureForError ()); + } + } else { + if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE | Modifiers.BACKING_FIELD)) == 0) { + Report.SymbolRelatedToPreviousError (conflict_symbol); + Report.Warning (108, 2, Location, "`{0}' hides inherited member `{1}'. Use the new keyword if hiding was intended", + GetSignatureForError (), conflict_symbol.GetSignatureForError ()); + } + + if (conflict_symbol.IsAbstract) { + Report.SymbolRelatedToPreviousError (conflict_symbol); + Report.Error (533, Location, "`{0}' hides inherited abstract member `{1}'", + GetSignatureForError (), conflict_symbol.GetSignatureForError ()); + } + } + + return true; + } + + public virtual Constant ConvertInitializer (ResolveContext rc, Constant expr) + { + return expr.ConvertImplicitly (MemberType); + } + + protected override void DoMemberTypeDependentChecks () + { + base.DoMemberTypeDependentChecks (); + + if (MemberType.IsGenericParameter) + return; + + if (MemberType.IsStatic) + Error_VariableOfStaticClass (Location, GetSignatureForError (), MemberType, Report); + + if (!IsCompilerGenerated) + CheckBase (); + + IsTypePermitted (); + } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader { + get { return "F:"; } + } + + public override void Emit () + { + if (member_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (FieldBuilder); + } else if (!Parent.IsCompilerGenerated && member_type.HasDynamicElement) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (FieldBuilder, member_type, Location); + } + + if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated) + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (FieldBuilder); + if ((ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0) + Module.PredefinedAttributes.DebuggerBrowsable.EmitAttribute (FieldBuilder, System.Diagnostics.DebuggerBrowsableState.Never); + + if (OptAttributes != null) { + OptAttributes.Emit (); + } + + if (((status & Status.HAS_OFFSET) == 0) && (ModFlags & (Modifiers.STATIC | Modifiers.BACKING_FIELD)) == 0 && Parent.PartialContainer.HasExplicitLayout) { + Report.Error (625, Location, "`{0}': Instance field types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute", GetSignatureForError ()); + } + + ConstraintChecker.Check (this, member_type, type_expr.Location); + + base.Emit (); + } + + public static void Error_VariableOfStaticClass (Location loc, string variable_name, TypeSpec static_class, Report Report) + { + Report.SymbolRelatedToPreviousError (static_class); + Report.Error (723, loc, "`{0}': cannot declare variables of static types", + variable_name); + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if (!MemberType.IsCLSCompliant () || this is FixedField) { + Report.Warning (3003, 1, Location, "Type of `{0}' is not CLS-compliant", + GetSignatureForError ()); + } + return true; + } + } + + // + // Field specification + // + public class FieldSpec : MemberSpec, IInterfaceMemberSpec + { + FieldInfo metaInfo; + TypeSpec memberType; + + public FieldSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo info, Modifiers modifiers) + : base (MemberKind.Field, declaringType, definition, modifiers) + { + this.metaInfo = info; + this.memberType = memberType; + } + + #region Properties + + public bool IsReadOnly { + get { + return (Modifiers & Modifiers.READONLY) != 0; + } + } + + public TypeSpec MemberType { + get { + return memberType; + } + } + +#endregion + + public FieldInfo GetMetaInfo () + { + if ((state & StateFlags.PendingMetaInflate) != 0) { + var decl_meta = DeclaringType.GetMetaInfo (); + if (DeclaringType.IsTypeBuilder) { + metaInfo = TypeBuilder.GetField (decl_meta, metaInfo); + } else { + var orig_token = metaInfo.MetadataToken; + metaInfo = decl_meta.GetField (Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + if (metaInfo.MetadataToken != orig_token) + throw new NotImplementedException ("Resolved to wrong meta token"); + + // What a stupid API, does not work because field handle is imported + // metaInfo = FieldInfo.GetFieldFromHandle (metaInfo.FieldHandle, DeclaringType.MetaInfo.TypeHandle); + } + + state &= ~StateFlags.PendingMetaInflate; + } + + return metaInfo; + } + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var fs = (FieldSpec) base.InflateMember (inflator); + fs.memberType = inflator.Inflate (memberType); + return fs; + } + + public FieldSpec Mutate (TypeParameterMutator mutator) + { + var decl = DeclaringType; + if (DeclaringType.IsGenericOrParentIsGeneric) + decl = mutator.Mutate (decl); + + if (decl == DeclaringType) + return this; + + var fs = (FieldSpec) MemberwiseClone (); + fs.declaringType = decl; + fs.state |= StateFlags.PendingMetaInflate; + + // Gets back FieldInfo in case of metaInfo was inflated + fs.metaInfo = MemberCache.GetMember (TypeParameterMutator.GetMemberDeclaringType (DeclaringType), this).metaInfo; + return fs; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + return memberType.ResolveMissingDependencies (this); + } + } + + /// + /// Fixed buffer implementation + /// + public class FixedField : FieldBase + { + public const string FixedElementName = "FixedElementField"; + static int GlobalCounter; + + TypeBuilder fixed_buffer_type; + + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.UNSAFE; + + public FixedField (TypeDefinition parent, FullNamedExpression type, Modifiers mod, MemberName name, Attributes attrs) + : base (parent, type, mod, AllowedModifiers, name, attrs) + { + } + + #region Properties + + // + // Explicit struct layout set by parent + // + public CharSet? CharSet { + get; set; + } + + #endregion + + public override Constant ConvertInitializer (ResolveContext rc, Constant expr) + { + return expr.ImplicitConversionRequired (rc, rc.BuiltinTypes.Int); + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!BuiltinTypeSpec.IsPrimitiveType (MemberType)) { + Report.Error (1663, Location, + "`{0}': Fixed size buffers type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double", + GetSignatureForError ()); + } else if (declarators != null) { + foreach (var d in declarators) { + var f = new FixedField (Parent, d.GetFieldTypeExpression (this), ModFlags, new MemberName (d.Name.Value, d.Name.Location), OptAttributes); + f.initializer = d.Initializer; + ((ConstInitializer) f.initializer).Name = d.Name.Value; + f.Define (); + Parent.PartialContainer.Members.Add (f); + } + } + + // Create nested fixed buffer container + string name = String.Format ("<{0}>__FixedBuffer{1}", Name, GlobalCounter++); + fixed_buffer_type = Parent.TypeBuilder.DefineNestedType (name, + TypeAttributes.NestedPublic | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, + Compiler.BuiltinTypes.ValueType.GetMetaInfo ()); + + var ffield = fixed_buffer_type.DefineField (FixedElementName, MemberType.GetMetaInfo (), FieldAttributes.Public); + + FieldBuilder = Parent.TypeBuilder.DefineField (Name, fixed_buffer_type, ModifiersExtensions.FieldAttr (ModFlags)); + + var element_spec = new FieldSpec (null, this, MemberType, ffield, ModFlags); + spec = new FixedFieldSpec (Module, Parent.Definition, this, FieldBuilder, element_spec, ModFlags); + + Parent.MemberCache.AddMember (spec); + return true; + } + + protected override void DoMemberTypeIndependentChecks () + { + base.DoMemberTypeIndependentChecks (); + + if (!IsUnsafe) + Expression.UnsafeError (Report, Location); + + if (Parent.PartialContainer.Kind != MemberKind.Struct) { + Report.Error (1642, Location, "`{0}': Fixed size buffer fields may only be members of structs", + GetSignatureForError ()); + } + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void Emit() + { + ResolveContext rc = new ResolveContext (this); + IntConstant buffer_size_const = initializer.Resolve (rc) as IntConstant; + if (buffer_size_const == null) + return; + + int buffer_size = buffer_size_const.Value; + + if (buffer_size <= 0) { + Report.Error (1665, Location, "`{0}': Fixed size buffers must have a length greater than zero", GetSignatureForError ()); + return; + } + + EmitFieldSize (buffer_size); + +#if STATIC + if (Module.HasDefaultCharSet) + fixed_buffer_type.__SetAttributes (fixed_buffer_type.Attributes | Module.DefaultCharSetType); +#endif + + Module.PredefinedAttributes.UnsafeValueType.EmitAttribute (fixed_buffer_type); + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (fixed_buffer_type); + fixed_buffer_type.CreateType (); + + base.Emit (); + } + + void EmitFieldSize (int buffer_size) + { + int type_size = BuiltinTypeSpec.GetSize (MemberType); + + if (buffer_size > int.MaxValue / type_size) { + Report.Error (1664, Location, "Fixed size buffer `{0}' of length `{1}' and type `{2}' exceeded 2^31 limit", + GetSignatureForError (), buffer_size.ToString (), MemberType.GetSignatureForError ()); + return; + } + + AttributeEncoder encoder; + + var ctor = Module.PredefinedMembers.StructLayoutAttributeCtor.Resolve (Location); + if (ctor == null) + return; + + var field_size = Module.PredefinedMembers.StructLayoutSize.Resolve (Location); + var field_charset = Module.PredefinedMembers.StructLayoutCharSet.Resolve (Location); + if (field_size == null || field_charset == null) + return; + + var char_set = CharSet ?? Module.DefaultCharSet ?? 0; + + encoder = new AttributeEncoder (); + encoder.Encode ((short)LayoutKind.Sequential); + encoder.EncodeNamedArguments ( + new [] { field_size, field_charset }, + new Constant [] { + new IntConstant (Compiler.BuiltinTypes, buffer_size * type_size, Location), + new IntConstant (Compiler.BuiltinTypes, (int) char_set, Location) + } + ); + + fixed_buffer_type.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + + // + // Don't emit FixedBufferAttribute attribute for private types + // + if ((ModFlags & Modifiers.PRIVATE) != 0) + return; + + ctor = Module.PredefinedMembers.FixedBufferAttributeCtor.Resolve (Location); + if (ctor == null) + return; + + encoder = new AttributeEncoder (); + encoder.EncodeTypeName (MemberType); + encoder.Encode (buffer_size); + encoder.EncodeEmptyNamedArguments (); + + FieldBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + class FixedFieldSpec : FieldSpec + { + readonly FieldSpec element; + + public FixedFieldSpec (ModuleContainer module, TypeSpec declaringType, IMemberDefinition definition, FieldInfo info, FieldSpec element, Modifiers modifiers) + : base (declaringType, definition, PointerContainer.MakeType (module, element.MemberType), info, modifiers) + { + this.element = element; + + // It's never CLS-Compliant + state &= ~StateFlags.CLSCompliant_Undetected; + } + + public FieldSpec Element { + get { + return element; + } + } + + public TypeSpec ElementType { + get { + return element.MemberType; + } + } + } + + // + // The Field class is used to represents class/struct fields during parsing. + // + public class Field : FieldBase { + // + // Modifiers allowed in a class declaration + // + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.VOLATILE | + Modifiers.UNSAFE | + Modifiers.READONLY; + + public Field (TypeDefinition parent, FullNamedExpression type, Modifiers mod, MemberName name, Attributes attrs) + : base (parent, type, mod, AllowedModifiers, name, attrs) + { + } + + bool CanBeVolatile () + { + switch (MemberType.BuiltinType) { + case BuiltinTypeSpec.Type.Bool: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.UIntPtr: + case BuiltinTypeSpec.Type.IntPtr: + return true; + } + + if (TypeSpec.IsReferenceType (MemberType)) + return true; + + if (MemberType.IsEnum) + return true; + + return false; + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + MetaType[] required_modifier = null; + if ((ModFlags & Modifiers.VOLATILE) != 0) { + var mod = Module.PredefinedTypes.IsVolatile.Resolve (); + if (mod != null) + required_modifier = new MetaType[] { mod.GetMetaInfo () }; + } + + FieldBuilder = Parent.TypeBuilder.DefineField ( + Name, member_type.GetMetaInfo (), required_modifier, null, ModifiersExtensions.FieldAttr (ModFlags)); + + spec = new FieldSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags); + + // + // Don't cache inaccessible fields except for struct where we + // need them for definitive assignment checks + // + if ((ModFlags & Modifiers.BACKING_FIELD) == 0 || Parent.Kind == MemberKind.Struct) { + Parent.MemberCache.AddMember (spec); + } + + if (initializer != null) { + Parent.RegisterFieldForInitialization (this, new FieldInitializer (this, initializer, TypeExpression.Location)); + } + + if (declarators != null) { + foreach (var d in declarators) { + var f = new Field (Parent, d.GetFieldTypeExpression (this), ModFlags, new MemberName (d.Name.Value, d.Name.Location), OptAttributes); + if (d.Initializer != null) + f.initializer = d.Initializer; + + f.Define (); + Parent.PartialContainer.Members.Add (f); + } + } + + return true; + } + + protected override void DoMemberTypeDependentChecks () + { + if ((ModFlags & Modifiers.BACKING_FIELD) != 0) + return; + + base.DoMemberTypeDependentChecks (); + + if ((ModFlags & Modifiers.VOLATILE) != 0) { + if (!CanBeVolatile ()) { + Report.Error (677, Location, "`{0}': A volatile field cannot be of the type `{1}'", + GetSignatureForError (), MemberType.GetSignatureForError ()); + } + + if ((ModFlags & Modifiers.READONLY) != 0) { + Report.Error (678, Location, "`{0}': A field cannot be both volatile and readonly", + GetSignatureForError ()); + } + } + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if ((ModFlags & Modifiers.VOLATILE) != 0) { + Report.Warning (3026, 1, Location, "CLS-compliant field `{0}' cannot be volatile", GetSignatureForError ()); + } + + return true; + } + } + + class PrimaryConstructorField : Field + { + // + // Proxy resolved parameter type expression to avoid type double resolve + // and problems with correct resolve context on partial classes + // + sealed class TypeExpressionFromParameter : TypeExpr + { + Parameter parameter; + + public TypeExpressionFromParameter (Parameter parameter) + { + this.parameter = parameter; + eclass = ExprClass.Type; + loc = parameter.Location; + } + + public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments) + { + return parameter.Type; + } + } + + public PrimaryConstructorField (TypeDefinition parent, Parameter parameter) + : base (parent, new TypeExpressionFromParameter (parameter), Modifiers.PRIVATE, new MemberName (parameter.Name, parameter.Location), null) + { + caching_flags |= Flags.IsUsed | Flags.IsAssigned; + } + + public override string GetSignatureForError () + { + return MemberName.Name; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/flowanalysis.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/flowanalysis.cs new file mode 100644 index 000000000..dbb74a751 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/flowanalysis.cs @@ -0,0 +1,685 @@ +// +// flowanalyis.cs: The control flow analysis code +// +// Authors: +// Martin Baulig (martin@ximian.com) +// Raja R Harinath (rharinath@novell.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin, Inc. +// + +using System; +using System.Text; +using System.Collections.Generic; + +namespace Mono.CSharp +{ + // + // This is used by the flow analysis code to keep track of the type of local variables. + // + // The flow code uses a BitVector to keep track of whether a variable has been assigned + // or not. This is easy for fundamental types (int, char etc.) or reference types since + // you can only assign the whole variable as such. + // + // For structs, we also need to keep track of all its fields. To do this, we allocate one + // bit for the struct itself (it's used if you assign/access the whole struct) followed by + // one bit for each of its fields. + // + // This class computes this `layout' for each type. + // + public class TypeInfo + { + // + // Total number of bits a variable of this type consumes in the flow vector. + // + public readonly int TotalLength; + + // + // Number of bits the simple fields of a variable of this type consume + // in the flow vector. + // + public readonly int Length; + + // + // This is only used by sub-structs. + // + public readonly int Offset; + + // + // If this is a struct. + // + public readonly bool IsStruct; + + // + // If this is a struct, all fields which are structs theirselves. + // + public TypeInfo[] SubStructInfo; + + readonly StructInfo struct_info; + private static Dictionary type_hash; + + static readonly TypeInfo simple_type = new TypeInfo (1); + + static TypeInfo () + { + Reset (); + } + + public static void Reset () + { + type_hash = new Dictionary (); + StructInfo.field_type_hash = new Dictionary (); + } + + TypeInfo (int totalLength) + { + this.TotalLength = totalLength; + } + + TypeInfo (StructInfo struct_info, int offset) + { + this.struct_info = struct_info; + this.Offset = offset; + this.Length = struct_info.Length; + this.TotalLength = struct_info.TotalLength; + this.SubStructInfo = struct_info.StructFields; + this.IsStruct = true; + } + + public int GetFieldIndex (string name) + { + if (struct_info == null) + return 0; + + return struct_info [name]; + } + + public TypeInfo GetStructField (string name) + { + if (struct_info == null) + return null; + + return struct_info.GetStructField (name); + } + + public static TypeInfo GetTypeInfo (TypeSpec type) + { + if (!type.IsStruct) + return simple_type; + + TypeInfo info; + if (type_hash.TryGetValue (type, out info)) + return info; + + var struct_info = StructInfo.GetStructInfo (type); + if (struct_info != null) { + info = new TypeInfo (struct_info, 0); + } else { + info = simple_type; + } + + type_hash.Add (type, info); + return info; + } + + // + // A struct's constructor must always assign all fields. + // This method checks whether it actually does so. + // + public bool IsFullyInitialized (FlowAnalysisContext fc, VariableInfo vi, Location loc) + { + if (struct_info == null) + return true; + + bool ok = true; + for (int i = 0; i < struct_info.Count; i++) { + var field = struct_info.Fields[i]; + + if (!fc.IsStructFieldDefinitelyAssigned (vi, field.Name)) { + var bf = field.MemberDefinition as Property.BackingField; + if (bf != null) { + if (bf.Initializer != null) + continue; + + fc.Report.Error (843, loc, + "An automatically implemented property `{0}' must be fully assigned before control leaves the constructor. Consider calling the default struct contructor from a constructor initializer", + field.GetSignatureForError ()); + + ok = false; + continue; + } + + fc.Report.Error (171, loc, + "Field `{0}' must be fully assigned before control leaves the constructor", + field.GetSignatureForError ()); + ok = false; + } + } + + return ok; + } + + public override string ToString () + { + return String.Format ("TypeInfo ({0}:{1}:{2})", + Offset, Length, TotalLength); + } + + class StructInfo + { + readonly List fields; + public readonly TypeInfo[] StructFields; + public readonly int Length; + public readonly int TotalLength; + + public static Dictionary field_type_hash; + private Dictionary struct_field_hash; + private Dictionary field_hash; + + bool InTransit; + + // + // We only need one instance per type + // + StructInfo (TypeSpec type) + { + field_type_hash.Add (type, this); + + fields = MemberCache.GetAllFieldsForDefiniteAssignment (type); + + struct_field_hash = new Dictionary (); + field_hash = new Dictionary (fields.Count); + + StructFields = new TypeInfo[fields.Count]; + StructInfo[] sinfo = new StructInfo[fields.Count]; + + InTransit = true; + + for (int i = 0; i < fields.Count; i++) { + var field = fields [i]; + + if (field.MemberType.IsStruct) + sinfo [i] = GetStructInfo (field.MemberType); + + if (sinfo [i] == null) + field_hash.Add (field.Name, ++Length); + else if (sinfo [i].InTransit) { + sinfo [i] = null; + return; + } + } + + InTransit = false; + + TotalLength = Length + 1; + for (int i = 0; i < fields.Count; i++) { + var field = fields [i]; + + if (sinfo [i] == null) + continue; + + field_hash.Add (field.Name, TotalLength); + + StructFields [i] = new TypeInfo (sinfo [i], TotalLength); + struct_field_hash.Add (field.Name, StructFields [i]); + TotalLength += sinfo [i].TotalLength; + } + } + + public int Count { + get { + return fields.Count; + } + } + + public List Fields { + get { + return fields; + } + } + + public int this [string name] { + get { + int val; + if (!field_hash.TryGetValue (name, out val)) + return 0; + + return val; + } + } + + public TypeInfo GetStructField (string name) + { + TypeInfo ti; + if (struct_field_hash.TryGetValue (name, out ti)) + return ti; + + return null; + } + + public static StructInfo GetStructInfo (TypeSpec type) + { + if (type.BuiltinType > 0) + return null; + + StructInfo info; + if (field_type_hash.TryGetValue (type, out info)) + return info; + + return new StructInfo (type); + } + } + } + + // + // This is used by definite assignment analysis code to store information about a local variable + // or parameter. Depending on the variable's type, we need to allocate one or more elements + // in the BitVector - if it's a fundamental or reference type, we just need to know whether + // it has been assigned or not, but for structs, we need this information for each of its fields. + // + public class VariableInfo + { + readonly string Name; + + readonly TypeInfo TypeInfo; + + // + // The bit offset of this variable in the flow vector. + // + readonly int Offset; + + // + // The number of bits this variable needs in the flow vector. + // The first bit always specifies whether the variable as such has been assigned while + // the remaining bits contain this information for each of a struct's fields. + // + readonly int Length; + + // + // If this is a parameter of local variable. + // + public bool IsParameter; + + VariableInfo[] sub_info; + + VariableInfo (string name, TypeSpec type, int offset) + { + this.Name = name; + this.Offset = offset; + this.TypeInfo = TypeInfo.GetTypeInfo (type); + + Length = TypeInfo.TotalLength; + + Initialize (); + } + + VariableInfo (VariableInfo parent, TypeInfo type) + { + this.Name = parent.Name; + this.TypeInfo = type; + this.Offset = parent.Offset + type.Offset; + this.Length = type.TotalLength; + + this.IsParameter = parent.IsParameter; + + Initialize (); + } + + void Initialize () + { + TypeInfo[] sub_fields = TypeInfo.SubStructInfo; + if (sub_fields != null) { + sub_info = new VariableInfo [sub_fields.Length]; + for (int i = 0; i < sub_fields.Length; i++) { + if (sub_fields [i] != null) + sub_info [i] = new VariableInfo (this, sub_fields [i]); + } + } else + sub_info = new VariableInfo [0]; + } + + public static VariableInfo Create (BlockContext bc, LocalVariable variable) + { + var info = new VariableInfo (variable.Name, variable.Type, bc.AssignmentInfoOffset); + bc.AssignmentInfoOffset += info.Length; + return info; + } + + public static VariableInfo Create (BlockContext bc, Parameter parameter) + { + var info = new VariableInfo (parameter.Name, parameter.Type, bc.AssignmentInfoOffset) { + IsParameter = true + }; + + bc.AssignmentInfoOffset += info.Length; + return info; + } + + public bool IsAssigned (DefiniteAssignmentBitSet vector) + { + if (vector == null) + return true; + + if (vector [Offset]) + return true; + + // Unless this is a struct + if (!TypeInfo.IsStruct) + return false; + + // + // Following case cannot be handled fully by SetStructFieldAssigned + // because we may encounter following case + // + // struct A { B b } + // struct B { int value; } + // + // setting a.b.value is propagated only to B's vector and not upwards to possible parents + // + // + // Each field must be assigned + // + for (int i = Offset + 1; i <= TypeInfo.Length + Offset; i++) { + if (!vector[i]) + return false; + } + + // Ok, now check all fields which are structs. + for (int i = 0; i < sub_info.Length; i++) { + VariableInfo sinfo = sub_info[i]; + if (sinfo == null) + continue; + + if (!sinfo.IsAssigned (vector)) + return false; + } + + vector.Set (Offset); + return true; + } + + public bool IsEverAssigned { get; set; } + + public bool IsFullyInitialized (FlowAnalysisContext fc, Location loc) + { + return TypeInfo.IsFullyInitialized (fc, this, loc); + } + + public bool IsStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name) + { + int field_idx = TypeInfo.GetFieldIndex (field_name); + + if (field_idx == 0) + return true; + + return vector [Offset + field_idx]; + } + + public void SetAssigned (DefiniteAssignmentBitSet vector, bool generatedAssignment) + { + if (Length == 1) + vector.Set (Offset); + else + vector.Set (Offset, Length); + + if (!generatedAssignment) + IsEverAssigned = true; + } + + public void SetStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name) + { + if (vector [Offset]) + return; + + int field_idx = TypeInfo.GetFieldIndex (field_name); + + if (field_idx == 0) + return; + + var complex_field = TypeInfo.GetStructField (field_name); + if (complex_field != null) { + vector.Set (Offset + complex_field.Offset, complex_field.TotalLength); + } else { + vector.Set (Offset + field_idx); + } + + IsEverAssigned = true; + + // + // Each field must be assigned before setting master bit + // + for (int i = Offset + 1; i < TypeInfo.TotalLength + Offset; i++) { + if (!vector[i]) + return; + } + + // + // Set master struct flag to assigned when all tested struct + // fields have been assigned + // + vector.Set (Offset); + } + + public VariableInfo GetStructFieldInfo (string fieldName) + { + TypeInfo type = TypeInfo.GetStructField (fieldName); + + if (type == null) + return null; + + return new VariableInfo (this, type); + } + + public override string ToString () + { + return String.Format ("Name={0} Offset={1} Length={2} {3})", Name, Offset, Length, TypeInfo); + } + } + + public struct Reachability + { + readonly bool unreachable; + + Reachability (bool unreachable) + { + this.unreachable = unreachable; + } + + public bool IsUnreachable { + get { + return unreachable; + } + } + + public static Reachability CreateUnreachable () + { + return new Reachability (true); + } + + public static Reachability operator & (Reachability a, Reachability b) + { + return new Reachability (a.unreachable && b.unreachable); + } + + public static Reachability operator | (Reachability a, Reachability b) + { + return new Reachability (a.unreachable | b.unreachable); + } + } + + // + // Special version of bit array. Many operations can be simplified because + // we are always dealing with arrays of same sizes + // + public class DefiniteAssignmentBitSet + { + const uint copy_on_write_flag = 1u << 31; + + uint bits; + + // Used when bits overflows + int[] large_bits; + + public static readonly DefiniteAssignmentBitSet Empty = new DefiniteAssignmentBitSet (0); + + public DefiniteAssignmentBitSet (int length) + { + if (length > 31) + large_bits = new int[(length + 31) / 32]; + } + + public DefiniteAssignmentBitSet (DefiniteAssignmentBitSet source) + { + if (source.large_bits != null) { + large_bits = source.large_bits; + bits = source.bits | copy_on_write_flag; + } else { + bits = source.bits & ~copy_on_write_flag; + } + } + + public static DefiniteAssignmentBitSet operator & (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b) + { + if (AreEqual (a, b)) + return a; + + DefiniteAssignmentBitSet res; + if (a.large_bits == null) { + res = new DefiniteAssignmentBitSet (a); + res.bits &= (b.bits & ~copy_on_write_flag); + return res; + } + + res = new DefiniteAssignmentBitSet (a); + res.Clone (); + var dest = res.large_bits; + var src = b.large_bits; + for (int i = 0; i < dest.Length; ++i) { + dest[i] &= src[i]; + } + + return res; + } + + public static DefiniteAssignmentBitSet operator | (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b) + { + if (AreEqual (a, b)) + return a; + + DefiniteAssignmentBitSet res; + if (a.large_bits == null) { + res = new DefiniteAssignmentBitSet (a); + res.bits |= b.bits; + res.bits &= ~copy_on_write_flag; + return res; + } + + res = new DefiniteAssignmentBitSet (a); + res.Clone (); + var dest = res.large_bits; + var src = b.large_bits; + + for (int i = 0; i < dest.Length; ++i) { + dest[i] |= src[i]; + } + + return res; + } + + public static DefiniteAssignmentBitSet And (List das) + { + if (das.Count == 0) + throw new ArgumentException ("Empty das"); + + DefiniteAssignmentBitSet res = das[0]; + for (int i = 1; i < das.Count; ++i) { + res &= das[i]; + } + + return res; + } + + bool CopyOnWrite { + get { + return (bits & copy_on_write_flag) != 0; + } + } + + int Length { + get { + return large_bits == null ? 31 : large_bits.Length * 32; + } + } + + public void Set (int index) + { + if (CopyOnWrite && !this[index]) + Clone (); + + SetBit (index); + } + + public void Set (int index, int length) + { + for (int i = 0; i < length; ++i) { + if (CopyOnWrite && !this[index + i]) + Clone (); + + SetBit (index + i); + } + } + + public bool this[int index] { + get { + return GetBit (index); + } + } + + public override string ToString () + { + var length = Length; + StringBuilder sb = new StringBuilder (length); + for (int i = 0; i < length; ++i) { + sb.Append (this[i] ? '1' : '0'); + } + + return sb.ToString (); + } + + void Clone () + { + large_bits = (int[]) large_bits.Clone (); + } + + bool GetBit (int index) + { + return large_bits == null ? + (bits & (1 << index)) != 0 : + (large_bits[index >> 5] & (1 << (index & 31))) != 0; + } + + void SetBit (int index) + { + if (large_bits == null) + bits = (uint) ((int) bits | (1 << index)); + else + large_bits[index >> 5] |= (1 << (index & 31)); + } + + static bool AreEqual (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b) + { + if (a.large_bits == null) + return (a.bits & ~copy_on_write_flag) == (b.bits & ~copy_on_write_flag); + + for (int i = 0; i < a.large_bits.Length; ++i) { + if (a.large_bits[i] != b.large_bits[i]) + return false; + } + + return true; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/generic.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/generic.cs new file mode 100644 index 000000000..f6d594fd0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/generic.cs @@ -0,0 +1,3660 @@ +// +// generic.cs: Generics support +// +// Authors: Martin Baulig (martin@ximian.com) +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + public class VarianceDecl + { + public VarianceDecl (Variance variance, Location loc) + { + this.Variance = variance; + this.Location = loc; + } + + public Variance Variance { get; private set; } + public Location Location { get; private set; } + + public static Variance CheckTypeVariance (TypeSpec t, Variance expected, IMemberContext member) + { + var tp = t as TypeParameterSpec; + if (tp != null) { + var v = tp.Variance; + if (expected == Variance.None && v != expected || + expected == Variance.Covariant && v == Variance.Contravariant || + expected == Variance.Contravariant && v == Variance.Covariant) { + ((TypeParameter) tp.MemberDefinition).ErrorInvalidVariance (member, expected); + } + + return expected; + } + + if (t.TypeArguments.Length > 0) { + var targs_definition = t.MemberDefinition.TypeParameters; + TypeSpec[] targs = TypeManager.GetTypeArguments (t); + for (int i = 0; i < targs.Length; ++i) { + var v = targs_definition[i].Variance; + CheckTypeVariance (targs[i], (Variance) ((int) v * (int) expected), member); + } + + return expected; + } + + var ac = t as ArrayContainer; + if (ac != null) + return CheckTypeVariance (ac.Element, expected, member); + + return Variance.None; + } + } + + public enum Variance + { + // + // Don't add or modify internal values, they are used as -/+ calculation signs + // + None = 0, + Covariant = 1, + Contravariant = -1 + } + + [Flags] + public enum SpecialConstraint + { + None = 0, + Constructor = 1 << 2, + Class = 1 << 3, + Struct = 1 << 4 + } + + public class SpecialContraintExpr : FullNamedExpression + { + public SpecialContraintExpr (SpecialConstraint constraint, Location loc) + { + this.loc = loc; + this.Constraint = constraint; + } + + public SpecialConstraint Constraint { get; private set; } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotImplementedException (); + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + throw new NotImplementedException (); + } + } + + // + // A set of parsed constraints for a type parameter + // + public class Constraints + { + readonly SimpleMemberName tparam; + readonly List constraints; + readonly Location loc; + bool resolved; + bool resolving; + + public IEnumerable ConstraintExpressions { + get { + return constraints; + } + } + + public Constraints (SimpleMemberName tparam, List constraints, Location loc) + { + this.tparam = tparam; + this.constraints = constraints; + this.loc = loc; + } + + #region Properties + + public List TypeExpressions { + get { + return constraints; + } + } + + public Location Location { + get { + return loc; + } + } + + public SimpleMemberName TypeParameter { + get { + return tparam; + } + } + + #endregion + + public static bool CheckConflictingInheritedConstraint (TypeParameterSpec spec, TypeSpec bb, IMemberContext context, Location loc) + { + if (spec.HasSpecialClass && bb.IsStruct) { + context.Module.Compiler.Report.Error (455, loc, + "Type parameter `{0}' inherits conflicting constraints `{1}' and `{2}'", + spec.Name, "class", bb.GetSignatureForError ()); + + return false; + } + + return CheckConflictingInheritedConstraint (spec, spec.BaseType, bb, context, loc); + } + + static bool CheckConflictingInheritedConstraint (TypeParameterSpec spec, TypeSpec ba, TypeSpec bb, IMemberContext context, Location loc) + { + if (ba == bb) + return true; + + if (TypeSpec.IsBaseClass (ba, bb, false) || TypeSpec.IsBaseClass (bb, ba, false)) + return true; + + Error_ConflictingConstraints (context, spec, ba, bb, loc); + return false; + } + + public static void Error_ConflictingConstraints (IMemberContext context, TypeParameterSpec tp, TypeSpec ba, TypeSpec bb, Location loc) + { + context.Module.Compiler.Report.Error (455, loc, + "Type parameter `{0}' inherits conflicting constraints `{1}' and `{2}'", + tp.Name, ba.GetSignatureForError (), bb.GetSignatureForError ()); + } + + public void CheckGenericConstraints (IMemberContext context, bool obsoleteCheck) + { + foreach (var c in constraints) { + if (c == null) + continue; + + var t = c.Type; + if (t == null) + continue; + + if (obsoleteCheck) { + ObsoleteAttribute obsolete_attr = t.GetAttributeObsolete (); + if (obsolete_attr != null) + AttributeTester.Report_ObsoleteMessage (obsolete_attr, t.GetSignatureForError (), c.Location, context.Module.Compiler.Report); + } + + ConstraintChecker.Check (context, t, c.Location); + } + } + + // + // Resolve the constraints types with only possible early checks, return + // value `false' is reserved for recursive failure + // + public bool Resolve (IMemberContext context, TypeParameter tp) + { + if (resolved) + return true; + + if (resolving) + return false; + + resolving = true; + var spec = tp.Type; + List tparam_types = null; + bool iface_found = false; + + spec.BaseType = context.Module.Compiler.BuiltinTypes.Object; + + for (int i = 0; i < constraints.Count; ++i) { + var constraint = constraints[i]; + + if (constraint is SpecialContraintExpr) { + spec.SpecialConstraint |= ((SpecialContraintExpr) constraint).Constraint; + if (spec.HasSpecialStruct) + spec.BaseType = context.Module.Compiler.BuiltinTypes.ValueType; + + // Set to null as it does not have a type + constraints[i] = null; + continue; + } + + var type = constraint.ResolveAsType (context); + if (type == null) + continue; + + if (type.Arity > 0 && ((InflatedTypeSpec) type).HasDynamicArgument ()) { + context.Module.Compiler.Report.Error (1968, constraint.Location, + "A constraint cannot be the dynamic type `{0}'", type.GetSignatureForError ()); + continue; + } + + if (!context.CurrentMemberDefinition.IsAccessibleAs (type)) { + context.Module.Compiler.Report.SymbolRelatedToPreviousError (type); + context.Module.Compiler.Report.Error (703, loc, + "Inconsistent accessibility: constraint type `{0}' is less accessible than `{1}'", + type.GetSignatureForError (), context.GetSignatureForError ()); + } + + if (type.IsInterface) { + if (!spec.AddInterface (type)) { + context.Module.Compiler.Report.Error (405, constraint.Location, + "Duplicate constraint `{0}' for type parameter `{1}'", type.GetSignatureForError (), tparam.Value); + } + + iface_found = true; + continue; + } + + var constraint_tp = type as TypeParameterSpec; + if (constraint_tp != null) { + if (tparam_types == null) { + tparam_types = new List (2); + } else if (tparam_types.Contains (constraint_tp)) { + context.Module.Compiler.Report.Error (405, constraint.Location, + "Duplicate constraint `{0}' for type parameter `{1}'", type.GetSignatureForError (), tparam.Value); + continue; + } + + // + // Checks whether each generic method parameter constraint type + // is valid with respect to T + // + if (tp.IsMethodTypeParameter) { + VarianceDecl.CheckTypeVariance (type, Variance.Contravariant, context); + } + + var tp_def = constraint_tp.MemberDefinition as TypeParameter; + if (tp_def != null && !tp_def.ResolveConstraints (context)) { + context.Module.Compiler.Report.Error (454, constraint.Location, + "Circular constraint dependency involving `{0}' and `{1}'", + constraint_tp.GetSignatureForError (), tp.GetSignatureForError ()); + continue; + } + + // + // Checks whether there are no conflicts between type parameter constraints + // + // class Foo + // where T : A + // where U : B, T + // + // A and B are not convertible and only 1 class constraint is allowed + // + if (constraint_tp.HasTypeConstraint) { + if (spec.HasTypeConstraint || spec.HasSpecialStruct) { + if (!CheckConflictingInheritedConstraint (spec, constraint_tp.BaseType, context, constraint.Location)) + continue; + } else { + for (int ii = 0; ii < tparam_types.Count; ++ii) { + if (!tparam_types[ii].HasTypeConstraint) + continue; + + if (!CheckConflictingInheritedConstraint (spec, tparam_types[ii].BaseType, constraint_tp.BaseType, context, constraint.Location)) + break; + } + } + } + + if (constraint_tp.TypeArguments != null) { + var eb = constraint_tp.GetEffectiveBase (); + if (eb != null && !CheckConflictingInheritedConstraint (spec, eb, spec.BaseType, context, constraint.Location)) + break; + } + + if (constraint_tp.HasSpecialStruct) { + context.Module.Compiler.Report.Error (456, constraint.Location, + "Type parameter `{0}' has the `struct' constraint, so it cannot be used as a constraint for `{1}'", + constraint_tp.GetSignatureForError (), tp.GetSignatureForError ()); + continue; + } + + tparam_types.Add (constraint_tp); + continue; + } + + if (iface_found || spec.HasTypeConstraint) { + context.Module.Compiler.Report.Error (406, constraint.Location, + "The class type constraint `{0}' must be listed before any other constraints. Consider moving type constraint to the beginning of the constraint list", + type.GetSignatureForError ()); + } + + if (spec.HasSpecialStruct || spec.HasSpecialClass) { + context.Module.Compiler.Report.Error (450, constraint.Location, + "`{0}': cannot specify both a constraint class and the `class' or `struct' constraint", + type.GetSignatureForError ()); + } + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Array: + case BuiltinTypeSpec.Type.Delegate: + case BuiltinTypeSpec.Type.MulticastDelegate: + case BuiltinTypeSpec.Type.Enum: + case BuiltinTypeSpec.Type.ValueType: + case BuiltinTypeSpec.Type.Object: + context.Module.Compiler.Report.Error (702, constraint.Location, + "A constraint cannot be special class `{0}'", type.GetSignatureForError ()); + continue; + case BuiltinTypeSpec.Type.Dynamic: + context.Module.Compiler.Report.Error (1967, constraint.Location, + "A constraint cannot be the dynamic type"); + continue; + } + + if (type.IsSealed || !type.IsClass) { + context.Module.Compiler.Report.Error (701, loc, + "`{0}' is not a valid constraint. A constraint must be an interface, a non-sealed class or a type parameter", + type.GetSignatureForError ()); + continue; + } + + if (type.IsStatic) { + context.Module.Compiler.Report.Error (717, constraint.Location, + "`{0}' is not a valid constraint. Static classes cannot be used as constraints", + type.GetSignatureForError ()); + } + + spec.BaseType = type; + } + + if (tparam_types != null) + spec.TypeArguments = tparam_types.ToArray (); + + resolving = false; + resolved = true; + return true; + } + + public void VerifyClsCompliance (Report report) + { + foreach (var c in constraints) + { + if (c == null) + continue; + + if (!c.Type.IsCLSCompliant ()) { + report.SymbolRelatedToPreviousError (c.Type); + report.Warning (3024, 1, loc, "Constraint type `{0}' is not CLS-compliant", + c.Type.GetSignatureForError ()); + } + } + } + } + + // + // A type parameter for a generic type or generic method definition + // + public class TypeParameter : MemberCore, ITypeDefinition + { + static readonly string[] attribute_target = { "type parameter" }; + + Constraints constraints; + GenericTypeParameterBuilder builder; + readonly TypeParameterSpec spec; + + public TypeParameter (int index, MemberName name, Constraints constraints, Attributes attrs, Variance Variance) + : base (null, name, attrs) + { + this.constraints = constraints; + this.spec = new TypeParameterSpec (null, index, this, SpecialConstraint.None, Variance, null); + } + + // + // Used by parser + // + public TypeParameter (MemberName name, Attributes attrs, VarianceDecl variance) + : base (null, name, attrs) + { + var var = variance == null ? Variance.None : variance.Variance; + this.spec = new TypeParameterSpec (null, -1, this, SpecialConstraint.None, var, null); + this.VarianceDecl = variance; + } + + public TypeParameter (TypeParameterSpec spec, TypeSpec parentSpec, MemberName name, Attributes attrs) + : base (null, name, attrs) + { + this.spec = new TypeParameterSpec (parentSpec, spec.DeclaredPosition, spec.MemberDefinition, spec.SpecialConstraint, spec.Variance, null) { + BaseType = spec.BaseType, + InterfacesDefined = spec.InterfacesDefined, + TypeArguments = spec.TypeArguments + }; + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.GenericParameter; + } + } + + public Constraints Constraints { + get { + return constraints; + } + set { + constraints = value; + } + } + + public IAssemblyDefinition DeclaringAssembly { + get { + return Module.DeclaringAssembly; + } + } + + public override string DocCommentHeader { + get { + throw new InvalidOperationException ( + "Unexpected attempt to get doc comment from " + this.GetType ()); + } + } + + bool ITypeDefinition.IsComImport { + get { + return false; + } + } + + bool ITypeDefinition.IsPartial { + get { + return false; + } + } + + public bool IsMethodTypeParameter { + get { + return spec.IsMethodOwned; + } + } + + bool ITypeDefinition.IsTypeForwarder { + get { + return false; + } + } + + bool ITypeDefinition.IsCyclicTypeForwarder { + get { + return false; + } + } + + public string Name { + get { + return MemberName.Name; + } + } + + public string Namespace { + get { + return null; + } + } + + public TypeParameterSpec Type { + get { + return spec; + } + } + + public int TypeParametersCount { + get { + return 0; + } + } + + public TypeParameterSpec[] TypeParameters { + get { + return null; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_target; + } + } + + public Variance Variance { + get { + return spec.Variance; + } + } + + public VarianceDecl VarianceDecl { get; private set; } + + #endregion + + // + // This is called for each part of a partial generic type definition. + // + // If partial type parameters constraints are not null and we don't + // already have constraints they become our constraints. If we already + // have constraints, we must check that they're same. + // + public bool AddPartialConstraints (TypeDefinition part, TypeParameter tp) + { + if (builder == null) + throw new InvalidOperationException (); + + var new_constraints = tp.constraints; + if (new_constraints == null) + return true; + + // TODO: could create spec only + //tp.Define (null, -1, part.Definition); + tp.spec.DeclaringType = part.Definition; + if (!tp.ResolveConstraints (part)) + return false; + + if (constraints != null) + return spec.HasSameConstraintsDefinition (tp.Type); + + // Copy constraint from resolved part to partial container + spec.SpecialConstraint = tp.spec.SpecialConstraint; + spec.InterfacesDefined = tp.spec.InterfacesDefined; + spec.TypeArguments = tp.spec.TypeArguments; + spec.BaseType = tp.spec.BaseType; + + return true; + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public void CheckGenericConstraints (bool obsoleteCheck) + { + if (constraints != null) + constraints.CheckGenericConstraints (this, obsoleteCheck); + } + + public TypeParameter CreateHoistedCopy (TypeSpec declaringSpec) + { + return new TypeParameter (spec, declaringSpec, MemberName, null); + } + + public override bool Define () + { + return true; + } + + // + // This is the first method which is called during the resolving + // process; we're called immediately after creating the type parameters + // with SRE (by calling `DefineGenericParameters()' on the TypeBuilder / + // MethodBuilder). + // + public void Create (TypeSpec declaringType, TypeContainer parent) + { + if (builder != null) + throw new InternalErrorException (); + + // Needed to get compiler reference + this.Parent = parent; + spec.DeclaringType = declaringType; + } + + public void Define (GenericTypeParameterBuilder type) + { + this.builder = type; + spec.SetMetaInfo (type); + } + + public void Define (TypeParameter tp) + { + builder = tp.builder; + } + + public void EmitConstraints (GenericTypeParameterBuilder builder) + { + var attr = GenericParameterAttributes.None; + if (spec.Variance == Variance.Contravariant) + attr |= GenericParameterAttributes.Contravariant; + else if (spec.Variance == Variance.Covariant) + attr |= GenericParameterAttributes.Covariant; + + if (spec.HasSpecialClass) + attr |= GenericParameterAttributes.ReferenceTypeConstraint; + else if (spec.HasSpecialStruct) + attr |= GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint; + + if (spec.HasSpecialConstructor) + attr |= GenericParameterAttributes.DefaultConstructorConstraint; + + if (spec.BaseType.BuiltinType != BuiltinTypeSpec.Type.Object) + builder.SetBaseTypeConstraint (spec.BaseType.GetMetaInfo ()); + + if (spec.InterfacesDefined != null) + builder.SetInterfaceConstraints (spec.InterfacesDefined.Select (l => l.GetMetaInfo ()).ToArray ()); + + if (spec.TypeArguments != null) { + var meta_constraints = new List (spec.TypeArguments.Length); + foreach (var c in spec.TypeArguments) { + // + // Inflated type parameters can collide with special constraint types, don't + // emit any such type parameter. + // + if (c.BuiltinType == BuiltinTypeSpec.Type.Object || c.BuiltinType == BuiltinTypeSpec.Type.ValueType) + continue; + + meta_constraints.Add (c.GetMetaInfo ()); + } + + builder.SetInterfaceConstraints (meta_constraints.ToArray ()); + } + + builder.SetGenericParameterAttributes (attr); + } + + public override void Emit () + { + EmitConstraints (builder); + + if (OptAttributes != null) + OptAttributes.Emit (); + + base.Emit (); + } + + public void ErrorInvalidVariance (IMemberContext mc, Variance expected) + { + Report.SymbolRelatedToPreviousError (mc.CurrentMemberDefinition); + string input_variance = Variance == Variance.Contravariant ? "contravariant" : "covariant"; + string gtype_variance; + switch (expected) { + case Variance.Contravariant: gtype_variance = "contravariantly"; break; + case Variance.Covariant: gtype_variance = "covariantly"; break; + default: gtype_variance = "invariantly"; break; + } + + Delegate d = mc as Delegate; + string parameters = d != null ? d.Parameters.GetSignatureForError () : ""; + + Report.Error (1961, Location, + "The {2} type parameter `{0}' must be {3} valid on `{1}{4}'", + GetSignatureForError (), mc.GetSignatureForError (), input_variance, gtype_variance, parameters); + } + + public TypeSpec GetAttributeCoClass () + { + return null; + } + + public string GetAttributeDefaultMember () + { + throw new NotSupportedException (); + } + + public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) + { + throw new NotSupportedException (); + } + + public override string GetSignatureForDocumentation () + { + throw new NotImplementedException (); + } + + public override string GetSignatureForError () + { + return MemberName.Name; + } + + bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) + { + return spec.MemberDefinition.DeclaringAssembly == assembly; + } + + public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) + { + throw new NotSupportedException ("Not supported for compiled definition"); + } + + // + // Resolves all type parameter constraints + // + public bool ResolveConstraints (IMemberContext context) + { + if (constraints != null) + return constraints.Resolve (context, this); + + if (spec.BaseType == null) + spec.BaseType = context.Module.Compiler.BuiltinTypes.Object; + + return true; + } + + public override bool IsClsComplianceRequired () + { + return false; + } + + public new void VerifyClsCompliance () + { + if (constraints != null) + constraints.VerifyClsCompliance (Report); + } + + public void WarningParentNameConflict (TypeParameter conflict) + { + conflict.Report.SymbolRelatedToPreviousError (conflict.Location, null); + conflict.Report.Warning (693, 3, Location, + "Type parameter `{0}' has the same name as the type parameter from outer type `{1}'", + GetSignatureForError (), conflict.CurrentType.GetSignatureForError ()); + } + } + + [System.Diagnostics.DebuggerDisplay ("{DisplayDebugInfo()}")] + public class TypeParameterSpec : TypeSpec + { + public static readonly new TypeParameterSpec[] EmptyTypes = new TypeParameterSpec[0]; + + Variance variance; + SpecialConstraint spec; + int tp_pos; + TypeSpec[] targs; + TypeSpec[] ifaces_defined; + TypeSpec effective_base; + + // + // Creates type owned type parameter + // + public TypeParameterSpec (TypeSpec declaringType, int index, ITypeDefinition definition, SpecialConstraint spec, Variance variance, MetaType info) + : base (MemberKind.TypeParameter, declaringType, definition, info, Modifiers.PUBLIC) + { + this.variance = variance; + this.spec = spec; + state &= ~StateFlags.Obsolete_Undetected; + tp_pos = index; + } + + // + // Creates method owned type parameter + // + public TypeParameterSpec (int index, ITypeDefinition definition, SpecialConstraint spec, Variance variance, MetaType info) + : this (null, index, definition, spec, variance, info) + { + } + + #region Properties + + public int DeclaredPosition { + get { + return tp_pos; + } + set { + tp_pos = value; + } + } + + public bool HasSpecialConstructor { + get { + return (spec & SpecialConstraint.Constructor) != 0; + } + } + + public bool HasSpecialClass { + get { + return (spec & SpecialConstraint.Class) != 0; + } + } + + public bool HasSpecialStruct { + get { + return (spec & SpecialConstraint.Struct) != 0; + } + } + + public bool HasAnyTypeConstraint { + get { + return (spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0 || ifaces != null || targs != null || HasTypeConstraint; + } + } + + public bool HasTypeConstraint { + get { + var bt = BaseType.BuiltinType; + return bt != BuiltinTypeSpec.Type.Object && bt != BuiltinTypeSpec.Type.ValueType; + } + } + + public override IList Interfaces { + get { + if ((state & StateFlags.InterfacesExpanded) == 0) { + if (ifaces != null) { + if (ifaces_defined == null) + ifaces_defined = ifaces.ToArray (); + + for (int i = 0; i < ifaces_defined.Length; ++i ) { + var iface_type = ifaces_defined[i]; + var td = iface_type.MemberDefinition as TypeDefinition; + if (td != null) + td.DoExpandBaseInterfaces (); + + if (iface_type.Interfaces != null) { + for (int ii = 0; ii < iface_type.Interfaces.Count; ++ii) { + var ii_iface_type = iface_type.Interfaces [ii]; + AddInterface (ii_iface_type); + } + } + } + } else if (ifaces_defined == null) { + ifaces_defined = ifaces == null ? TypeSpec.EmptyTypes : ifaces.ToArray (); + } + + // + // Include all base type interfaces too, see ImportTypeBase for details + // + if (BaseType != null) { + var td = BaseType.MemberDefinition as TypeDefinition; + if (td != null) + td.DoExpandBaseInterfaces (); + + if (BaseType.Interfaces != null) { + foreach (var iface in BaseType.Interfaces) { + AddInterface (iface); + } + } + } + + state |= StateFlags.InterfacesExpanded; + } + + return ifaces; + } + } + + // + // Unexpanded interfaces list + // + public TypeSpec[] InterfacesDefined { + get { + if (ifaces_defined == null) { + ifaces_defined = ifaces == null ? TypeSpec.EmptyTypes : ifaces.ToArray (); + } + + return ifaces_defined.Length == 0 ? null : ifaces_defined; + } + set { + ifaces_defined = value; + if (value != null && value.Length != 0) + ifaces = new List (value); + } + } + + public bool IsConstrained { + get { + return spec != SpecialConstraint.None || ifaces != null || targs != null || HasTypeConstraint; + } + } + + // + // Returns whether the type parameter is known to be a reference type + // + public new bool IsReferenceType { + get { + if ((spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) + return (spec & SpecialConstraint.Class) != 0; + + // + // Full check is needed (see IsValueType for details) + // + if (HasTypeConstraint && TypeSpec.IsReferenceType (BaseType)) + return true; + + if (targs != null) { + foreach (var ta in targs) { + // + // Secondary special constraints are ignored (I am not sure why) + // + var tp = ta as TypeParameterSpec; + if (tp != null && (tp.spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) + continue; + + if (TypeSpec.IsReferenceType (ta)) + return true; + } + } + + return false; + } + } + + // + // Returns whether the type parameter is known to be a value type + // + public new bool IsValueType { + get { + // + // Even if structs/enums cannot be used directly as constraints + // they can apear as constraint type when inheriting base constraint + // which has dependant type parameter constraint which has been + // inflated using value type + // + // class A : B { override void Foo () {} } + // class B { virtual void Foo () where U : T {} } + // + if (HasSpecialStruct) + return true; + + if (targs != null) { + foreach (var ta in targs) { + if (TypeSpec.IsValueType (ta)) + return true; + } + } + + return false; + } + } + + public override string Name { + get { + return definition.Name; + } + } + + public bool IsMethodOwned { + get { + return DeclaringType == null; + } + } + + public SpecialConstraint SpecialConstraint { + get { + return spec; + } + set { + spec = value; + } + } + + // + // Types used to inflate the generic type + // + public new TypeSpec[] TypeArguments { + get { + return targs; + } + set { + targs = value; + } + } + + public Variance Variance { + get { + return variance; + } + } + + #endregion + + public string DisplayDebugInfo () + { + var s = GetSignatureForError (); + return IsMethodOwned ? s + "!!" : s + "!"; + } + + // + // Finds effective base class. The effective base class is always a class-type + // + public TypeSpec GetEffectiveBase () + { + if (HasSpecialStruct) + return BaseType; + + // + // If T has a class-type constraint C but no type-parameter constraints, its effective base class is C + // + if (BaseType != null && targs == null) { + // + // If T has a constraint V that is a value-type, use instead the most specific base type of V that is a class-type. + // + // LAMESPEC: Is System.ValueType always the most specific base type in this case? + // + // Note: This can never happen in an explicitly given constraint, but may occur when the constraints of a generic method + // are implicitly inherited by an overriding method declaration or an explicit implementation of an interface method. + // + return BaseType.IsStruct ? BaseType.BaseType : BaseType; + } + + if (effective_base != null) + return effective_base; + + var types = new TypeSpec [HasTypeConstraint ? targs.Length + 1 : targs.Length]; + + for (int i = 0; i < targs.Length; ++i) { + var t = targs [i]; + + // Same issue as above, inherited constraints can be of struct type + if (t.IsStruct) { + types [i] = t.BaseType; + continue; + } + + var tps = t as TypeParameterSpec; + types [i] = tps != null ? tps.GetEffectiveBase () : t; + } + + if (HasTypeConstraint) + types [types.Length - 1] = BaseType; + + return effective_base = Convert.FindMostEncompassedType (types); + } + + public override string GetSignatureForDocumentation () + { + var prefix = IsMethodOwned ? "``" : "`"; + return prefix + DeclaredPosition; + } + + public override string GetSignatureForError () + { + return Name; + } + + // + // Constraints have to match by definition but not position, used by + // partial classes or methods + // + public bool HasSameConstraintsDefinition (TypeParameterSpec other) + { + if (spec != other.spec) + return false; + + if (BaseType != other.BaseType) + return false; + + if (!TypeSpecComparer.Override.IsSame (InterfacesDefined, other.InterfacesDefined)) + return false; + + if (!TypeSpecComparer.Override.IsSame (targs, other.targs)) + return false; + + return true; + } + + // + // Constraints have to match by using same set of types, used by + // implicit interface implementation + // + public bool HasSameConstraintsImplementation (TypeParameterSpec other) + { + if (spec != other.spec) + return false; + + // + // It can be same base type or inflated type parameter + // + // interface I { void Foo where U : T; } + // class A : I { void Foo where X : int {} } + // + bool found; + if (!TypeSpecComparer.Override.IsEqual (BaseType, other.BaseType)) { + if (other.targs == null) + return false; + + found = false; + foreach (var otarg in other.targs) { + if (TypeSpecComparer.Override.IsEqual (BaseType, otarg)) { + found = true; + break; + } + } + + if (!found) + return false; + } + + // Check interfaces implementation -> definition + if (InterfacesDefined != null) { + // + // Iterate over inflated interfaces + // + foreach (var iface in Interfaces) { + found = false; + if (other.InterfacesDefined != null) { + foreach (var oiface in other.Interfaces) { + if (TypeSpecComparer.Override.IsEqual (iface, oiface)) { + found = true; + break; + } + } + } + + if (found) + continue; + + if (other.targs != null) { + foreach (var otarg in other.targs) { + if (TypeSpecComparer.Override.IsEqual (iface, otarg)) { + found = true; + break; + } + } + } + + if (!found) + return false; + } + } + + // Check interfaces implementation <- definition + if (other.InterfacesDefined != null) { + if (InterfacesDefined == null) + return false; + + // + // Iterate over inflated interfaces + // + foreach (var oiface in other.Interfaces) { + found = false; + foreach (var iface in Interfaces) { + if (TypeSpecComparer.Override.IsEqual (iface, oiface)) { + found = true; + break; + } + } + + if (!found) + return false; + } + } + + // Check type parameters implementation -> definition + if (targs != null) { + if (other.targs == null) + return false; + + foreach (var targ in targs) { + found = false; + foreach (var otarg in other.targs) { + if (TypeSpecComparer.Override.IsEqual (targ, otarg)) { + found = true; + break; + } + } + + if (!found) + return false; + } + } + + // Check type parameters implementation <- definition + if (other.targs != null) { + foreach (var otarg in other.targs) { + // Ignore inflated type arguments, were checked above + if (!otarg.IsGenericParameter) + continue; + + if (targs == null) + return false; + + found = false; + foreach (var targ in targs) { + if (TypeSpecComparer.Override.IsEqual (targ, otarg)) { + found = true; + break; + } + } + + if (!found) + return false; + } + } + + return true; + } + + public static TypeParameterSpec[] InflateConstraints (TypeParameterInflator inflator, TypeParameterSpec[] tparams) + { + return InflateConstraints (tparams, l => l, inflator); + } + + public static TypeParameterSpec[] InflateConstraints (TypeParameterSpec[] tparams, Func inflatorFactory, T arg) + { + TypeParameterSpec[] constraints = null; + TypeParameterInflator? inflator = null; + + for (int i = 0; i < tparams.Length; ++i) { + var tp = tparams[i]; + if (tp.HasTypeConstraint || tp.InterfacesDefined != null || tp.TypeArguments != null) { + if (constraints == null) { + constraints = new TypeParameterSpec[tparams.Length]; + Array.Copy (tparams, constraints, constraints.Length); + } + + // + // Using a factory to avoid possibly expensive inflator build up + // + if (inflator == null) + inflator = inflatorFactory (arg); + + constraints[i] = (TypeParameterSpec) constraints[i].InflateMember (inflator.Value); + } + } + + if (constraints == null) + constraints = tparams; + + return constraints; + } + + public void InflateConstraints (TypeParameterInflator inflator, TypeParameterSpec tps) + { + tps.BaseType = inflator.Inflate (BaseType); + + var defined = InterfacesDefined; + if (defined != null) { + tps.ifaces_defined = new TypeSpec[defined.Length]; + for (int i = 0; i < defined.Length; ++i) + tps.ifaces_defined [i] = inflator.Inflate (defined[i]); + } else if (ifaces_defined == TypeSpec.EmptyTypes) { + tps.ifaces_defined = TypeSpec.EmptyTypes; + } + + var ifaces = Interfaces; + if (ifaces != null) { + tps.ifaces = new List (ifaces.Count); + for (int i = 0; i < ifaces.Count; ++i) + tps.ifaces.Add (inflator.Inflate (ifaces[i])); + tps.state |= StateFlags.InterfacesExpanded; + } + + if (targs != null) { + tps.targs = new TypeSpec[targs.Length]; + for (int i = 0; i < targs.Length; ++i) + tps.targs[i] = inflator.Inflate (targs[i]); + } + } + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var tps = (TypeParameterSpec) MemberwiseClone (); +#if DEBUG + tps.ID += 1000000; +#endif + + InflateConstraints (inflator, tps); + return tps; + } + + // + // Populates type parameter members using type parameter constraints + // The trick here is to be called late enough but not too late to + // populate member cache with all members from other types + // + protected override void InitializeMemberCache (bool onlyTypes) + { + cache = new MemberCache (); + + // + // For a type parameter the membercache is the union of the sets of members of the types + // specified as a primary constraint or secondary constraint + // + if (BaseType.BuiltinType != BuiltinTypeSpec.Type.Object && BaseType.BuiltinType != BuiltinTypeSpec.Type.ValueType) + cache.AddBaseType (BaseType); + + if (InterfacesDefined != null) { + foreach (var iface_type in InterfacesDefined) { + cache.AddInterface (iface_type); + } + } + + if (targs != null) { + foreach (var ta in targs) { + var tps = ta as TypeParameterSpec; + IList ifaces; + if (tps != null) { + var b_type = tps.GetEffectiveBase (); + if (b_type != null && b_type.BuiltinType != BuiltinTypeSpec.Type.Object && b_type.BuiltinType != BuiltinTypeSpec.Type.ValueType) + cache.AddBaseType (b_type); + + ifaces = tps.InterfacesDefined; + } else { + ifaces = ta.Interfaces; + } + + if (ifaces != null) { + foreach (var iface_type in ifaces) { + cache.AddInterface (iface_type); + } + } + } + } + } + + public bool IsConvertibleToInterface (TypeSpec iface) + { + if (Interfaces != null) { + foreach (var t in Interfaces) { + if (t == iface) + return true; + } + } + + if (TypeArguments != null) { + foreach (var t in TypeArguments) { + var tps = t as TypeParameterSpec; + if (tps != null) { + if (tps.IsConvertibleToInterface (iface)) + return true; + + continue; + } + + if (t.ImplementsInterface (iface, false)) + return true; + } + } + + return false; + } + + public static bool HasAnyTypeParameterTypeConstrained (IGenericMethodDefinition md) + { + var tps = md.TypeParameters; + for (int i = 0; i < md.TypeParametersCount; ++i) { + if (tps[i].HasAnyTypeConstraint) { + return true; + } + } + + return false; + } + + public static bool HasAnyTypeParameterConstrained (IGenericMethodDefinition md) + { + var tps = md.TypeParameters; + for (int i = 0; i < md.TypeParametersCount; ++i) { + if (tps[i].IsConstrained) { + return true; + } + } + + return false; + } + + public bool HasDependencyOn (TypeSpec type) + { + if (TypeArguments != null) { + foreach (var targ in TypeArguments) { + if (TypeSpecComparer.Override.IsEqual (targ, type)) + return true; + + var tps = targ as TypeParameterSpec; + if (tps != null && tps.HasDependencyOn (type)) + return true; + } + } + + return false; + } + + public override TypeSpec Mutate (TypeParameterMutator mutator) + { + return mutator.Mutate (this); + } + } + + public struct TypeParameterInflator + { + readonly TypeSpec type; + readonly TypeParameterSpec[] tparams; + readonly TypeSpec[] targs; + readonly IModuleContext context; + + public TypeParameterInflator (TypeParameterInflator nested, TypeSpec type) + : this (nested.context, type, nested.tparams, nested.targs) + { + } + + public TypeParameterInflator (IModuleContext context, TypeSpec type, TypeParameterSpec[] tparams, TypeSpec[] targs) + { + if (tparams.Length != targs.Length) + throw new ArgumentException ("Invalid arguments"); + + this.context = context; + this.tparams = tparams; + this.targs = targs; + this.type = type; + } + + #region Properties + + public IModuleContext Context { + get { + return context; + } + } + + public TypeSpec TypeInstance { + get { + return type; + } + } + + // + // Type parameters to inflate + // + public TypeParameterSpec[] TypeParameters { + get { + return tparams; + } + } + + #endregion + + public TypeSpec Inflate (TypeSpec type) + { + var tp = type as TypeParameterSpec; + if (tp != null) + return Inflate (tp); + + var ec = type as ElementTypeSpec; + if (ec != null) { + var et = Inflate (ec.Element); + if (et != ec.Element) { + var ac = ec as ArrayContainer; + if (ac != null) + return ArrayContainer.MakeType (context.Module, et, ac.Rank); + + if (ec is PointerContainer) + return PointerContainer.MakeType (context.Module, et); + + throw new NotImplementedException (); + } + + return ec; + } + + if (type.Kind == MemberKind.MissingType) + return type; + + // + // When inflating a nested type, inflate its parent first + // in case it's using same type parameters (was inflated within the type) + // + TypeSpec[] targs; + int i = 0; + if (type.IsNested) { + var parent = Inflate (type.DeclaringType); + + // + // Keep the inflated type arguments + // + targs = type.TypeArguments; + + // + // When inflating imported nested type used inside same declaring type, we get TypeSpec + // because the import cache helps us to catch it. However, that means we have to look at + // type definition to get type argument (they are in fact type parameter in this case) + // + if (targs.Length == 0 && type.Arity > 0) + targs = type.MemberDefinition.TypeParameters; + + // + // Parent was inflated, find the same type on inflated type + // to use same cache for nested types on same generic parent + // + type = MemberCache.FindNestedType (parent, type.Name, type.Arity); + + // + // Handle the tricky case where parent shares local type arguments + // which means inflating inflated type + // + // class Test { + // public static Nested Foo () { return null; } + // + // public class Nested {} + // } + // + // return type of Test.Foo() has to be Test.Nested + // + if (targs.Length > 0) { + var inflated_targs = new TypeSpec[targs.Length]; + for (; i < targs.Length; ++i) + inflated_targs[i] = Inflate (targs[i]); + + type = type.MakeGenericType (context, inflated_targs); + } + + return type; + } + + // Nothing to do for non-generic type + if (type.Arity == 0) + return type; + + targs = new TypeSpec[type.Arity]; + + // + // Inflating using outside type arguments, var v = new Foo (), class Foo {} + // + if (type is InflatedTypeSpec) { + for (; i < targs.Length; ++i) + targs[i] = Inflate (type.TypeArguments[i]); + + type = type.GetDefinition (); + } else { + // + // Inflating parent using inside type arguments, class Foo { ITest foo; } + // + var args = type.MemberDefinition.TypeParameters; + foreach (var ds_tp in args) + targs[i++] = Inflate (ds_tp); + } + + return type.MakeGenericType (context, targs); + } + + public TypeSpec Inflate (TypeParameterSpec tp) + { + for (int i = 0; i < tparams.Length; ++i) + if (tparams [i] == tp) + return targs[i]; + + // This can happen when inflating nested types + // without type arguments specified + return tp; + } + } + + // + // Before emitting any code we have to change all MVAR references to VAR + // when the method is of generic type and has hoisted variables + // + public class TypeParameterMutator + { + readonly TypeParameters mvar; + readonly TypeParameters var; + readonly TypeParameterSpec[] src; + Dictionary mutated_typespec; + + public TypeParameterMutator (TypeParameters mvar, TypeParameters var) + { + if (mvar.Count != var.Count) + throw new ArgumentException (); + + this.mvar = mvar; + this.var = var; + } + + public TypeParameterMutator (TypeParameterSpec[] srcVar, TypeParameters destVar) + { + if (srcVar.Length != destVar.Count) + throw new ArgumentException (); + + this.src = srcVar; + this.var = destVar; + } + + #region Properties + + public TypeParameters MethodTypeParameters { + get { + return mvar; + } + } + + #endregion + + public static TypeSpec GetMemberDeclaringType (TypeSpec type) + { + if (type is InflatedTypeSpec) { + if (type.DeclaringType == null) + return type.GetDefinition (); + + var parent = GetMemberDeclaringType (type.DeclaringType); + type = MemberCache.GetMember (parent, type); + } + + return type; + } + + public TypeSpec Mutate (TypeSpec ts) + { + TypeSpec value; + if (mutated_typespec != null && mutated_typespec.TryGetValue (ts, out value)) + return value; + + value = ts.Mutate (this); + if (mutated_typespec == null) + mutated_typespec = new Dictionary (); + + mutated_typespec.Add (ts, value); + return value; + } + + public TypeParameterSpec Mutate (TypeParameterSpec tp) + { + if (mvar != null) { + for (int i = 0; i < mvar.Count; ++i) { + if (mvar[i].Type == tp) + return var[i].Type; + } + } else { + for (int i = 0; i < src.Length; ++i) { + if (src[i] == tp) + return var[i].Type; + } + } + + return tp; + } + + public TypeSpec[] Mutate (TypeSpec[] targs) + { + TypeSpec[] mutated = new TypeSpec[targs.Length]; + bool changed = false; + for (int i = 0; i < targs.Length; ++i) { + mutated[i] = Mutate (targs[i]); + changed |= targs[i] != mutated[i]; + } + + return changed ? mutated : targs; + } + } + + /// + /// A TypeExpr which already resolved to a type parameter. + /// + public class TypeParameterExpr : TypeExpression + { + public TypeParameterExpr (TypeParameter type_parameter, Location loc) + : base (type_parameter.Type, loc) + { + this.eclass = ExprClass.TypeParameter; + } + } + + public class InflatedTypeSpec : TypeSpec + { + TypeSpec[] targs; + TypeParameterSpec[] constraints; + readonly TypeSpec open_type; + readonly IModuleContext context; + + public InflatedTypeSpec (IModuleContext context, TypeSpec openType, TypeSpec declaringType, TypeSpec[] targs) + : base (openType.Kind, declaringType, openType.MemberDefinition, null, openType.Modifiers) + { + if (targs == null) + throw new ArgumentNullException ("targs"); + + this.state &= ~SharedStateFlags; + this.state |= (openType.state & SharedStateFlags); + + this.context = context; + this.open_type = openType; + this.targs = targs; + + foreach (var arg in targs) { + if (arg.HasDynamicElement || arg.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + state |= StateFlags.HasDynamicElement; + break; + } + } + + if (open_type.Kind == MemberKind.MissingType) + MemberCache = MemberCache.Empty; + + if ((open_type.Modifiers & Modifiers.COMPILER_GENERATED) != 0) + state |= StateFlags.ConstraintsChecked; + } + + #region Properties + + public override TypeSpec BaseType { + get { + if (cache == null || (state & StateFlags.PendingBaseTypeInflate) != 0) + InitializeMemberCache (true); + + return base.BaseType; + } + } + + // + // Inflated type parameters with constraints array, mapping with type arguments is based on index + // + public TypeParameterSpec[] Constraints { + get { + if (constraints == null) { + constraints = TypeParameterSpec.InflateConstraints (MemberDefinition.TypeParameters, l => l.CreateLocalInflator (context), this); + } + + return constraints; + } + } + + // + // Used to cache expensive constraints validation on constructed types + // + public bool HasConstraintsChecked { + get { + return (state & StateFlags.ConstraintsChecked) != 0; + } + set { + state = value ? state | StateFlags.ConstraintsChecked : state & ~StateFlags.ConstraintsChecked; + } + } + + public override IList Interfaces { + get { + if (cache == null) + InitializeMemberCache (true); + + return base.Interfaces; + } + } + + public override bool IsExpressionTreeType { + get { + return (open_type.state & StateFlags.InflatedExpressionType) != 0; + } + } + + public override bool IsArrayGenericInterface { + get { + return (open_type.state & StateFlags.GenericIterateInterface) != 0; + } + } + + public override bool IsGenericTask { + get { + return (open_type.state & StateFlags.GenericTask) != 0; + } + } + + public override bool IsNullableType { + get { + return (open_type.state & StateFlags.InflatedNullableType) != 0; + } + } + + // + // Types used to inflate the generic type + // + public override TypeSpec[] TypeArguments { + get { + return targs; + } + } + + #endregion + + public override bool AddInterface (TypeSpec iface) + { + var inflator = CreateLocalInflator (context); + iface = inflator.Inflate (iface); + if (iface == null) + return false; + + return base.AddInterface (iface); + } + + public static bool ContainsTypeParameter (TypeSpec type) + { + if (type.Kind == MemberKind.TypeParameter) + return true; + + var element_container = type as ElementTypeSpec; + if (element_container != null) + return ContainsTypeParameter (element_container.Element); + + foreach (var t in type.TypeArguments) { + if (ContainsTypeParameter (t)) { + return true; + } + } + + return false; + } + + public TypeParameterInflator CreateLocalInflator (IModuleContext context) + { + TypeParameterSpec[] tparams_full; + TypeSpec[] targs_full = targs; + if (IsNested) { + // + // Special case is needed when we are inflating an open type (nested type definition) + // on inflated parent. Consider following case + // + // Foo.Bar => Foo.Bar + // + // Any later inflation of Foo.Bar has to also inflate T if used inside Bar + // + List merged_targs = null; + List merged_tparams = null; + + var type = DeclaringType; + + do { + if (type.TypeArguments.Length > 0) { + if (merged_targs == null) { + merged_targs = new List (); + merged_tparams = new List (); + if (targs.Length > 0) { + merged_targs.AddRange (targs); + merged_tparams.AddRange (open_type.MemberDefinition.TypeParameters); + } + } + merged_tparams.AddRange (type.MemberDefinition.TypeParameters); + merged_targs.AddRange (type.TypeArguments); + } + type = type.DeclaringType; + } while (type != null); + + if (merged_targs != null) { + // Type arguments are not in the right order but it should not matter in this case + targs_full = merged_targs.ToArray (); + tparams_full = merged_tparams.ToArray (); + } else if (targs.Length == 0) { + tparams_full = TypeParameterSpec.EmptyTypes; + } else { + tparams_full = open_type.MemberDefinition.TypeParameters; + } + } else if (targs.Length == 0) { + tparams_full = TypeParameterSpec.EmptyTypes; + } else { + tparams_full = open_type.MemberDefinition.TypeParameters; + } + + return new TypeParameterInflator (context, this, tparams_full, targs_full); + } + + MetaType CreateMetaInfo () + { + // + // Converts nested type arguments into right order + // Foo.Bar => string, bool, int + // + var all = new List (); + TypeSpec type = this; + TypeSpec definition = type; + do { + if (type.GetDefinition().IsGeneric) { + all.InsertRange (0, + type.TypeArguments != TypeSpec.EmptyTypes ? + type.TypeArguments.Select (l => l.GetMetaInfo ()) : + type.MemberDefinition.TypeParameters.Select (l => l.GetMetaInfo ())); + } + + definition = definition.GetDefinition (); + type = type.DeclaringType; + } while (type != null); + + return definition.GetMetaInfo ().MakeGenericType (all.ToArray ()); + } + + public override ObsoleteAttribute GetAttributeObsolete () + { + return open_type.GetAttributeObsolete (); + } + + protected override bool IsNotCLSCompliant (out bool attrValue) + { + if (base.IsNotCLSCompliant (out attrValue)) + return true; + + foreach (var ta in TypeArguments) { + if (ta.MemberDefinition.CLSAttributeValue == false) + return true; + } + + return false; + } + + public override TypeSpec GetDefinition () + { + return open_type; + } + + public override MetaType GetMetaInfo () + { + if (info == null) + info = CreateMetaInfo (); + + return info; + } + + public override string GetSignatureForError () + { + if (IsNullableType) + return targs[0].GetSignatureForError () + "?"; + + return base.GetSignatureForError (); + } + + protected override string GetTypeNameSignature () + { + if (targs.Length == 0 || MemberDefinition is AnonymousTypeClass) + return null; + + return "<" + TypeManager.CSharpName (targs) + ">"; + } + + public bool HasDynamicArgument () + { + for (int i = 0; i < targs.Length; ++i) { + var item = targs[i]; + + if (item.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + + if (item is InflatedTypeSpec) { + if (((InflatedTypeSpec) item).HasDynamicArgument ()) + return true; + + continue; + } + + if (item.IsArray) { + while (item.IsArray) { + item = ((ArrayContainer) item).Element; + } + + if (item.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + } + } + + return false; + } + + protected override void InitializeMemberCache (bool onlyTypes) + { + if (cache == null) { + var open_cache = onlyTypes ? open_type.MemberCacheTypes : open_type.MemberCache; + + // Surprisingly, calling MemberCache on open type could meantime create cache on this type + // for imported type parameter constraints referencing nested type of this declaration + if (cache == null) + cache = new MemberCache (open_cache); + } + + var inflator = CreateLocalInflator (context); + + // + // Two stage inflate due to possible nested types recursive + // references + // + // class A { + // B b; + // class B { + // T Value; + // } + // } + // + // When resolving type of `b' members of `B' cannot be + // inflated because are not yet available in membercache + // + if ((state & StateFlags.PendingMemberCacheMembers) == 0) { + open_type.MemberCacheTypes.InflateTypes (cache, inflator); + + // + // Inflate any implemented interfaces + // + if (open_type.Interfaces != null) { + ifaces = new List (open_type.Interfaces.Count); + foreach (var iface in open_type.Interfaces) { + var iface_inflated = inflator.Inflate (iface); + if (iface_inflated == null) + continue; + + base.AddInterface (iface_inflated); + } + } + + // + // Handles the tricky case of recursive nested base generic type + // + // class A : Base.Nested> { + // class Nested {} + // } + // + // When inflating A. base type is not yet known, secondary + // inflation is required (not common case) once base scope + // is known + // + if (open_type.BaseType == null) { + if (IsClass) + state |= StateFlags.PendingBaseTypeInflate; + } else { + BaseType = inflator.Inflate (open_type.BaseType); + } + } else if ((state & StateFlags.PendingBaseTypeInflate) != 0) { + // + // It can happen when resolving base type without being defined + // which is not allowed to happen and will always lead to an error + // + // class B { class N {} } + // class A : A {} + // + if (open_type.BaseType == null) + return; + + BaseType = inflator.Inflate (open_type.BaseType); + state &= ~StateFlags.PendingBaseTypeInflate; + } + + if (onlyTypes) { + state |= StateFlags.PendingMemberCacheMembers; + return; + } + + var tc = open_type.MemberDefinition as TypeDefinition; + if (tc != null && !tc.HasMembersDefined) { + // + // Inflating MemberCache with undefined members + // + return; + } + + if ((state & StateFlags.PendingBaseTypeInflate) != 0) { + BaseType = inflator.Inflate (open_type.BaseType); + state &= ~StateFlags.PendingBaseTypeInflate; + } + + state &= ~StateFlags.PendingMemberCacheMembers; + open_type.MemberCache.InflateMembers (cache, open_type, inflator); + } + + public override TypeSpec Mutate (TypeParameterMutator mutator) + { + var targs = TypeArguments; + if (targs != null) + targs = mutator.Mutate (targs); + + var decl = DeclaringType; + if (IsNested && DeclaringType.IsGenericOrParentIsGeneric) + decl = mutator.Mutate (decl); + + if (targs == TypeArguments && decl == DeclaringType) + return this; + + var mutated = (InflatedTypeSpec) MemberwiseClone (); + if (decl != DeclaringType) { + // Gets back MethodInfo in case of metaInfo was inflated + //mutated.info = MemberCache.GetMember (DeclaringType.GetDefinition (), this).info; + + mutated.declaringType = decl; + mutated.state |= StateFlags.PendingMetaInflate; + } + + if (targs != null) { + mutated.targs = targs; + mutated.info = null; + } + + return mutated; + } + } + + + // + // Tracks the type arguments when instantiating a generic type. It's used + // by both type arguments and type parameters + // + public class TypeArguments + { + List args; + TypeSpec[] atypes; + + public List Args { + get { return this.args; } + } + + public TypeArguments (params FullNamedExpression[] types) + { + this.args = new List (types); + } + + public void Add (FullNamedExpression type) + { + args.Add (type); + } + + /// + /// We may only be used after Resolve() is called and return the fully + /// resolved types. + /// + // TODO: Not needed, just return type from resolve + public TypeSpec[] Arguments { + get { + return atypes; + } + set { + atypes = value; + } + } + + public int Count { + get { + return args.Count; + } + } + + public virtual bool IsEmpty { + get { + return false; + } + } + + public List TypeExpressions { + get { + return this.args; + } + } + + public string GetSignatureForError() + { + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < Count; ++i) { + var expr = args[i]; + if (expr != null) + sb.Append (expr.GetSignatureForError ()); + + if (i + 1 < Count) + sb.Append (','); + } + + return sb.ToString (); + } + + /// + /// Resolve the type arguments. + /// + public virtual bool Resolve (IMemberContext ec) + { + if (atypes != null) + return true; + + int count = args.Count; + bool ok = true; + + atypes = new TypeSpec [count]; + + var errors = ec.Module.Compiler.Report.Errors; + + for (int i = 0; i < count; i++){ + var te = args[i].ResolveAsType (ec); + if (te == null) { + ok = false; + continue; + } + + atypes[i] = te; + + if (te.IsStatic) { + ec.Module.Compiler.Report.Error (718, args[i].Location, "`{0}': static classes cannot be used as generic arguments", + te.GetSignatureForError ()); + ok = false; + } + + if (te.IsPointer || te.IsSpecialRuntimeType) { + ec.Module.Compiler.Report.Error (306, args[i].Location, + "The type `{0}' may not be used as a type argument", + te.GetSignatureForError ()); + ok = false; + } + } + + if (!ok || errors != ec.Module.Compiler.Report.Errors) + atypes = null; + + return ok; + } + + public TypeArguments Clone () + { + TypeArguments copy = new TypeArguments (); + foreach (var ta in args) + copy.args.Add (ta); + + return copy; + } + } + + public class UnboundTypeArguments : TypeArguments + { + public UnboundTypeArguments (int arity) + : base (new FullNamedExpression[arity]) + { + } + + public override bool IsEmpty { + get { + return true; + } + } + + public override bool Resolve (IMemberContext ec) + { + // Nothing to be resolved + return true; + } + } + + public class TypeParameters + { + List names; + TypeParameterSpec[] types; + + public TypeParameters () + { + names = new List (); + } + + public TypeParameters (int count) + { + names = new List (count); + } + + #region Properties + + public int Count { + get { + return names.Count; + } + } + + public TypeParameterSpec[] Types { + get { + return types; + } + } + + #endregion + + public void Add (TypeParameter tparam) + { + names.Add (tparam); + } + + public void Add (TypeParameters tparams) + { + names.AddRange (tparams.names); + } + + public void Create (TypeSpec declaringType, int parentOffset, TypeContainer parent) + { + types = new TypeParameterSpec[Count]; + for (int i = 0; i < types.Length; ++i) { + var tp = names[i]; + + tp.Create (declaringType, parent); + types[i] = tp.Type; + types[i].DeclaredPosition = i + parentOffset; + + if (tp.Variance != Variance.None && !(declaringType != null && (declaringType.Kind == MemberKind.Interface || declaringType.Kind == MemberKind.Delegate))) { + parent.Compiler.Report.Error (1960, tp.Location, "Variant type parameters can only be used with interfaces and delegates"); + } + } + } + + public void Define (GenericTypeParameterBuilder[] builders) + { + for (int i = 0; i < types.Length; ++i) { + var tp = names[i]; + tp.Define (builders [types [i].DeclaredPosition]); + } + } + + public TypeParameter this[int index] { + get { + return names [index]; + } + set { + names[index] = value; + } + } + + public TypeParameter Find (string name) + { + foreach (var tp in names) { + if (tp.Name == name) + return tp; + } + + return null; + } + + public string[] GetAllNames () + { + return names.Select (l => l.Name).ToArray (); + } + + public string GetSignatureForError () + { + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < Count; ++i) { + if (i > 0) + sb.Append (','); + + var name = names[i]; + if (name != null) + sb.Append (name.GetSignatureForError ()); + } + + return sb.ToString (); + } + + + public void CheckPartialConstraints (Method part) + { + var partTypeParameters = part.CurrentTypeParameters; + + for (int i = 0; i < Count; i++) { + var tp_a = names[i]; + var tp_b = partTypeParameters [i]; + if (tp_a.Constraints == null) { + if (tp_b.Constraints == null) + continue; + } else if (tp_b.Constraints != null && tp_a.Type.HasSameConstraintsDefinition (tp_b.Type)) { + continue; + } + + part.Compiler.Report.SymbolRelatedToPreviousError (this[i].CurrentMemberDefinition.Location, ""); + part.Compiler.Report.Error (761, part.Location, + "Partial method declarations of `{0}' have inconsistent constraints for type parameter `{1}'", + part.GetSignatureForError (), partTypeParameters[i].GetSignatureForError ()); + } + } + + public void UpdateConstraints (TypeDefinition part) + { + var partTypeParameters = part.MemberName.TypeParameters; + + for (int i = 0; i < Count; i++) { + var tp = names [i]; + if (tp.AddPartialConstraints (part, partTypeParameters [i])) + continue; + + part.Compiler.Report.SymbolRelatedToPreviousError (this[i].CurrentMemberDefinition); + part.Compiler.Report.Error (265, part.Location, + "Partial declarations of `{0}' have inconsistent constraints for type parameter `{1}'", + part.GetSignatureForError (), tp.GetSignatureForError ()); + } + } + + public void VerifyClsCompliance () + { + foreach (var tp in names) { + tp.VerifyClsCompliance (); + } + } + } + + // + // A type expression of generic type with type arguments + // + class GenericTypeExpr : TypeExpr + { + TypeArguments args; + TypeSpec open_type; + + /// + /// Instantiate the generic type `t' with the type arguments `args'. + /// Use this constructor if you already know the fully resolved + /// generic type. + /// + public GenericTypeExpr (TypeSpec open_type, TypeArguments args, Location l) + { + this.open_type = open_type; + loc = l; + this.args = args; + } + + public override string GetSignatureForError () + { + return type.GetSignatureForError (); + } + + public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + if (eclass != ExprClass.Unresolved) + return type; + + if (!args.Resolve (mc)) + return null; + + TypeSpec[] atypes = args.Arguments; + if (atypes == null) + return null; + + // + // Now bind the parameters + // + var inflated = open_type.MakeGenericType (mc, atypes); + type = inflated; + eclass = ExprClass.Type; + + // + // The constraints can be checked only when full type hierarchy is known + // + if (!inflated.HasConstraintsChecked && mc.Module.HasTypesFullyDefined) { + var constraints = inflated.Constraints; + if (constraints != null) { + var cc = new ConstraintChecker (mc); + if (cc.CheckAll (open_type, atypes, constraints, loc)) { + inflated.HasConstraintsChecked = true; + } + } + } + + return type; + } + + public override bool Equals (object obj) + { + GenericTypeExpr cobj = obj as GenericTypeExpr; + if (cobj == null) + return false; + + if ((type == null) || (cobj.type == null)) + return false; + + return type == cobj.type; + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + } + + // + // Generic type with unbound type arguments, used for typeof (G<,,>) + // + class GenericOpenTypeExpr : TypeExpression + { + public GenericOpenTypeExpr (TypeSpec type, /*UnboundTypeArguments args,*/ Location loc) + : base (type.GetDefinition (), loc) + { + } + } + + struct ConstraintChecker + { + IMemberContext mc; + bool recursive_checks; + + public ConstraintChecker (IMemberContext ctx) + { + this.mc = ctx; + recursive_checks = false; + } + + // + // Checks the constraints of open generic type against type + // arguments. This version is used for types which could not be + // checked immediatelly during construction because the type + // hierarchy was not yet fully setup (before Emit phase) + // + public static bool Check (IMemberContext mc, TypeSpec type, Location loc) + { + // + // Check declaring type first if there is any + // + if (type.DeclaringType != null && !Check (mc, type.DeclaringType, loc)) + return false; + + while (type is ElementTypeSpec) + type = ((ElementTypeSpec) type).Element; + + if (type.Arity == 0) + return true; + + var gtype = type as InflatedTypeSpec; + if (gtype == null) + return true; + + var constraints = gtype.Constraints; + if (constraints == null) + return true; + + if (gtype.HasConstraintsChecked) + return true; + + var cc = new ConstraintChecker (mc); + cc.recursive_checks = true; + + if (cc.CheckAll (gtype.GetDefinition (), type.TypeArguments, constraints, loc)) { + gtype.HasConstraintsChecked = true; + return true; + } + + return false; + } + + // + // Checks all type arguments againts type parameters constraints + // NOTE: It can run in probing mode when `this.mc' is null + // + public bool CheckAll (MemberSpec context, TypeSpec[] targs, TypeParameterSpec[] tparams, Location loc) + { + for (int i = 0; i < tparams.Length; i++) { + var targ = targs[i]; + if (!CheckConstraint (context, targ, tparams [i], loc)) + return false; + + if (!recursive_checks) + continue; + + if (!Check (mc, targ, loc)) + return false; + } + + return true; + } + + bool CheckConstraint (MemberSpec context, TypeSpec atype, TypeParameterSpec tparam, Location loc) + { + // + // First, check the `class' and `struct' constraints. + // + if (tparam.HasSpecialClass && !TypeSpec.IsReferenceType (atype)) { + if (mc != null) { + mc.Module.Compiler.Report.Error (452, loc, + "The type `{0}' must be a reference type in order to use it as type parameter `{1}' in the generic type or method `{2}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); + } + + return false; + } + + if (tparam.HasSpecialStruct && (!TypeSpec.IsValueType (atype) || atype.IsNullableType)) { + if (mc != null) { + mc.Module.Compiler.Report.Error (453, loc, + "The type `{0}' must be a non-nullable value type in order to use it as type parameter `{1}' in the generic type or method `{2}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); + } + + return false; + } + + bool ok = true; + + // + // Check the class constraint + // + if (tparam.HasTypeConstraint) { + if (!CheckConversion (mc, context, atype, tparam, tparam.BaseType, loc)) { + if (mc == null) + return false; + + ok = false; + } + } + + // + // Check the interfaces constraints + // + if (tparam.InterfacesDefined != null) { + foreach (TypeSpec iface in tparam.InterfacesDefined) { + if (!CheckConversion (mc, context, atype, tparam, iface, loc)) { + if (mc == null) + return false; + + ok = false; + break; + } + } + } + + // + // Check the type parameter constraint + // + if (tparam.TypeArguments != null) { + foreach (var ta in tparam.TypeArguments) { + if (!CheckConversion (mc, context, atype, tparam, ta, loc)) { + if (mc == null) + return false; + + ok = false; + break; + } + } + } + + // + // Finally, check the constructor constraint. + // + if (!tparam.HasSpecialConstructor) + return ok; + + if (!HasDefaultConstructor (atype)) { + if (mc != null) { + mc.Module.Compiler.Report.SymbolRelatedToPreviousError (atype); + mc.Module.Compiler.Report.Error (310, loc, + "The type `{0}' must have a public parameterless constructor in order to use it as parameter `{1}' in the generic type or method `{2}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); + } + return false; + } + + return ok; + } + + static bool HasDynamicTypeArgument (TypeSpec[] targs) + { + for (int i = 0; i < targs.Length; ++i) { + var targ = targs [i]; + if (targ.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + + if (HasDynamicTypeArgument (targ.TypeArguments)) + return true; + } + + return false; + } + + bool CheckConversion (IMemberContext mc, MemberSpec context, TypeSpec atype, TypeParameterSpec tparam, TypeSpec ttype, Location loc) + { + if (atype == ttype) + return true; + + if (atype.IsGenericParameter) { + var tps = (TypeParameterSpec) atype; + if (tps.HasDependencyOn (ttype)) + return true; + + if (Convert.ImplicitTypeParameterConversion (null, tps, ttype) != null) + return true; + + } else if (TypeSpec.IsValueType (atype)) { + if (atype.IsNullableType) { + // + // LAMESPEC: Only identity or base type ValueType or Object satisfy nullable type + // + if (TypeSpec.IsBaseClass (atype, ttype, false)) + return true; + } else { + if (Convert.ImplicitBoxingConversion (null, atype, ttype) != null) + return true; + } + } else { + if (Convert.ImplicitReferenceConversionExists (atype, ttype) || Convert.ImplicitBoxingConversion (null, atype, ttype) != null) + return true; + } + + if (mc != null) { + mc.Module.Compiler.Report.SymbolRelatedToPreviousError (tparam); + if (atype.IsGenericParameter) { + mc.Module.Compiler.Report.Error (314, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no boxing or type parameter conversion from `{0}' to `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } else if (TypeSpec.IsValueType (atype)) { + if (atype.IsNullableType) { + if (ttype.IsInterface) { + mc.Module.Compiler.Report.Error (313, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. The nullable type `{0}' never satisfies interface constraint `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } else { + mc.Module.Compiler.Report.Error (312, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. The nullable type `{0}' does not satisfy constraint `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } + } else { + mc.Module.Compiler.Report.Error (315, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no boxing conversion from `{0}' to `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } + } else { + mc.Module.Compiler.Report.Error (311, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no implicit reference conversion from `{0}' to `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } + } + + return false; + } + + static bool HasDefaultConstructor (TypeSpec atype) + { + var tp = atype as TypeParameterSpec; + if (tp != null) { + return tp.HasSpecialConstructor || tp.HasSpecialStruct; + } + + if (atype.IsStruct || atype.IsEnum) + return true; + + if (atype.IsAbstract) + return false; + + var tdef = atype.GetDefinition (); + + var found = MemberCache.FindMember (tdef, + MemberFilter.Constructor (ParametersCompiled.EmptyReadOnlyParameters), + BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly); + + return found != null && (found.Modifiers & Modifiers.PUBLIC) != 0; + } + } + + // + // Implements C# type inference + // + class TypeInference + { + // + // Tracks successful rate of type inference + // + int score = int.MaxValue; + readonly Arguments arguments; + readonly int arg_count; + + public TypeInference (Arguments arguments) + { + this.arguments = arguments; + if (arguments != null) + arg_count = arguments.Count; + } + + public int InferenceScore { + get { + return score; + } + } + + public TypeSpec[] InferMethodArguments (ResolveContext ec, MethodSpec method) + { + var method_generic_args = method.GenericDefinition.TypeParameters; + TypeInferenceContext context = new TypeInferenceContext (method_generic_args); + if (!context.UnfixedVariableExists) + return TypeSpec.EmptyTypes; + + AParametersCollection pd = method.Parameters; + if (!InferInPhases (ec, context, pd)) + return null; + + return context.InferredTypeArguments; + } + + // + // Implements method type arguments inference + // + bool InferInPhases (ResolveContext ec, TypeInferenceContext tic, AParametersCollection methodParameters) + { + int params_arguments_start; + if (methodParameters.HasParams) { + params_arguments_start = methodParameters.Count - 1; + } else { + params_arguments_start = arg_count; + } + + TypeSpec [] ptypes = methodParameters.Types; + + // + // The first inference phase + // + TypeSpec method_parameter = null; + for (int i = 0; i < arg_count; i++) { + Argument a = arguments [i]; + if (a == null) + continue; + + if (i < params_arguments_start) { + method_parameter = methodParameters.Types [i]; + } else if (i == params_arguments_start) { + if (arg_count == params_arguments_start + 1 && TypeManager.HasElementType (a.Type)) + method_parameter = methodParameters.Types [params_arguments_start]; + else + method_parameter = TypeManager.GetElementType (methodParameters.Types [params_arguments_start]); + + ptypes = (TypeSpec[]) ptypes.Clone (); + ptypes [i] = method_parameter; + } + + // + // When a lambda expression, an anonymous method + // is used an explicit argument type inference takes a place + // + AnonymousMethodExpression am = a.Expr as AnonymousMethodExpression; + if (am != null) { + if (am.ExplicitTypeInference (tic, method_parameter)) + --score; + continue; + } + + if (a.IsByRef) { + score -= tic.ExactInference (a.Type, method_parameter); + continue; + } + + if (a.Expr.Type == InternalType.NullLiteral) + continue; + + if (TypeSpec.IsValueType (method_parameter)) { + score -= tic.LowerBoundInference (a.Type, method_parameter); + continue; + } + + // + // Otherwise an output type inference is made + // + score -= tic.OutputTypeInference (ec, a.Expr, method_parameter); + } + + // + // Part of the second phase but because it happens only once + // we don't need to call it in cycle + // + bool fixed_any = false; + if (!tic.FixIndependentTypeArguments (ec, ptypes, ref fixed_any)) + return false; + + return DoSecondPhase (ec, tic, ptypes, !fixed_any); + } + + bool DoSecondPhase (ResolveContext ec, TypeInferenceContext tic, TypeSpec[] methodParameters, bool fixDependent) + { + bool fixed_any = false; + if (fixDependent && !tic.FixDependentTypes (ec, ref fixed_any)) + return false; + + // If no further unfixed type variables exist, type inference succeeds + if (!tic.UnfixedVariableExists) + return true; + + if (!fixed_any && fixDependent) + return false; + + // For all arguments where the corresponding argument output types + // contain unfixed type variables but the input types do not, + // an output type inference is made + for (int i = 0; i < arg_count; i++) { + + // Align params arguments + TypeSpec t_i = methodParameters [i >= methodParameters.Length ? methodParameters.Length - 1: i]; + + if (!t_i.IsDelegate) { + if (!t_i.IsExpressionTreeType) + continue; + + t_i = TypeManager.GetTypeArguments (t_i) [0]; + } + + var mi = Delegate.GetInvokeMethod (t_i); + TypeSpec rtype = mi.ReturnType; + + if (tic.IsReturnTypeNonDependent (mi, rtype)) { + // It can be null for default arguments + if (arguments[i] == null) + continue; + + score -= tic.OutputTypeInference (ec, arguments[i].Expr, t_i); + } + } + + + return DoSecondPhase (ec, tic, methodParameters, true); + } + } + + public class TypeInferenceContext + { + protected enum BoundKind + { + Exact = 0, + Lower = 1, + Upper = 2 + } + + struct BoundInfo : IEquatable + { + public readonly TypeSpec Type; + public readonly BoundKind Kind; + + public BoundInfo (TypeSpec type, BoundKind kind) + { + this.Type = type; + this.Kind = kind; + } + + public override int GetHashCode () + { + return Type.GetHashCode (); + } + + public Expression GetTypeExpression () + { + return new TypeExpression (Type, Location.Null); + } + + #region IEquatable Members + + public bool Equals (BoundInfo other) + { + return Type == other.Type && Kind == other.Kind; + } + + #endregion + } + + readonly TypeSpec[] tp_args; + readonly TypeSpec[] fixed_types; + readonly List[] bounds; + + // TODO MemberCache: Could it be TypeParameterSpec[] ?? + public TypeInferenceContext (TypeSpec[] typeArguments) + { + if (typeArguments.Length == 0) + throw new ArgumentException ("Empty generic arguments"); + + fixed_types = new TypeSpec [typeArguments.Length]; + for (int i = 0; i < typeArguments.Length; ++i) { + if (typeArguments [i].IsGenericParameter) { + if (bounds == null) { + bounds = new List [typeArguments.Length]; + tp_args = new TypeSpec [typeArguments.Length]; + } + tp_args [i] = typeArguments [i]; + } else { + fixed_types [i] = typeArguments [i]; + } + } + } + + // + // Used together with AddCommonTypeBound fo implement + // 7.4.2.13 Finding the best common type of a set of expressions + // + public TypeInferenceContext () + { + fixed_types = new TypeSpec [1]; + tp_args = new TypeSpec [1]; + tp_args[0] = InternalType.Arglist; // it can be any internal type + bounds = new List [1]; + } + + public TypeSpec[] InferredTypeArguments { + get { + return fixed_types; + } + } + + public void AddCommonTypeBound (TypeSpec type) + { + AddToBounds (new BoundInfo (type, BoundKind.Lower), 0, false); + } + + public void AddCommonTypeBoundAsync (TypeSpec type) + { + AddToBounds (new BoundInfo (type, BoundKind.Lower), 0, true); + } + + void AddToBounds (BoundInfo bound, int index, bool voidAllowed) + { + // + // Some types cannot be used as type arguments + // + if ((bound.Type.Kind == MemberKind.Void && !voidAllowed) || bound.Type.IsPointer || bound.Type.IsSpecialRuntimeType || + bound.Type == InternalType.MethodGroup || bound.Type == InternalType.AnonymousMethod) + return; + + var a = bounds [index]; + if (a == null) { + a = new List (2); + a.Add (bound); + bounds [index] = a; + return; + } + + if (a.Contains (bound)) + return; + + a.Add (bound); + } + + bool AllTypesAreFixed (TypeSpec[] types) + { + foreach (TypeSpec t in types) { + if (t.IsGenericParameter) { + if (!IsFixed (t)) + return false; + continue; + } + + if (TypeManager.IsGenericType (t)) + return AllTypesAreFixed (TypeManager.GetTypeArguments (t)); + } + + return true; + } + + // + // 26.3.3.8 Exact Inference + // + public int ExactInference (TypeSpec u, TypeSpec v) + { + // If V is an array type + if (v.IsArray) { + if (!u.IsArray) + return 0; + + var ac_u = (ArrayContainer) u; + var ac_v = (ArrayContainer) v; + if (ac_u.Rank != ac_v.Rank) + return 0; + + return ExactInference (ac_u.Element, ac_v.Element); + } + + // If V is constructed type and U is constructed type + if (TypeManager.IsGenericType (v)) { + if (!TypeManager.IsGenericType (u) || v.MemberDefinition != u.MemberDefinition) + return 0; + + TypeSpec [] ga_u = TypeManager.GetTypeArguments (u); + TypeSpec [] ga_v = TypeManager.GetTypeArguments (v); + if (ga_u.Length != ga_v.Length) + return 0; + + int score = 0; + for (int i = 0; i < ga_u.Length; ++i) + score += ExactInference (ga_u [i], ga_v [i]); + + return System.Math.Min (1, score); + } + + // If V is one of the unfixed type arguments + int pos = IsUnfixed (v); + if (pos == -1) + return 0; + + AddToBounds (new BoundInfo (u, BoundKind.Exact), pos, false); + return 1; + } + + public bool FixAllTypes (ResolveContext ec) + { + for (int i = 0; i < tp_args.Length; ++i) { + if (!FixType (ec, i)) + return false; + } + return true; + } + + // + // All unfixed type variables Xi are fixed for which all of the following hold: + // a, There is at least one type variable Xj that depends on Xi + // b, Xi has a non-empty set of bounds + // + public bool FixDependentTypes (ResolveContext ec, ref bool fixed_any) + { + for (int i = 0; i < tp_args.Length; ++i) { + if (fixed_types[i] != null) + continue; + + if (bounds[i] == null) + continue; + + if (!FixType (ec, i)) + return false; + + fixed_any = true; + } + + return true; + } + + // + // All unfixed type variables Xi which depend on no Xj are fixed + // + public bool FixIndependentTypeArguments (ResolveContext ec, TypeSpec[] methodParameters, ref bool fixed_any) + { + var types_to_fix = new List (tp_args); + for (int i = 0; i < methodParameters.Length; ++i) { + TypeSpec t = methodParameters[i]; + + if (!t.IsDelegate) { + if (!t.IsExpressionTreeType) + continue; + + t = TypeManager.GetTypeArguments (t) [0]; + } + + if (t.IsGenericParameter) + continue; + + var invoke = Delegate.GetInvokeMethod (t); + TypeSpec rtype = invoke.ReturnType; + while (rtype.IsArray) + rtype = ((ArrayContainer) rtype).Element; + + if (!rtype.IsGenericParameter && !TypeManager.IsGenericType (rtype)) + continue; + + // Remove dependent types, they cannot be fixed yet + RemoveDependentTypes (types_to_fix, rtype); + } + + foreach (TypeSpec t in types_to_fix) { + if (t == null) + continue; + + int idx = IsUnfixed (t); + if (idx >= 0 && !FixType (ec, idx)) { + return false; + } + } + + fixed_any = types_to_fix.Count > 0; + return true; + } + + // + // 26.3.3.10 Fixing + // + public bool FixType (ResolveContext ec, int i) + { + // It's already fixed + if (fixed_types[i] != null) + throw new InternalErrorException ("Type argument has been already fixed"); + + var candidates = bounds [i]; + if (candidates == null) + return false; + + if (candidates.Count == 1) { + TypeSpec t = candidates[0].Type; + if (t == InternalType.NullLiteral) + return false; + + fixed_types [i] = t; + return true; + } + + // + // The set of candidate types Uj starts out as the set of + // all types in the set of bounds for Xi + // + var applicable = new bool [candidates.Count]; + for (int ci = 0; ci < applicable.Length; ++ci) + applicable [ci] = true; + + for (int ci = 0; ci < applicable.Length; ++ci) { + var bound = candidates [ci]; + int cii = 0; + + switch (bound.Kind) { + case BoundKind.Exact: + for (; cii != applicable.Length; ++cii) { + if (ci == cii) + continue; + + if (!applicable[cii]) + break; + + // + // For each exact bound U of Xi all types Uj which are not identical + // to U are removed from the candidate set + // + if (candidates [cii].Type != bound.Type) + applicable[cii] = false; + } + + break; + case BoundKind.Lower: + for (; cii != applicable.Length; ++cii) { + if (ci == cii) + continue; + + if (!applicable[cii]) + break; + + // + // For each lower bound U of Xi all types Uj to which there is not an implicit conversion + // from U are removed from the candidate set + // + if (!Convert.ImplicitConversionExists (ec, bound.GetTypeExpression (), candidates [cii].Type)) { + applicable[cii] = false; + } + } + + break; + + case BoundKind.Upper: + for (; cii != applicable.Length; ++cii) { + if (ci == cii) + continue; + + if (!applicable[cii]) + break; + + // + // For each upper bound U of Xi all types Uj from which there is not an implicit conversion + // to U are removed from the candidate set + // + if (!Convert.ImplicitConversionExists (ec, candidates[cii].GetTypeExpression (), bound.Type)) + applicable[cii] = false; + } + + break; + } + } + + TypeSpec best_candidate = null; + for (int ci = 0; ci < applicable.Length; ++ci) { + if (!applicable[ci]) + continue; + + var bound = candidates [ci]; + if (bound.Type == best_candidate) + continue; + + int cii = 0; + for (; cii < applicable.Length; ++cii) { + if (ci == cii) + continue; + + if (!applicable[cii]) + continue; + + if (!Convert.ImplicitConversionExists (ec, candidates[cii].GetTypeExpression (), bound.Type)) + break; + } + + if (cii != applicable.Length) + continue; + + // + // We already have the best candidate, break if it's different (non-unique) + // + // Dynamic is never ambiguous as we prefer dynamic over other best candidate types + // + if (best_candidate != null) { + + if (best_candidate.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + continue; + + if (bound.Type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && best_candidate != bound.Type) + return false; + } + + best_candidate = bound.Type; + } + + if (best_candidate == null) + return false; + + fixed_types[i] = best_candidate; + return true; + } + + public bool HasBounds (int pos) + { + return bounds[pos] != null; + } + + // + // Uses inferred or partially infered types to inflate delegate type argument. Returns + // null when type parameter has not been fixed + // + public TypeSpec InflateGenericArgument (IModuleContext context, TypeSpec parameter) + { + var tp = parameter as TypeParameterSpec; + if (tp != null) { + // + // Type inference works on generic arguments (MVAR) only + // + if (!tp.IsMethodOwned) + return parameter; + + // + // Ensure the type parameter belongs to same container + // + if (tp.DeclaredPosition < tp_args.Length && tp_args[tp.DeclaredPosition] == parameter) + return fixed_types[tp.DeclaredPosition] ?? parameter; + + return parameter; + } + + var gt = parameter as InflatedTypeSpec; + if (gt != null) { + var inflated_targs = new TypeSpec [gt.TypeArguments.Length]; + for (int ii = 0; ii < inflated_targs.Length; ++ii) { + var inflated = InflateGenericArgument (context, gt.TypeArguments [ii]); + if (inflated == null) + return null; + + inflated_targs[ii] = inflated; + } + + return gt.GetDefinition ().MakeGenericType (context, inflated_targs); + } + + var ac = parameter as ArrayContainer; + if (ac != null) { + var inflated = InflateGenericArgument (context, ac.Element); + if (inflated != ac.Element) + return ArrayContainer.MakeType (context.Module, inflated); + } + + return parameter; + } + + // + // Tests whether all delegate input arguments are fixed and generic output type + // requires output type inference + // + public bool IsReturnTypeNonDependent (MethodSpec invoke, TypeSpec returnType) + { + AParametersCollection d_parameters = invoke.Parameters; + + if (d_parameters.IsEmpty) + return true; + + while (returnType.IsArray) + returnType = ((ArrayContainer) returnType).Element; + + if (returnType.IsGenericParameter) { + if (IsFixed (returnType)) + return false; + } else if (TypeManager.IsGenericType (returnType)) { + TypeSpec[] g_args = TypeManager.GetTypeArguments (returnType); + + // At least one unfixed return type has to exist + if (AllTypesAreFixed (g_args)) + return false; + } else { + return false; + } + + // All generic input arguments have to be fixed + return AllTypesAreFixed (d_parameters.Types); + } + + bool IsFixed (TypeSpec type) + { + return IsUnfixed (type) == -1; + } + + int IsUnfixed (TypeSpec type) + { + if (!type.IsGenericParameter) + return -1; + + for (int i = 0; i < tp_args.Length; ++i) { + if (tp_args[i] == type) { + if (fixed_types[i] != null) + break; + + return i; + } + } + + return -1; + } + + // + // 26.3.3.9 Lower-bound Inference + // + public int LowerBoundInference (TypeSpec u, TypeSpec v) + { + return LowerBoundInference (u, v, false); + } + + // + // Lower-bound (false) or Upper-bound (true) inference based on inversed argument + // + int LowerBoundInference (TypeSpec u, TypeSpec v, bool inversed) + { + // If V is one of the unfixed type arguments + int pos = IsUnfixed (v); + if (pos != -1) { + AddToBounds (new BoundInfo (u, inversed ? BoundKind.Upper : BoundKind.Lower), pos, false); + return 1; + } + + // If U is an array type + var u_ac = u as ArrayContainer; + if (u_ac != null) { + var v_ac = v as ArrayContainer; + if (v_ac != null) { + if (u_ac.Rank != v_ac.Rank) + return 0; + + if (TypeSpec.IsValueType (u_ac.Element)) + return ExactInference (u_ac.Element, v_ac.Element); + + return LowerBoundInference (u_ac.Element, v_ac.Element, inversed); + } + + if (u_ac.Rank != 1 || !v.IsArrayGenericInterface) + return 0; + + var v_i = TypeManager.GetTypeArguments (v) [0]; + if (TypeSpec.IsValueType (u_ac.Element)) + return ExactInference (u_ac.Element, v_i); + + return LowerBoundInference (u_ac.Element, v_i); + } + + if (v.IsGenericOrParentIsGeneric) { + // + // if V is a constructed type C and there is a unique type C + // such that U is identical to, inherits from (directly or indirectly), + // or implements (directly or indirectly) C + // + var u_candidates = new List (); + var open_v = v.MemberDefinition; + + for (TypeSpec t = u; t != null; t = t.BaseType) { + if (open_v == t.MemberDefinition) + u_candidates.Add (t); + + // + // Using this trick for dynamic type inference, the spec says the type arguments are "unknown" but + // that would complicate the process a lot, instead I treat them as dynamic + // + if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + u_candidates.Add (t); + } + + if (u.Interfaces != null) { + foreach (var iface in u.Interfaces) { + if (open_v == iface.MemberDefinition) + u_candidates.Add (iface); + } + } + + TypeSpec[] unique_candidate_targs = null; + var ga_v = TypeSpec.GetAllTypeArguments (v); + foreach (TypeSpec u_candidate in u_candidates) { + // + // The unique set of types U1..Uk means that if we have an interface I, + // class U : I, I then no type inference is made when inferring + // type I by applying type U because T could be int or long + // + if (unique_candidate_targs != null) { + TypeSpec[] second_unique_candidate_targs = TypeSpec.GetAllTypeArguments (u_candidate); + if (TypeSpecComparer.Equals (unique_candidate_targs, second_unique_candidate_targs)) { + unique_candidate_targs = second_unique_candidate_targs; + continue; + } + + // + // Break when candidate arguments are ambiguous + // + return 0; + } + + // + // A candidate is dynamic type expression, to simplify things use dynamic + // for all type parameter of this type. For methods like this one + // + // void M (IList, IList) + // + // dynamic becomes both T and U when the arguments are of dynamic type + // + if (u_candidate.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + unique_candidate_targs = new TypeSpec[ga_v.Length]; + for (int i = 0; i < unique_candidate_targs.Length; ++i) + unique_candidate_targs[i] = u_candidate; + } else { + unique_candidate_targs = TypeSpec.GetAllTypeArguments (u_candidate); + } + } + + if (unique_candidate_targs != null) { + int score = 0; + int tp_index = -1; + TypeParameterSpec[] tps = null; + + for (int i = 0; i < unique_candidate_targs.Length; ++i) { + if (tp_index < 0) { + while (v.Arity == 0) + v = v.DeclaringType; + + tps = v.MemberDefinition.TypeParameters; + tp_index = tps.Length - 1; + } + + Variance variance = tps [tp_index--].Variance; + + TypeSpec u_i = unique_candidate_targs [i]; + if (variance == Variance.None || TypeSpec.IsValueType (u_i)) { + if (ExactInference (u_i, ga_v [i]) == 0) + ++score; + } else { + bool upper_bound = (variance == Variance.Contravariant && !inversed) || + (variance == Variance.Covariant && inversed); + + if (LowerBoundInference (u_i, ga_v [i], upper_bound) == 0) + ++score; + } + } + + return score; + } + } + + return 0; + } + + // + // 26.3.3.6 Output Type Inference + // + public int OutputTypeInference (ResolveContext ec, Expression e, TypeSpec t) + { + // If e is a lambda or anonymous method with inferred return type + AnonymousMethodExpression ame = e as AnonymousMethodExpression; + if (ame != null) { + TypeSpec rt = ame.InferReturnType (ec, this, t); + var invoke = Delegate.GetInvokeMethod (t); + + if (rt == null) { + AParametersCollection pd = invoke.Parameters; + return ame.Parameters.Count == pd.Count ? 1 : 0; + } + + TypeSpec rtype = invoke.ReturnType; + return LowerBoundInference (rt, rtype) + 1; + } + + // + // if E is a method group and T is a delegate type or expression tree type + // return type Tb with parameter types T1..Tk and return type Tb, and overload + // resolution of E with the types T1..Tk yields a single method with return type U, + // then a lower-bound inference is made from U for Tb. + // + if (e is MethodGroupExpr) { + if (!t.IsDelegate) { + if (!t.IsExpressionTreeType) + return 0; + + t = TypeManager.GetTypeArguments (t)[0]; + } + + var invoke = Delegate.GetInvokeMethod (t); + TypeSpec rtype = invoke.ReturnType; + + if (!IsReturnTypeNonDependent (invoke, rtype)) + return 0; + + // LAMESPEC: Standard does not specify that all methodgroup arguments + // has to be fixed but it does not specify how to do recursive type inference + // either. We choose the simple option and infer return type only + // if all delegate generic arguments are fixed. + TypeSpec[] param_types = new TypeSpec [invoke.Parameters.Count]; + for (int i = 0; i < param_types.Length; ++i) { + var inflated = InflateGenericArgument (ec, invoke.Parameters.Types[i]); + if (inflated == null) + return 0; + + param_types[i] = inflated; + } + + MethodGroupExpr mg = (MethodGroupExpr) e; + Arguments args = DelegateCreation.CreateDelegateMethodArguments (ec, invoke.Parameters, param_types, e.Location); + mg = mg.OverloadResolve (ec, ref args, null, OverloadResolver.Restrictions.CovariantDelegate | OverloadResolver.Restrictions.ProbingOnly); + if (mg == null) + return 0; + + return LowerBoundInference (mg.BestCandidateReturnType, rtype) + 1; + } + + // + // if e is an expression with type U, then + // a lower-bound inference is made from U for T + // + return LowerBoundInference (e.Type, t) * 2; + } + + void RemoveDependentTypes (List types, TypeSpec returnType) + { + int idx = IsUnfixed (returnType); + if (idx >= 0) { + types [idx] = null; + return; + } + + if (TypeManager.IsGenericType (returnType)) { + foreach (TypeSpec t in TypeManager.GetTypeArguments (returnType)) { + RemoveDependentTypes (types, t); + } + } + } + + public bool UnfixedVariableExists { + get { + foreach (TypeSpec ut in fixed_types) { + if (ut == null) + return true; + } + + return false; + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/import.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/import.cs new file mode 100644 index 000000000..6dea50a33 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/import.cs @@ -0,0 +1,2352 @@ +// +// import.cs: System.Reflection conversions +// +// Authors: Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2009-2011 Novell, Inc +// Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com) +// + +using System; +using System.Runtime.CompilerServices; +using System.Linq; +using System.Collections.Generic; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public abstract class MetadataImporter + { + // + // Dynamic types reader with additional logic to reconstruct a dynamic + // type using DynamicAttribute values + // + protected struct DynamicTypeReader + { + static readonly bool[] single_attribute = { true }; + + public int Position; + bool[] flags; + + // There is no common type for CustomAttributeData and we cannot + // use ICustomAttributeProvider + object provider; + + // + // A member provider which can be used to get CustomAttributeData + // + public DynamicTypeReader (object provider) + { + Position = 0; + flags = null; + this.provider = provider; + } + + // + // Returns true when object at local position has dynamic attribute flag + // + public bool IsDynamicObject () + { + if (provider != null) + ReadAttribute (); + + return flags != null && Position < flags.Length && flags[Position]; + } + + // + // Returns true when DynamicAttribute exists + // + public bool HasDynamicAttribute () + { + if (provider != null) + ReadAttribute (); + + return flags != null; + } + + IList GetCustomAttributes () + { + var mi = provider as MemberInfo; + if (mi != null) + return CustomAttributeData.GetCustomAttributes (mi); + + var pi = provider as ParameterInfo; + if (pi != null) + return CustomAttributeData.GetCustomAttributes (pi); + + provider = null; + return null; + } + + void ReadAttribute () + { + var cad = GetCustomAttributes (); + if (cad == null) { + return; + } + + if (cad.Count > 0) { + foreach (var ca in cad) { + var dt = ca.Constructor.DeclaringType; + if (dt.Name != "DynamicAttribute" || dt.Namespace != CompilerServicesNamespace) + continue; + + if (ca.ConstructorArguments.Count == 0) { + flags = single_attribute; + break; + } + + var arg_type = ca.ConstructorArguments[0].ArgumentType; + + if (arg_type.IsArray && MetaType.GetTypeCode (arg_type.GetElementType ()) == TypeCode.Boolean) { + var carg = (IList) ca.ConstructorArguments[0].Value; + flags = new bool[carg.Count]; + for (int i = 0; i < flags.Length; ++i) { + if (MetaType.GetTypeCode (carg[i].ArgumentType) == TypeCode.Boolean) + flags[i] = (bool) carg[i].Value; + } + + break; + } + } + } + + provider = null; + } + } + + protected readonly Dictionary import_cache; + protected readonly Dictionary compiled_types; + protected readonly Dictionary assembly_2_definition; + protected readonly ModuleContainer module; + + public static readonly string CompilerServicesNamespace = "System.Runtime.CompilerServices"; + + protected MetadataImporter (ModuleContainer module) + { + this.module = module; + + import_cache = new Dictionary (1024, ReferenceEquality.Default); + compiled_types = new Dictionary (40, ReferenceEquality.Default); + assembly_2_definition = new Dictionary (ReferenceEquality.Default); + IgnorePrivateMembers = true; + } + + #region Properties + + public ICollection Assemblies { + get { + return assembly_2_definition.Values; + } + } + + public bool IgnorePrivateMembers { get; set; } + + #endregion + + public abstract void AddCompiledType (TypeBuilder builder, TypeSpec spec); + protected abstract MemberKind DetermineKindFromBaseType (MetaType baseType); + protected abstract bool HasVolatileModifier (MetaType[] modifiers); + + public FieldSpec CreateField (FieldInfo fi, TypeSpec declaringType) + { + Modifiers mod; + var fa = fi.Attributes; + switch (fa & FieldAttributes.FieldAccessMask) { + case FieldAttributes.Public: + mod = Modifiers.PUBLIC; + break; + case FieldAttributes.Assembly: + mod = Modifiers.INTERNAL; + break; + case FieldAttributes.Family: + mod = Modifiers.PROTECTED; + break; + case FieldAttributes.FamORAssem: + mod = Modifiers.PROTECTED | Modifiers.INTERNAL; + break; + default: + // Ignore private fields (even for error reporting) to not require extra dependencies + if ((IgnorePrivateMembers && !declaringType.IsStruct) || + HasAttribute (CustomAttributeData.GetCustomAttributes (fi), "CompilerGeneratedAttribute", CompilerServicesNamespace)) + return null; + + mod = Modifiers.PRIVATE; + break; + } + + TypeSpec field_type; + + try { + field_type = ImportType (fi.FieldType, new DynamicTypeReader (fi)); + + // + // Private field has private type which is not fixed buffer + // + if (field_type == null) + return null; + } catch (Exception e) { + // TODO: I should construct fake TypeSpec based on TypeRef signature + // but there is no way to do it with System.Reflection + throw new InternalErrorException (e, "Cannot import field `{0}.{1}' referenced in assembly `{2}'", + declaringType.GetSignatureForError (), fi.Name, declaringType.MemberDefinition.DeclaringAssembly); + } + + var definition = new ImportedMemberDefinition (fi, field_type, this); + + if ((fa & FieldAttributes.Literal) != 0) { + Constant c = field_type.Kind == MemberKind.MissingType ? + new NullConstant (InternalType.ErrorType, Location.Null) : + Constant.CreateConstantFromValue (field_type, fi.GetRawConstantValue (), Location.Null); + return new ConstSpec (declaringType, definition, field_type, fi, mod, c); + } + + if ((fa & FieldAttributes.InitOnly) != 0) { + if (field_type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + var dc = ReadDecimalConstant (CustomAttributeData.GetCustomAttributes (fi)); + if (dc != null) + return new ConstSpec (declaringType, definition, field_type, fi, mod, dc); + } + + mod |= Modifiers.READONLY; + } else { + var req_mod = fi.GetRequiredCustomModifiers (); + if (req_mod.Length > 0 && HasVolatileModifier (req_mod)) + mod |= Modifiers.VOLATILE; + } + + if ((fa & FieldAttributes.Static) != 0) { + mod |= Modifiers.STATIC; + } else { + // Fixed buffers cannot be static + if (declaringType.IsStruct && field_type.IsStruct && field_type.IsNested && + HasAttribute (CustomAttributeData.GetCustomAttributes (fi), "FixedBufferAttribute", CompilerServicesNamespace)) { + + // TODO: Sanity check on field_type (only few types are allowed) + var element_field = CreateField (fi.FieldType.GetField (FixedField.FixedElementName), declaringType); + return new FixedFieldSpec (module, declaringType, definition, fi, element_field, mod); + } + } + + return new FieldSpec (declaringType, definition, field_type, fi, mod); + } + + public EventSpec CreateEvent (EventInfo ei, TypeSpec declaringType, MethodSpec add, MethodSpec remove) + { + add.IsAccessor = true; + remove.IsAccessor = true; + + if (add.Modifiers != remove.Modifiers) + throw new NotImplementedException ("Different accessor modifiers " + ei.Name); + + var event_type = ImportType (ei.EventHandlerType, new DynamicTypeReader (ei)); + var definition = new ImportedMemberDefinition (ei, event_type, this); + return new EventSpec (declaringType, definition, event_type, add.Modifiers, add, remove); + } + + TypeParameterSpec[] CreateGenericParameters (MetaType type, TypeSpec declaringType) + { + var tparams = type.GetGenericArguments (); + + int parent_owned_count; + if (type.IsNested) { + parent_owned_count = type.DeclaringType.GetGenericArguments ().Length; + + // + // System.Reflection duplicates parent type parameters for each + // nested type with slightly modified properties (eg. different owner) + // This just makes things more complicated (think of cloned constraints) + // therefore we remap any nested type owned by parent using `type_cache' + // to the single TypeParameterSpec + // + if (declaringType != null && parent_owned_count > 0) { + int read_count = 0; + while (read_count != parent_owned_count) { + var tparams_count = declaringType.Arity; + if (tparams_count != 0) { + var parent_tp = declaringType.MemberDefinition.TypeParameters; + read_count += tparams_count; + for (int i = 0; i < tparams_count; i++) { + import_cache.Add (tparams[parent_owned_count - read_count + i], parent_tp[i]); + } + } + + declaringType = declaringType.DeclaringType; + } + } + } else { + parent_owned_count = 0; + } + + if (tparams.Length - parent_owned_count == 0) + return null; + + return CreateGenericParameters (parent_owned_count, tparams); + } + + TypeParameterSpec[] CreateGenericParameters (int first, MetaType[] tparams) + { + var tspec = new TypeParameterSpec[tparams.Length - first]; + for (int pos = first; pos < tparams.Length; ++pos) { + var type = tparams[pos]; + int index = pos - first; + + tspec[index] = (TypeParameterSpec) CreateType (type, new DynamicTypeReader (), false); + } + + return tspec; + } + + TypeSpec[] CreateGenericArguments (int first, MetaType[] tparams, DynamicTypeReader dtype) + { + ++dtype.Position; + + var tspec = new TypeSpec [tparams.Length - first]; + for (int pos = first; pos < tparams.Length; ++pos) { + var type = tparams[pos]; + int index = pos - first; + + TypeSpec spec; + if (type.HasElementType) { + var element = type.GetElementType (); + ++dtype.Position; + spec = ImportType (element, dtype); + + if (!type.IsArray) { + throw new NotImplementedException ("Unknown element type " + type.ToString ()); + } + + spec = ArrayContainer.MakeType (module, spec, type.GetArrayRank ()); + } else { + spec = CreateType (type, dtype, true); + + // + // We treat nested generic types as inflated internally where + // reflection uses type definition + // + // class A { + // IFoo> foo; // A is definition in this case + // } + // + if (!IsMissingType (type) && type.IsGenericTypeDefinition) { + var start_pos = spec.DeclaringType == null ? 0 : spec.DeclaringType.MemberDefinition.TypeParametersCount; + var targs = CreateGenericArguments (start_pos, type.GetGenericArguments (), dtype); + spec = spec.MakeGenericType (module, targs); + } + } + + if (spec == null) + return null; + + ++dtype.Position; + tspec[index] = spec; + } + + return tspec; + } + + public MethodSpec CreateMethod (MethodBase mb, TypeSpec declaringType) + { + Modifiers mod = ReadMethodModifiers (mb, declaringType); + TypeParameterSpec[] tparams; + + var parameters = CreateParameters (declaringType, mb.GetParameters (), mb); + + if (mb.IsGenericMethod) { + if (!mb.IsGenericMethodDefinition) + throw new NotSupportedException ("assert"); + + tparams = CreateGenericParameters (0, mb.GetGenericArguments ()); + } else { + tparams = null; + } + + MemberKind kind; + TypeSpec returnType; + if (mb.MemberType == MemberTypes.Constructor) { + kind = MemberKind.Constructor; + returnType = module.Compiler.BuiltinTypes.Void; + } else { + // + // Detect operators and destructors + // + string name = mb.Name; + kind = MemberKind.Method; + if (tparams == null && !mb.DeclaringType.IsInterface && name.Length > 6) { + if ((mod & (Modifiers.STATIC | Modifiers.PUBLIC)) == (Modifiers.STATIC | Modifiers.PUBLIC)) { + if (name[2] == '_' && name[1] == 'p' && name[0] == 'o' && (mb.Attributes & MethodAttributes.SpecialName) != 0) { + var op_type = Operator.GetType (name); + if (op_type.HasValue && parameters.Count > 0 && parameters.Count < 3) { + kind = MemberKind.Operator; + } + } + } else if (parameters.IsEmpty && name == Destructor.MetadataName) { + kind = MemberKind.Destructor; + if (declaringType.BuiltinType == BuiltinTypeSpec.Type.Object) { + mod &= ~Modifiers.OVERRIDE; + mod |= Modifiers.VIRTUAL; + } + } + } + + var mi = (MethodInfo) mb; + returnType = ImportType (mi.ReturnType, new DynamicTypeReader (mi.ReturnParameter)); + + // Cannot set to OVERRIDE without full hierarchy checks + // this flag indicates that the method could be override + // but further validation is needed + if ((mod & Modifiers.OVERRIDE) != 0) { + bool is_real_override = false; + if (kind == MemberKind.Method && declaringType.BaseType != null) { + var btype = declaringType.BaseType; + if (IsOverrideMethodBaseTypeAccessible (btype)) { + var filter = MemberFilter.Method (name, tparams != null ? tparams.Length : 0, parameters, null); + var candidate = MemberCache.FindMember (btype, filter, BindingRestriction.None); + + // + // For imported class method do additional validation to be sure that metadata + // override flag was correct + // + // Difference between protected internal and protected is ok + // + const Modifiers conflict_mask = Modifiers.AccessibilityMask & ~Modifiers.INTERNAL; + if (candidate != null && (candidate.Modifiers & conflict_mask) == (mod & conflict_mask) && !candidate.IsStatic) { + is_real_override = true; + } + } + } + + if (!is_real_override) { + mod &= ~Modifiers.OVERRIDE; + if ((mod & Modifiers.SEALED) != 0) + mod &= ~Modifiers.SEALED; + else + mod |= Modifiers.VIRTUAL; + } + } else if (parameters.HasExtensionMethodType) { + mod |= Modifiers.METHOD_EXTENSION; + } + } + + IMethodDefinition definition; + if (tparams != null) { + var gmd = new ImportedGenericMethodDefinition ((MethodInfo) mb, returnType, parameters, tparams, this); + foreach (var tp in gmd.TypeParameters) { + ImportTypeParameterTypeConstraints (tp, tp.GetMetaInfo ()); + } + + definition = gmd; + } else { + definition = new ImportedMethodDefinition (mb, returnType, parameters, this); + } + + MethodSpec ms = new MethodSpec (kind, declaringType, definition, returnType, parameters, mod); + if (tparams != null) + ms.IsGeneric = true; + + return ms; + } + + bool IsOverrideMethodBaseTypeAccessible (TypeSpec baseType) + { + switch (baseType.Modifiers & Modifiers.AccessibilityMask) { + case Modifiers.PUBLIC: + return true; + case Modifiers.INTERNAL: + // + // Check whether imported method in base type is accessible from compiled + // context + // + return baseType.MemberDefinition.IsInternalAsPublic (module.DeclaringAssembly); + case Modifiers.PRIVATE: + return false; + default: + // protected + // protected internal + // + // Method accessibility checks will be done later based on context + // where the method is called (CS0122 error will be reported for inaccessible) + // + return true; + } + } + + // + // Imports System.Reflection parameters + // + AParametersCollection CreateParameters (TypeSpec parent, ParameterInfo[] pi, MethodBase method) + { + int varargs = method != null && (method.CallingConvention & CallingConventions.VarArgs) != 0 ? 1 : 0; + + if (pi.Length == 0 && varargs == 0) + return ParametersCompiled.EmptyReadOnlyParameters; + + TypeSpec[] types = new TypeSpec[pi.Length + varargs]; + IParameterData[] par = new IParameterData[pi.Length + varargs]; + bool is_params = false; + for (int i = 0; i < pi.Length; i++) { + ParameterInfo p = pi[i]; + Parameter.Modifier mod = 0; + Expression default_value = null; + if (p.ParameterType.IsByRef) { + if ((p.Attributes & (ParameterAttributes.Out | ParameterAttributes.In)) == ParameterAttributes.Out) + mod = Parameter.Modifier.OUT; + else + mod = Parameter.Modifier.REF; + + // + // Strip reference wrapping + // + var el = p.ParameterType.GetElementType (); + types[i] = ImportType (el, new DynamicTypeReader (p)); // TODO: 1-based positio to be csc compatible + } else if (i == 0 && method.IsStatic && (parent.Modifiers & Modifiers.METHOD_EXTENSION) != 0 && + HasAttribute (CustomAttributeData.GetCustomAttributes (method), "ExtensionAttribute", CompilerServicesNamespace)) { + mod = Parameter.Modifier.This; + types[i] = ImportType (p.ParameterType, new DynamicTypeReader (p)); + } else { + types[i] = ImportType (p.ParameterType, new DynamicTypeReader (p)); + + if (i >= pi.Length - 2 && types[i] is ArrayContainer) { + if (HasAttribute (CustomAttributeData.GetCustomAttributes (p), "ParamArrayAttribute", "System")) { + mod = Parameter.Modifier.PARAMS; + is_params = true; + } + } + + if (!is_params && p.IsOptional) { + object value = p.RawDefaultValue; + var ptype = types[i]; + if ((p.Attributes & ParameterAttributes.HasDefault) != 0 && ptype.Kind != MemberKind.TypeParameter && (value != null || TypeSpec.IsReferenceType (ptype))) { + if (value == null) { + default_value = Constant.CreateConstantFromValue (ptype, null, Location.Null); + } else { + default_value = ImportParameterConstant (value); + + if (ptype.IsEnum) { + default_value = new EnumConstant ((Constant) default_value, ptype); + } + } + + var attrs = CustomAttributeData.GetCustomAttributes (p); + for (int ii = 0; ii < attrs.Count; ++ii) { + var attr = attrs[ii]; + var dt = attr.Constructor.DeclaringType; + if (dt.Namespace != CompilerServicesNamespace) + continue; + + if (dt.Name == "CallerLineNumberAttribute" && (ptype.BuiltinType == BuiltinTypeSpec.Type.Int || Convert.ImplicitNumericConversionExists (module.Compiler.BuiltinTypes.Int, ptype))) + mod |= Parameter.Modifier.CallerLineNumber; + else if (dt.Name == "CallerFilePathAttribute" && Convert.ImplicitReferenceConversionExists (module.Compiler.BuiltinTypes.String, ptype)) + mod |= Parameter.Modifier.CallerFilePath; + else if (dt.Name == "CallerMemberNameAttribute" && Convert.ImplicitReferenceConversionExists (module.Compiler.BuiltinTypes.String, ptype)) + mod |= Parameter.Modifier.CallerMemberName; + } + } else if (value == Missing.Value) { + default_value = EmptyExpression.MissingValue; + } else if (value == null) { + default_value = new DefaultValueExpression (new TypeExpression (ptype, Location.Null), Location.Null); + } else if (ptype.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + default_value = ImportParameterConstant (value); + } + } + } + + par[i] = new ParameterData (p.Name, mod, default_value); + } + + if (varargs != 0) { + par[par.Length - 1] = new ArglistParameter (Location.Null); + types[types.Length - 1] = InternalType.Arglist; + } + + return method != null ? + new ParametersImported (par, types, varargs != 0, is_params) : + new ParametersImported (par, types, is_params); + } + + // + // Returns null when the property is not valid C# property + // + public PropertySpec CreateProperty (PropertyInfo pi, TypeSpec declaringType, MethodSpec get, MethodSpec set) + { + Modifiers mod = 0; + AParametersCollection param = null; + TypeSpec type = null; + if (get != null) { + mod = get.Modifiers; + param = get.Parameters; + type = get.ReturnType; + } + + bool is_valid_property = true; + if (set != null) { + if (set.ReturnType.Kind != MemberKind.Void) + is_valid_property = false; + + var set_param_count = set.Parameters.Count - 1; + + if (set_param_count < 0) { + set_param_count = 0; + is_valid_property = false; + } + + var set_type = set.Parameters.Types[set_param_count]; + + if (mod == 0) { + AParametersCollection set_based_param; + + if (set_param_count == 0) { + set_based_param = ParametersCompiled.EmptyReadOnlyParameters; + } else { + // + // Create indexer parameters based on setter method parameters (the last parameter has to be removed) + // + var data = new IParameterData[set_param_count]; + var types = new TypeSpec[set_param_count]; + Array.Copy (set.Parameters.FixedParameters, data, set_param_count); + Array.Copy (set.Parameters.Types, types, set_param_count); + set_based_param = new ParametersImported (data, types, set.Parameters.HasParams); + } + + mod = set.Modifiers; + param = set_based_param; + type = set_type; + } else { + if (set_param_count != get.Parameters.Count) + is_valid_property = false; + + if (get.ReturnType != set_type) + is_valid_property = false; + + // Possible custom accessor modifiers + if ((mod & Modifiers.AccessibilityMask) != (set.Modifiers & Modifiers.AccessibilityMask)) { + var get_acc = mod & Modifiers.AccessibilityMask; + if (get_acc != Modifiers.PUBLIC) { + var set_acc = set.Modifiers & Modifiers.AccessibilityMask; + // If the accessor modifiers are not same, do extra restriction checks + if (get_acc != set_acc) { + var get_restr = ModifiersExtensions.IsRestrictedModifier (get_acc, set_acc); + var set_restr = ModifiersExtensions.IsRestrictedModifier (set_acc, get_acc); + if (get_restr && set_restr) { + is_valid_property = false; // Neither is more restrictive + } + + if (get_restr) { + mod &= ~Modifiers.AccessibilityMask; + mod |= set_acc; + } + } + } + } + } + } + + PropertySpec spec = null; + if (!param.IsEmpty) { + if (is_valid_property) { + var index_name = declaringType.MemberDefinition.GetAttributeDefaultMember (); + if (index_name == null) { + is_valid_property = false; + } else { + if (get != null) { + if (get.IsStatic) + is_valid_property = false; + if (get.Name.IndexOf (index_name, StringComparison.Ordinal) != 4) + is_valid_property = false; + } + if (set != null) { + if (set.IsStatic) + is_valid_property = false; + if (set.Name.IndexOf (index_name, StringComparison.Ordinal) != 4) + is_valid_property = false; + } + } + + if (is_valid_property) { + spec = new IndexerSpec (declaringType, new ImportedParameterMemberDefinition (pi, type, param, this), type, param, pi, mod); + } else if (declaringType.MemberDefinition.IsComImport && param.FixedParameters[0].HasDefaultValue) { + // + // Enables support for properties with parameters (must have default value) of COM-imported types + // + is_valid_property = true; + + for (int i = 0; i < param.FixedParameters.Length; ++i) { + if (!param.FixedParameters[i].HasDefaultValue) { + is_valid_property = false; + break; + } + } + } + } + } + + if (spec == null) + spec = new PropertySpec (MemberKind.Property, declaringType, new ImportedMemberDefinition (pi, type, this), type, pi, mod); + + if (!is_valid_property) { + spec.IsNotCSharpCompatible = true; + return spec; + } + + if (set != null) + spec.Set = set; + if (get != null) + spec.Get = get; + + return spec; + } + + public TypeSpec CreateType (MetaType type) + { + return CreateType (type, new DynamicTypeReader (), true); + } + + public TypeSpec CreateNestedType (MetaType type, TypeSpec declaringType) + { + return CreateType (type, declaringType, new DynamicTypeReader (type), false); + } + + TypeSpec CreateType (MetaType type, DynamicTypeReader dtype, bool canImportBaseType) + { + TypeSpec declaring_type; + if (type.IsNested && !type.IsGenericParameter) + declaring_type = CreateType (type.DeclaringType, new DynamicTypeReader (type.DeclaringType), true); + else + declaring_type = null; + + return CreateType (type, declaring_type, dtype, canImportBaseType); + } + + protected TypeSpec CreateType (MetaType type, TypeSpec declaringType, DynamicTypeReader dtype, bool canImportBaseType) + { + TypeSpec spec; + if (import_cache.TryGetValue (type, out spec)) { + if (spec.BuiltinType == BuiltinTypeSpec.Type.Object) { + if (dtype.IsDynamicObject ()) + return module.Compiler.BuiltinTypes.Dynamic; + + return spec; + } + + if (!spec.IsGeneric || type.IsGenericTypeDefinition) + return spec; + + if (!dtype.HasDynamicAttribute ()) + return spec; + + // We've found same object in the cache but this one has a dynamic custom attribute + // and it's most likely dynamic version of same type IFoo agains IFoo + // Do type resolve process again in that case + + // TODO: Handle cases where they still unify + } + + if (IsMissingType (type)) { + spec = new TypeSpec (MemberKind.MissingType, declaringType, new ImportedTypeDefinition (type, this), type, Modifiers.PUBLIC); + spec.MemberCache = MemberCache.Empty; + import_cache.Add (type, spec); + return spec; + } + + if (type.IsGenericType && !type.IsGenericTypeDefinition) { + var type_def = type.GetGenericTypeDefinition (); + + // Generic type definition can also be forwarded + if (compiled_types.TryGetValue (type_def, out spec)) + return spec; + + var targs = CreateGenericArguments (0, type.GetGenericArguments (), dtype); + if (targs == null) + return null; + if (declaringType == null) { + // Simple case, no nesting + spec = CreateType (type_def, null, new DynamicTypeReader (), canImportBaseType); + spec = spec.MakeGenericType (module, targs); + } else { + // + // Nested type case, converting .NET types like + // A`1.B`1.C`1 to typespec like + // A.B.C + // + var nested_hierarchy = new List (); + while (declaringType.IsNested) { + nested_hierarchy.Add (declaringType); + declaringType = declaringType.DeclaringType; + } + + int targs_pos = 0; + if (declaringType.Arity > 0) { + spec = declaringType.MakeGenericType (module, targs.Skip (targs_pos).Take (declaringType.Arity).ToArray ()); + targs_pos = spec.Arity; + } else { + spec = declaringType; + } + + for (int i = nested_hierarchy.Count; i != 0; --i) { + var t = nested_hierarchy [i - 1]; + if (t.Kind == MemberKind.MissingType) + spec = t; + else + spec = MemberCache.FindNestedType (spec, t.Name, t.Arity); + + if (t.Arity > 0) { + spec = spec.MakeGenericType (module, targs.Skip (targs_pos).Take (spec.Arity).ToArray ()); + targs_pos += t.Arity; + } + } + + if (spec.Kind == MemberKind.MissingType) { + spec = new TypeSpec (MemberKind.MissingType, spec, new ImportedTypeDefinition (type_def, this), type_def, Modifiers.PUBLIC); + spec.MemberCache = MemberCache.Empty; + } else { + if ((type_def.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate && IgnorePrivateMembers) + return null; + + string name = type.Name; + int index = name.IndexOf ('`'); + if (index > 0) + name = name.Substring (0, index); + + spec = MemberCache.FindNestedType (spec, name, targs.Length - targs_pos); + + if (spec.Arity > 0) { + spec = spec.MakeGenericType (module, targs.Skip (targs_pos).ToArray ()); + } + } + } + + // Don't add generic type with dynamic arguments, they can interfere with same type + // using object type arguments + if (!spec.HasDynamicElement) { + + // Add to reading cache to speed up reading + if (!import_cache.ContainsKey (type)) + import_cache.Add (type, spec); + } + + return spec; + } + + Modifiers mod; + MemberKind kind; + + var ma = type.Attributes; + switch (ma & TypeAttributes.VisibilityMask) { + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + mod = Modifiers.PUBLIC; + break; + case TypeAttributes.NestedPrivate: + mod = Modifiers.PRIVATE; + break; + case TypeAttributes.NestedFamily: + mod = Modifiers.PROTECTED; + break; + case TypeAttributes.NestedFamORAssem: + mod = Modifiers.PROTECTED | Modifiers.INTERNAL; + break; + default: + mod = Modifiers.INTERNAL; + break; + } + + if ((ma & TypeAttributes.Interface) != 0) { + kind = MemberKind.Interface; + } else if (type.IsGenericParameter) { + kind = MemberKind.TypeParameter; + } else { + var base_type = type.BaseType; + if (base_type == null || (ma & TypeAttributes.Abstract) != 0) { + kind = MemberKind.Class; + } else { + kind = DetermineKindFromBaseType (base_type); + if (kind == MemberKind.Struct || kind == MemberKind.Delegate) { + mod |= Modifiers.SEALED; + } + } + + if (kind == MemberKind.Class) { + if ((ma & TypeAttributes.Sealed) != 0) { + if ((ma & TypeAttributes.Abstract) != 0) + mod |= Modifiers.STATIC; + else + mod |= Modifiers.SEALED; + } else if ((ma & TypeAttributes.Abstract) != 0) { + mod |= Modifiers.ABSTRACT; + } + } + } + + var definition = new ImportedTypeDefinition (type, this); + TypeSpec pt; + + if (kind == MemberKind.Enum) { + const BindingFlags underlying_member = BindingFlags.DeclaredOnly | + BindingFlags.Instance | + BindingFlags.Public | BindingFlags.NonPublic; + + var type_members = type.GetFields (underlying_member); + foreach (var type_member in type_members) { + spec = new EnumSpec (declaringType, definition, CreateType (type_member.FieldType), type, mod); + break; + } + + if (spec == null) + kind = MemberKind.Class; + + } else if (kind == MemberKind.TypeParameter) { + spec = CreateTypeParameter (type, declaringType); + } else if (type.IsGenericTypeDefinition) { + definition.TypeParameters = CreateGenericParameters (type, declaringType); + } else if (compiled_types.TryGetValue (type, out pt)) { + // + // Same type was found in inside compiled types. It's + // either build-in type or forward referenced typed + // which point into just compiled assembly. + // + spec = pt; + BuiltinTypeSpec bts = pt as BuiltinTypeSpec; + if (bts != null) + bts.SetDefinition (definition, type, mod); + } + + if (spec == null) + spec = new TypeSpec (kind, declaringType, definition, type, mod); + + import_cache.Add (type, spec); + + if (kind == MemberKind.TypeParameter) { + if (canImportBaseType) + ImportTypeParameterTypeConstraints ((TypeParameterSpec) spec, type); + + return spec; + } + + // + // Two stage setup as the base type can be inflated declaring type or + // another nested type inside same declaring type which has not been + // loaded, therefore we can import a base type of nested types once + // the types have been imported + // + if (canImportBaseType) + ImportTypeBase (spec, type); + + return spec; + } + + public IAssemblyDefinition GetAssemblyDefinition (Assembly assembly) + { + IAssemblyDefinition found; + if (!assembly_2_definition.TryGetValue (assembly, out found)) { + + // This can happen in dynamic context only + var def = new ImportedAssemblyDefinition (assembly); + assembly_2_definition.Add (assembly, def); + def.ReadAttributes (); + found = def; + } + + return found; + } + + public void ImportTypeBase (MetaType type) + { + TypeSpec spec = import_cache[type]; + if (spec != null) + ImportTypeBase (spec, type); + } + + TypeParameterSpec CreateTypeParameter (MetaType type, TypeSpec declaringType) + { + Variance variance; + switch (type.GenericParameterAttributes & GenericParameterAttributes.VarianceMask) { + case GenericParameterAttributes.Covariant: + variance = Variance.Covariant; + break; + case GenericParameterAttributes.Contravariant: + variance = Variance.Contravariant; + break; + default: + variance = Variance.None; + break; + } + + SpecialConstraint special = SpecialConstraint.None; + var import_special = type.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask; + + if ((import_special & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) { + special |= SpecialConstraint.Struct; + } else if ((import_special & GenericParameterAttributes.DefaultConstructorConstraint) != 0) { + special = SpecialConstraint.Constructor; + } + + if ((import_special & GenericParameterAttributes.ReferenceTypeConstraint) != 0) { + special |= SpecialConstraint.Class; + } + + TypeParameterSpec spec; + var def = new ImportedTypeParameterDefinition (type, this); + if (type.DeclaringMethod != null) { + spec = new TypeParameterSpec (type.GenericParameterPosition, def, special, variance, type); + } else { + spec = new TypeParameterSpec (declaringType, type.GenericParameterPosition, def, special, variance, type); + } + + return spec; + } + + // + // Test for a custom attribute type match. Custom attributes are not really predefined globaly + // they can be assembly specific therefore we do check based on names only + // + public static bool HasAttribute (IList attributesData, string attrName, string attrNamespace) + { + if (attributesData.Count == 0) + return false; + + foreach (var attr in attributesData) { + var dt = attr.Constructor.DeclaringType; + if (dt.Name == attrName && dt.Namespace == attrNamespace) + return true; + } + + return false; + } + + void ImportTypeBase (TypeSpec spec, MetaType type) + { + if (spec.Kind == MemberKind.Interface) + spec.BaseType = module.Compiler.BuiltinTypes.Object; + else if (type.BaseType != null) { + TypeSpec base_type; + if (!IsMissingType (type.BaseType) && type.BaseType.IsGenericType) + base_type = CreateType (type.BaseType, new DynamicTypeReader (type), true); + else + base_type = CreateType (type.BaseType); + + spec.BaseType = base_type; + } + + if (spec.MemberDefinition.TypeParametersCount > 0) { + foreach (var tp in spec.MemberDefinition.TypeParameters) { + ImportTypeParameterTypeConstraints (tp, tp.GetMetaInfo ()); + } + } + } + + protected void ImportTypes (MetaType[] types, Namespace targetNamespace, bool importExtensionTypes) + { + Namespace ns = targetNamespace; + string prev_namespace = null; + foreach (var t in types) { + if (t == null) + continue; + + // Be careful not to trigger full parent type loading + if (t.MemberType == MemberTypes.NestedType) + continue; + + if (t.Name[0] == '<') + continue; + + var it = CreateType (t, null, new DynamicTypeReader (t), true); + if (it == null) + continue; + + if (prev_namespace != t.Namespace) { + ns = t.Namespace == null ? targetNamespace : targetNamespace.GetNamespace (t.Namespace, true); + prev_namespace = t.Namespace; + } + + // Cannot rely on assembly level Extension attribute or static modifier because they + // are not followed by other compilers (e.g. F#). + if (it.IsClass && it.Arity == 0 && importExtensionTypes && + HasAttribute (CustomAttributeData.GetCustomAttributes (t), "ExtensionAttribute", CompilerServicesNamespace)) { + it.SetExtensionMethodContainer (); + } + + ns.AddType (module, it); + } + } + + void ImportTypeParameterTypeConstraints (TypeParameterSpec spec, MetaType type) + { + var constraints = type.GetGenericParameterConstraints (); + List tparams = null; + foreach (var ct in constraints) { + if (ct.IsGenericParameter) { + if (tparams == null) + tparams = new List (); + + tparams.Add (CreateType (ct)); + continue; + } + + var constraint_type = CreateType (ct); + if (constraint_type.IsClass) { + spec.BaseType = constraint_type; + continue; + } + + spec.AddInterface (constraint_type); + } + + if (spec.BaseType == null) + spec.BaseType = module.Compiler.BuiltinTypes.Object; + + if (tparams != null) + spec.TypeArguments = tparams.ToArray (); + } + + Constant ImportParameterConstant (object value) + { + // + // Get type of underlying value as int constant can be used for object + // parameter type. This is not allowed in C# but other languages can do that + // + var types = module.Compiler.BuiltinTypes; + switch (System.Type.GetTypeCode (value.GetType ())) { + case TypeCode.Boolean: + return new BoolConstant (types, (bool) value, Location.Null); + case TypeCode.Byte: + return new ByteConstant (types, (byte) value, Location.Null); + case TypeCode.Char: + return new CharConstant (types, (char) value, Location.Null); + case TypeCode.Decimal: + return new DecimalConstant (types, (decimal) value, Location.Null); + case TypeCode.Double: + return new DoubleConstant (types, (double) value, Location.Null); + case TypeCode.Int16: + return new ShortConstant (types, (short) value, Location.Null); + case TypeCode.Int32: + return new IntConstant (types, (int) value, Location.Null); + case TypeCode.Int64: + return new LongConstant (types, (long) value, Location.Null); + case TypeCode.SByte: + return new SByteConstant (types, (sbyte) value, Location.Null); + case TypeCode.Single: + return new FloatConstant (types, (float) value, Location.Null); + case TypeCode.String: + return new StringConstant (types, (string) value, Location.Null); + case TypeCode.UInt16: + return new UShortConstant (types, (ushort) value, Location.Null); + case TypeCode.UInt32: + return new UIntConstant (types, (uint) value, Location.Null); + case TypeCode.UInt64: + return new ULongConstant (types, (ulong) value, Location.Null); + } + + throw new NotImplementedException (value.GetType ().ToString ()); + } + + public TypeSpec ImportType (MetaType type) + { + return ImportType (type, new DynamicTypeReader (type)); + } + + TypeSpec ImportType (MetaType type, DynamicTypeReader dtype) + { + if (type.HasElementType) { + var element = type.GetElementType (); + ++dtype.Position; + var spec = ImportType (element, dtype); + + if (type.IsArray) + return ArrayContainer.MakeType (module, spec, type.GetArrayRank ()); + if (type.IsByRef) + return ReferenceContainer.MakeType (module, spec); + if (type.IsPointer) + return PointerContainer.MakeType (module, spec); + + throw new NotImplementedException ("Unknown element type " + type.ToString ()); + } + + TypeSpec compiled_type; + if (compiled_types.TryGetValue (type, out compiled_type)) { + if (compiled_type.BuiltinType == BuiltinTypeSpec.Type.Object && dtype.IsDynamicObject ()) + return module.Compiler.BuiltinTypes.Dynamic; + + return compiled_type; + } + + return CreateType (type, dtype, true); + } + + static bool IsMissingType (MetaType type) + { +#if STATIC + return type.__IsMissing; +#else + return false; +#endif + } + + // + // Decimal constants cannot be encoded in the constant blob, and thus are marked + // as IsInitOnly ('readonly' in C# parlance). We get its value from the + // DecimalConstantAttribute metadata. + // + Constant ReadDecimalConstant (IList attrs) + { + if (attrs.Count == 0) + return null; + + foreach (var ca in attrs) { + var dt = ca.Constructor.DeclaringType; + if (dt.Name != "DecimalConstantAttribute" || dt.Namespace != CompilerServicesNamespace) + continue; + + var value = new decimal ( + (int) (uint) ca.ConstructorArguments[4].Value, + (int) (uint) ca.ConstructorArguments[3].Value, + (int) (uint) ca.ConstructorArguments[2].Value, + (byte) ca.ConstructorArguments[1].Value != 0, + (byte) ca.ConstructorArguments[0].Value); + + return new DecimalConstant (module.Compiler.BuiltinTypes, value, Location.Null); + } + + return null; + } + + static Modifiers ReadMethodModifiers (MethodBase mb, TypeSpec declaringType) + { + Modifiers mod; + var ma = mb.Attributes; + switch (ma & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Public: + mod = Modifiers.PUBLIC; + break; + case MethodAttributes.Assembly: + mod = Modifiers.INTERNAL; + break; + case MethodAttributes.Family: + mod = Modifiers.PROTECTED; + break; + case MethodAttributes.FamORAssem: + mod = Modifiers.PROTECTED | Modifiers.INTERNAL; + break; + default: + mod = Modifiers.PRIVATE; + break; + } + + if ((ma & MethodAttributes.Static) != 0) { + mod |= Modifiers.STATIC; + return mod; + } + if ((ma & MethodAttributes.Abstract) != 0 && declaringType.IsClass) { + mod |= Modifiers.ABSTRACT; + return mod; + } + + // It can be sealed and override + if ((ma & MethodAttributes.Final) != 0) + mod |= Modifiers.SEALED; + + if ((ma & MethodAttributes.Virtual) != 0) { + // Not every member can be detected based on MethodAttribute, we + // set virtual or non-virtual only when we are certain. Further checks + // to really find out what `virtual' means for this member are done + // later + if ((ma & MethodAttributes.NewSlot) != 0) { + if ((mod & Modifiers.SEALED) != 0) { + mod &= ~Modifiers.SEALED; + } else { + mod |= Modifiers.VIRTUAL; + } + } else { + mod |= Modifiers.OVERRIDE; + } + } + + return mod; + } + } + + abstract class ImportedDefinition : IMemberDefinition + { + protected class AttributesBag + { + public static readonly AttributesBag Default = new AttributesBag (); + + public AttributeUsageAttribute AttributeUsage; + public ObsoleteAttribute Obsolete; + public string[] Conditionals; + public string DefaultIndexerName; + public bool? CLSAttributeValue; + public TypeSpec CoClass; + + static bool HasMissingType (ConstructorInfo ctor) + { +#if STATIC + // + // Mimic odd csc behaviour where missing type on predefined + // attributes means the attribute is silently ignored. This can + // happen with PCL facades + // + foreach (var p in ctor.GetParameters ()) { + if (p.ParameterType.__ContainsMissingType) + return true; + } +#endif + + return false; + } + + public static AttributesBag Read (MemberInfo mi, MetadataImporter importer) + { + AttributesBag bag = null; + List conditionals = null; + + // It should not throw any loading exception + IList attrs = CustomAttributeData.GetCustomAttributes (mi); + + foreach (var a in attrs) { + var dt = a.Constructor.DeclaringType; + string name = dt.Name; + if (name == "ObsoleteAttribute") { + if (dt.Namespace != "System") + continue; + + if (bag == null) + bag = new AttributesBag (); + + var args = a.ConstructorArguments; + + if (args.Count == 1) { + bag.Obsolete = new ObsoleteAttribute ((string) args[0].Value); + } else if (args.Count == 2) { + bag.Obsolete = new ObsoleteAttribute ((string) args[0].Value, (bool) args[1].Value); + } else { + bag.Obsolete = new ObsoleteAttribute (); + } + + continue; + } + + if (name == "ConditionalAttribute") { + if (dt.Namespace != "System.Diagnostics") + continue; + + if (bag == null) + bag = new AttributesBag (); + + if (conditionals == null) + conditionals = new List (2); + + conditionals.Add ((string) a.ConstructorArguments[0].Value); + continue; + } + + if (name == "CLSCompliantAttribute") { + if (dt.Namespace != "System") + continue; + + if (bag == null) + bag = new AttributesBag (); + + bag.CLSAttributeValue = (bool) a.ConstructorArguments[0].Value; + continue; + } + + // Type only attributes + if (mi.MemberType == MemberTypes.TypeInfo || mi.MemberType == MemberTypes.NestedType) { + if (name == "DefaultMemberAttribute") { + if (dt.Namespace != "System.Reflection") + continue; + + if (bag == null) + bag = new AttributesBag (); + + bag.DefaultIndexerName = (string) a.ConstructorArguments[0].Value; + continue; + } + + if (name == "AttributeUsageAttribute") { + if (dt.Namespace != "System") + continue; + + if (HasMissingType (a.Constructor)) + continue; + + if (bag == null) + bag = new AttributesBag (); + + bag.AttributeUsage = new AttributeUsageAttribute ((AttributeTargets) a.ConstructorArguments[0].Value); + foreach (var named in a.NamedArguments) { + if (named.MemberInfo.Name == "AllowMultiple") + bag.AttributeUsage.AllowMultiple = (bool) named.TypedValue.Value; + else if (named.MemberInfo.Name == "Inherited") + bag.AttributeUsage.Inherited = (bool) named.TypedValue.Value; + } + continue; + } + + // Interface only attribute + if (name == "CoClassAttribute") { + if (dt.Namespace != "System.Runtime.InteropServices") + continue; + + if (HasMissingType (a.Constructor)) + continue; + + if (bag == null) + bag = new AttributesBag (); + + bag.CoClass = importer.ImportType ((MetaType) a.ConstructorArguments[0].Value); + continue; + } + } + } + + if (bag == null) + return Default; + + if (conditionals != null) + bag.Conditionals = conditionals.ToArray (); + + return bag; + } + } + + protected readonly MemberInfo provider; + protected AttributesBag cattrs; + protected readonly MetadataImporter importer; + + protected ImportedDefinition (MemberInfo provider, MetadataImporter importer) + { + this.provider = provider; + this.importer = importer; + } + + #region Properties + + public bool IsImported { + get { + return true; + } + } + + public virtual string Name { + get { + return provider.Name; + } + } + + #endregion + + public string[] ConditionalConditions () + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.Conditionals; + } + + public ObsoleteAttribute GetAttributeObsolete () + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.Obsolete; + } + + public bool? CLSAttributeValue { + get { + if (cattrs == null) + ReadAttributes (); + + return cattrs.CLSAttributeValue; + } + } + + protected void ReadAttributes () + { + cattrs = AttributesBag.Read (provider, importer); + } + + public void SetIsAssigned () + { + // Unused for imported members + } + + public void SetIsUsed () + { + // Unused for imported members + } + } + + public class ImportedModuleDefinition + { + readonly Module module; + bool cls_compliant; + + public ImportedModuleDefinition (Module module) + { + this.module = module; + } + + #region Properties + + public bool IsCLSCompliant { + get { + return cls_compliant; + } + } + + public string Name { + get { + return module.Name; + } + } + + #endregion + + public void ReadAttributes () + { + IList attrs = CustomAttributeData.GetCustomAttributes (module); + + foreach (var a in attrs) { + var dt = a.Constructor.DeclaringType; + if (dt.Name == "CLSCompliantAttribute") { + if (dt.Namespace != "System") + continue; + + cls_compliant = (bool) a.ConstructorArguments[0].Value; + continue; + } + } + } + + // + // Reads assembly attributes which where attached to a special type because + // module does have assembly manifest + // + public List ReadAssemblyAttributes () + { + var t = module.GetType (AssemblyAttributesPlaceholder.GetGeneratedName (Name)); + if (t == null) + return null; + + var field = t.GetField (AssemblyAttributesPlaceholder.AssemblyFieldName, BindingFlags.NonPublic | BindingFlags.Static); + if (field == null) + return null; + + // TODO: implement, the idea is to fabricate specil Attribute class and + // add it to OptAttributes before resolving the source code attributes + // Need to build module location as well for correct error reporting + + //var assembly_attributes = CustomAttributeData.GetCustomAttributes (field); + //var attrs = new List (assembly_attributes.Count); + //foreach (var a in assembly_attributes) + //{ + // var type = metaImporter.ImportType (a.Constructor.DeclaringType); + // var ctor = metaImporter.CreateMethod (a.Constructor, type); + + // foreach (var carg in a.ConstructorArguments) { + // carg.Value + // } + + // attrs.Add (new Attribute ("assembly", ctor, null, Location.Null, true)); + //} + + return null; + } + } + + public class ImportedAssemblyDefinition : IAssemblyDefinition + { + readonly Assembly assembly; + readonly AssemblyName aname; + bool cls_compliant; + + List internals_visible_to; + Dictionary internals_visible_to_cache; + + public ImportedAssemblyDefinition (Assembly assembly) + { + this.assembly = assembly; + this.aname = assembly.GetName (); + } + + #region Properties + + public Assembly Assembly { + get { + return assembly; + } + } + + public string FullName { + get { + return aname.FullName; + } + } + + public bool HasStrongName { + get { + return aname.GetPublicKey ().Length != 0; + } + } + + public bool IsMissing { + get { +#if STATIC + return assembly.__IsMissing; +#else + return false; +#endif + } + } + + public bool IsCLSCompliant { + get { + return cls_compliant; + } + } + + public string Location { + get { + return assembly.Location; + } + } + + public string Name { + get { + return aname.Name; + } + } + + #endregion + + public byte[] GetPublicKeyToken () + { + return aname.GetPublicKeyToken (); + } + + public AssemblyName GetAssemblyVisibleToName (IAssemblyDefinition assembly) + { + return internals_visible_to_cache [assembly]; + } + + public bool IsFriendAssemblyTo (IAssemblyDefinition assembly) + { + if (internals_visible_to == null) + return false; + + AssemblyName is_visible = null; + if (internals_visible_to_cache == null) { + internals_visible_to_cache = new Dictionary (); + } else { + if (internals_visible_to_cache.TryGetValue (assembly, out is_visible)) + return is_visible != null; + } + + var token = assembly.GetPublicKeyToken (); + if (token != null && token.Length == 0) + token = null; + + foreach (var internals in internals_visible_to) { + if (internals.Name != assembly.Name) + continue; + + if (token == null && assembly is AssemblyDefinition) { + is_visible = internals; + break; + } + + if (!ArrayComparer.IsEqual (token, internals.GetPublicKeyToken ())) + continue; + + is_visible = internals; + break; + } + + internals_visible_to_cache.Add (assembly, is_visible); + return is_visible != null; + } + + public void ReadAttributes () + { +#if STATIC + if (assembly.__IsMissing) + return; +#endif + + IList attrs = CustomAttributeData.GetCustomAttributes (assembly); + + foreach (var a in attrs) { + var dt = a.Constructor.DeclaringType; + var name = dt.Name; + if (name == "CLSCompliantAttribute") { + if (dt.Namespace == "System") { + cls_compliant = (bool) a.ConstructorArguments[0].Value; + } + continue; + } + + if (name == "InternalsVisibleToAttribute") { + if (dt.Namespace != MetadataImporter.CompilerServicesNamespace) + continue; + + string s = a.ConstructorArguments[0].Value as string; + if (s == null) + continue; + + var an = new AssemblyName (s); + if (internals_visible_to == null) + internals_visible_to = new List (); + + internals_visible_to.Add (an); + continue; + } + } + } + + public override string ToString () + { + return FullName; + } + } + + class ImportedMemberDefinition : ImportedDefinition + { + readonly TypeSpec type; + + public ImportedMemberDefinition (MemberInfo member, TypeSpec type, MetadataImporter importer) + : base (member, importer) + { + this.type = type; + } + + #region Properties + + public TypeSpec MemberType { + get { + return type; + } + } + + #endregion + } + + class ImportedParameterMemberDefinition : ImportedMemberDefinition, IParametersMember + { + readonly AParametersCollection parameters; + + protected ImportedParameterMemberDefinition (MethodBase provider, TypeSpec type, AParametersCollection parameters, MetadataImporter importer) + : base (provider, type, importer) + { + this.parameters = parameters; + } + + public ImportedParameterMemberDefinition (PropertyInfo provider, TypeSpec type, AParametersCollection parameters, MetadataImporter importer) + : base (provider, type, importer) + { + this.parameters = parameters; + } + + #region Properties + + public AParametersCollection Parameters { + get { + return parameters; + } + } + + #endregion + } + + class ImportedMethodDefinition : ImportedParameterMemberDefinition, IMethodDefinition + { + public ImportedMethodDefinition (MethodBase provider, TypeSpec type, AParametersCollection parameters, MetadataImporter importer) + : base (provider, type, parameters, importer) + { + } + + MethodBase IMethodDefinition.Metadata { + get { + return (MethodBase) provider; + } + } + } + + class ImportedGenericMethodDefinition : ImportedMethodDefinition, IGenericMethodDefinition + { + readonly TypeParameterSpec[] tparams; + + public ImportedGenericMethodDefinition (MethodInfo provider, TypeSpec type, AParametersCollection parameters, TypeParameterSpec[] tparams, MetadataImporter importer) + : base (provider, type, parameters, importer) + { + this.tparams = tparams; + } + + #region Properties + + public TypeParameterSpec[] TypeParameters { + get { + return tparams; + } + } + + public int TypeParametersCount { + get { + return tparams.Length; + } + } + + #endregion + } + + class ImportedTypeDefinition : ImportedDefinition, ITypeDefinition + { + TypeParameterSpec[] tparams; + string name; + + public ImportedTypeDefinition (MetaType type, MetadataImporter importer) + : base (type, importer) + { + } + + #region Properties + + public IAssemblyDefinition DeclaringAssembly { + get { + return importer.GetAssemblyDefinition (provider.Module.Assembly); + } + } + + bool ITypeDefinition.IsComImport { + get { + return ((MetaType) provider).IsImport; + } + } + + + bool ITypeDefinition.IsPartial { + get { + return false; + } + } + + bool ITypeDefinition.IsTypeForwarder { + get { +#if STATIC + return ((MetaType) provider).__IsTypeForwarder; +#else + return false; +#endif + } + } + + bool ITypeDefinition.IsCyclicTypeForwarder { + get { +#if STATIC + return ((MetaType) provider).__IsCyclicTypeForwarder; +#else + return false; +#endif + } + } + + public override string Name { + get { + if (name == null) { + name = base.Name; + if (tparams != null) { + int arity_start = name.IndexOf ('`'); + if (arity_start > 0) + name = name.Substring (0, arity_start); + } + } + + return name; + } + } + + public string Namespace { + get { + return ((MetaType) provider).Namespace; + } + } + + public int TypeParametersCount { + get { + return tparams == null ? 0 : tparams.Length; + } + } + + public TypeParameterSpec[] TypeParameters { + get { + return tparams; + } + set { + tparams = value; + } + } + + #endregion + + public void DefineInterfaces (TypeSpec spec) + { + var type = (MetaType) provider; + MetaType[] ifaces; +#if STATIC + ifaces = type.__GetDeclaredInterfaces (); + if (ifaces.Length != 0) { + foreach (var iface in ifaces) { + var it = importer.CreateType (iface); + if (it == null) + continue; + + spec.AddInterfaceDefined (it); + + // Unfortunately not all languages expand inherited interfaces + var bifaces = it.Interfaces; + if (bifaces != null) { + foreach (var biface in bifaces) { + spec.AddInterfaceDefined (biface); + } + } + } + } + + // + // It's impossible to get declared interfaces only using System.Reflection + // hence we need to mimic the behavior with ikvm-reflection too to keep + // our type look-up logic same + // + if (spec.BaseType != null) { + var bifaces = spec.BaseType.Interfaces; + if (bifaces != null) { + // + // Before adding base class interfaces close defined interfaces + // on type parameter + // + var tp = spec as TypeParameterSpec; + if (tp != null && tp.InterfacesDefined == null) { + tp.InterfacesDefined = TypeSpec.EmptyTypes; + } + + foreach (var iface in bifaces) + spec.AddInterfaceDefined (iface); + } + } +#else + ifaces = type.GetInterfaces (); + + if (ifaces.Length > 0) { + foreach (var iface in ifaces) { + spec.AddInterface (importer.CreateType (iface)); + } + } +#endif + + } + + public static void Error_MissingDependency (IMemberContext ctx, List missing, Location loc) + { + // + // Report details about missing type and most likely cause of the problem. + // csc reports 1683, 1684 as warnings but we report them only when used + // or referenced from the user core in which case compilation error has to + // be reported because compiler cannot continue anyway + // + + var report = ctx.Module.Compiler.Report; + + for (int i = 0; i < missing.Count; ++i) { + var t = missing [i].Type; + + // + // Report missing types only once + // + if (report.Printer.MissingTypeReported (t.MemberDefinition)) + continue; + + string name = t.GetSignatureForError (); + + var caller = missing[i].Caller; + if (caller.Kind != MemberKind.MissingType) + report.SymbolRelatedToPreviousError (caller); + + var definition = t.MemberDefinition; + if (definition.DeclaringAssembly == ctx.Module.DeclaringAssembly) { + report.Error (1683, loc, + "Reference to type `{0}' claims it is defined in this assembly, but it is not defined in source or any added modules", + name); + } else if (definition.DeclaringAssembly.IsMissing) { + if (definition.IsTypeForwarder) { + report.Error (1070, loc, + "The type `{0}' has been forwarded to an assembly that is not referenced. Consider adding a reference to assembly `{1}'", + name, definition.DeclaringAssembly.FullName); + } else { + report.Error (12, loc, + "The type `{0}' is defined in an assembly that is not referenced. Consider adding a reference to assembly `{1}'", + name, definition.DeclaringAssembly.FullName); + } + } else if (definition.IsTypeForwarder) { + report.Error (731, loc, "The type forwarder for type `{0}' in assembly `{1}' has circular dependency", + name, definition.DeclaringAssembly.FullName); + } else { + report.Error (1684, loc, + "Reference to type `{0}' claims it is defined assembly `{1}', but it could not be found", + name, t.MemberDefinition.DeclaringAssembly.FullName); + } + } + } + + public TypeSpec GetAttributeCoClass () + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.CoClass; + } + + public string GetAttributeDefaultMember () + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.DefaultIndexerName; + } + + public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.AttributeUsage; + } + + bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) + { + var a = importer.GetAssemblyDefinition (provider.Module.Assembly); + return a == assembly || a.IsFriendAssemblyTo (assembly); + } + + public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) + { + // + // Not interested in members of nested private types unless the importer needs them + // + if (declaringType.IsPrivate && importer.IgnorePrivateMembers) { + cache = MemberCache.Empty; + return; + } + + var loading_type = (MetaType) provider; + const BindingFlags all_members = BindingFlags.DeclaredOnly | + BindingFlags.Static | BindingFlags.Instance | + BindingFlags.Public | BindingFlags.NonPublic; + + const MethodAttributes explicit_impl = MethodAttributes.NewSlot | + MethodAttributes.Virtual | MethodAttributes.HideBySig | + MethodAttributes.Final; + + Dictionary possible_accessors = null; + List imported_events = null; + EventSpec event_spec; + MemberSpec imported; + MethodInfo m; + MemberInfo[] all; + try { + all = loading_type.GetMembers (all_members); + } catch (Exception e) { + throw new InternalErrorException (e, "Could not import type `{0}' from `{1}'", + declaringType.GetSignatureForError (), declaringType.MemberDefinition.DeclaringAssembly.FullName); + } + + if (cache == null) { + cache = new MemberCache (all.Length); + + // + // Do the types first as they can be referenced by the members before + // they are found or inflated + // + foreach (var member in all) { + if (member.MemberType != MemberTypes.NestedType) + continue; + + var t = (MetaType) member; + + // Ignore compiler generated types, mostly lambda containers + if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate && importer.IgnorePrivateMembers) + continue; + + try { + imported = importer.CreateNestedType (t, declaringType); + } catch (Exception e) { + throw new InternalErrorException (e, "Could not import nested type `{0}' from `{1}'", + t.FullName, declaringType.MemberDefinition.DeclaringAssembly.FullName); + } + + cache.AddMemberImported (imported); + } + + foreach (var member in all) { + if (member.MemberType != MemberTypes.NestedType) + continue; + + var t = (MetaType) member; + + if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate && importer.IgnorePrivateMembers) + continue; + + importer.ImportTypeBase (t); + } + } + + // + // Load base interfaces first to minic behaviour of compiled members + // + if (declaringType.IsInterface && declaringType.Interfaces != null) { + foreach (var iface in declaringType.Interfaces) { + cache.AddInterface (iface); + } + } + + if (!onlyTypes) { + // + // The logic here requires methods to be returned first which seems to work for both Mono and .NET + // + foreach (var member in all) { + switch (member.MemberType) { + case MemberTypes.Constructor: + if (declaringType.IsInterface) + continue; + + goto case MemberTypes.Method; + case MemberTypes.Method: + MethodBase mb = (MethodBase) member; + var attrs = mb.Attributes; + + if ((attrs & MethodAttributes.MemberAccessMask) == MethodAttributes.Private) { + if (importer.IgnorePrivateMembers) + continue; + + // Ignore explicitly implemented members + if ((attrs & explicit_impl) == explicit_impl) + continue; + + // Ignore compiler generated methods + if (MetadataImporter.HasAttribute (CustomAttributeData.GetCustomAttributes (mb), "CompilerGeneratedAttribute", MetadataImporter.CompilerServicesNamespace)) + continue; + } + + imported = importer.CreateMethod (mb, declaringType); + if (imported.Kind == MemberKind.Method && !imported.IsGeneric) { + if (possible_accessors == null) + possible_accessors = new Dictionary (ReferenceEquality.Default); + + // There are no metadata rules for accessors, we have to consider any method as possible candidate + possible_accessors.Add (mb, (MethodSpec) imported); + } + + break; + case MemberTypes.Property: + if (possible_accessors == null) + continue; + + var p = (PropertyInfo) member; + // + // Links possible accessors with property + // + MethodSpec get, set; + m = p.GetGetMethod (true); + if (m == null || !possible_accessors.TryGetValue (m, out get)) + get = null; + + m = p.GetSetMethod (true); + if (m == null || !possible_accessors.TryGetValue (m, out set)) + set = null; + + // No accessors registered (e.g. explicit implementation) + if (get == null && set == null) + continue; + + try { + imported = importer.CreateProperty (p, declaringType, get, set); + } catch (Exception ex) { + throw new InternalErrorException (ex, "Could not import property `{0}' inside `{1}'", + p.Name, declaringType.GetSignatureForError ()); + } + + if (imported == null) + continue; + + break; + case MemberTypes.Event: + if (possible_accessors == null) + continue; + + var e = (EventInfo) member; + // + // Links accessors with event + // + MethodSpec add, remove; + m = e.GetAddMethod (true); + if (m == null || !possible_accessors.TryGetValue (m, out add)) + add = null; + + m = e.GetRemoveMethod (true); + if (m == null || !possible_accessors.TryGetValue (m, out remove)) + remove = null; + + // Both accessors are required + if (add == null || remove == null) + continue; + + event_spec = importer.CreateEvent (e, declaringType, add, remove); + if (!importer.IgnorePrivateMembers) { + if (imported_events == null) + imported_events = new List (); + + imported_events.Add (event_spec); + } + + imported = event_spec; + break; + case MemberTypes.Field: + var fi = (FieldInfo) member; + + imported = importer.CreateField (fi, declaringType); + if (imported == null) + continue; + + // + // For dynamic binder event has to be fully restored to allow operations + // within the type container to work correctly + // + if (imported_events != null) { + // The backing event field should be private but it may not + int i; + for (i = 0; i < imported_events.Count; ++i) { + var ev = imported_events[i]; + if (ev.Name == fi.Name) { + ev.BackingField = (FieldSpec) imported; + imported_events.RemoveAt (i); + i = -1; + break; + } + } + + if (i < 0) + continue; + } + + break; + case MemberTypes.NestedType: + // Already in the cache from the first pass + continue; + default: + throw new NotImplementedException (member.ToString ()); + } + + if (imported.IsStatic && declaringType.IsInterface) + continue; + + cache.AddMemberImported (imported); + } + } + } + } + + class ImportedTypeParameterDefinition : ImportedDefinition, ITypeDefinition + { + public ImportedTypeParameterDefinition (MetaType type, MetadataImporter importer) + : base (type, importer) + { + } + + #region Properties + + public IAssemblyDefinition DeclaringAssembly { + get { + throw new NotImplementedException (); + } + } + + bool ITypeDefinition.IsComImport { + get { + return false; + } + } + + bool ITypeDefinition.IsPartial { + get { + return false; + } + } + + bool ITypeDefinition.IsTypeForwarder { + get { + return false; + } + } + + bool ITypeDefinition.IsCyclicTypeForwarder { + get { + return false; + } + } + + public string Namespace { + get { + return null; + } + } + + public int TypeParametersCount { + get { + return 0; + } + } + + public TypeParameterSpec[] TypeParameters { + get { + return null; + } + } + + #endregion + + public TypeSpec GetAttributeCoClass () + { + return null; + } + + public string GetAttributeDefaultMember () + { + throw new NotSupportedException (); + } + + public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) + { + throw new NotSupportedException (); + } + + bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) + { + throw new NotImplementedException (); + } + + public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) + { + throw new NotImplementedException (); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/iterators.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/iterators.cs new file mode 100644 index 000000000..5dce0ed15 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/iterators.cs @@ -0,0 +1,1260 @@ +// +// iterators.cs: Support for implementing iterators +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// Copyright 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using Mono.CompilerServices.SymbolWriter; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public abstract class YieldStatement : ResumableStatement where T : StateMachineInitializer + { + protected Expression expr; + protected bool unwind_protect; + protected T machine_initializer; + int resume_pc; + ExceptionStatement inside_try_block; + + protected YieldStatement (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + public Expression Expr { + get { return this.expr; } + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + var target = (YieldStatement) t; + target.expr = expr.Clone (clonectx); + } + + protected override void DoEmit (EmitContext ec) + { + machine_initializer.InjectYield (ec, expr, resume_pc, unwind_protect, resume_point); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + + RegisterResumePoint (); + + return false; + } + + public override bool Resolve (BlockContext bc) + { + expr = expr.Resolve (bc); + if (expr == null) + return false; + + machine_initializer = bc.CurrentAnonymousMethod as T; + inside_try_block = bc.CurrentTryBlock; + return true; + } + + public void RegisterResumePoint () + { + if (resume_pc != 0) + return; + + if (inside_try_block == null) { + resume_pc = machine_initializer.AddResumePoint (this); + } else { + resume_pc = inside_try_block.AddResumePoint (this, resume_pc, machine_initializer); + unwind_protect = true; + inside_try_block = null; + } + } + } + + public class Yield : YieldStatement + { + public Yield (Expression expr, Location loc) + : base (expr, loc) + { + } + + public static bool CheckContext (BlockContext bc, Location loc) + { + if (!bc.CurrentAnonymousMethod.IsIterator) { + bc.Report.Error (1621, loc, + "The yield statement cannot be used inside anonymous method blocks"); + return false; + } + + if (bc.HasSet (ResolveContext.Options.FinallyScope)) { + bc.Report.Error (1625, loc, "Cannot yield in the body of a finally clause"); + return false; + } + + return true; + } + + public override bool Resolve (BlockContext bc) + { + if (!CheckContext (bc, loc)) + return false; + + if (bc.HasAny (ResolveContext.Options.TryWithCatchScope)) { + bc.Report.Error (1626, loc, "Cannot yield a value in the body of a try block with a catch clause"); + } + + if (bc.HasSet (ResolveContext.Options.CatchScope)) { + bc.Report.Error (1631, loc, "Cannot yield a value in the body of a catch clause"); + } + + if (!base.Resolve (bc)) + return false; + + var otype = bc.CurrentIterator.OriginalIteratorType; + if (expr.Type != otype) { + expr = Convert.ImplicitConversionRequired (bc, expr, otype, loc); + if (expr == null) + return false; + } + + return true; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class YieldBreak : ExitStatement + { + Iterator iterator; + + public YieldBreak (Location l) + { + loc = l; + } + + protected override bool IsLocalExit { + get { + return false; + } + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + protected override bool DoResolve (BlockContext bc) + { + iterator = bc.CurrentIterator; + return Yield.CheckContext (bc, loc); + } + + protected override void DoEmit (EmitContext ec) + { + iterator.EmitYieldBreak (ec, unwind_protect); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public abstract class StateMachine : AnonymousMethodStorey + { + public enum State + { + Running = -3, // Used only in CurrentPC, never stored into $PC + Uninitialized = -2, + After = -1, + Start = 0 + } + + Field pc_field; + StateMachineMethod method; + int local_name_idx; + + protected StateMachine (ParametersBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind) + : base (block, parent, host, tparams, name, kind) + { + OriginalTypeParameters = tparams; + } + + #region Properties + + public TypeParameters OriginalTypeParameters { get; private set; } + + public StateMachineMethod StateMachineMethod { + get { + return method; + } + } + + public Field PC { + get { + return pc_field; + } + } + + #endregion + + public void AddEntryMethod (StateMachineMethod method) + { + if (this.method != null) + throw new InternalErrorException (); + + this.method = method; + Members.Add (method); + } + + protected override bool DoDefineMembers () + { + pc_field = AddCompilerGeneratedField ("$PC", new TypeExpression (Compiler.BuiltinTypes.Int, Location)); + + return base.DoDefineMembers (); + } + + protected override string GetVariableMangledName (LocalVariable local_info) + { + if (local_info.IsCompilerGenerated) + return base.GetVariableMangledName (local_info); + + return "<" + local_info.Name + ">__" + local_name_idx++.ToString ("X"); + } + } + + class IteratorStorey : StateMachine + { + class GetEnumeratorMethod : StateMachineMethod + { + sealed class GetEnumeratorStatement : Statement + { + readonly IteratorStorey host; + readonly StateMachineMethod host_method; + + Expression new_storey; + + public GetEnumeratorStatement (IteratorStorey host, StateMachineMethod host_method) + { + this.host = host; + this.host_method = host_method; + loc = host_method.Location; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + public override bool Resolve (BlockContext ec) + { + TypeExpression storey_type_expr = new TypeExpression (host.Definition, loc); + List init = null; + if (host.hoisted_this != null) { + init = new List (host.hoisted_params == null ? 1 : host.HoistedParameters.Count + 1); + HoistedThis ht = host.hoisted_this; + FieldExpr from = new FieldExpr (ht.Field, loc); + from.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + init.Add (new ElementInitializer (ht.Field.Name, from, loc)); + } + + if (host.hoisted_params != null) { + if (init == null) + init = new List (host.HoistedParameters.Count); + + for (int i = 0; i < host.hoisted_params.Count; ++i) { + HoistedParameter hp = host.hoisted_params [i]; + HoistedParameter hp_cp = host.hoisted_params_copy [i] ?? hp; + + FieldExpr from = new FieldExpr (hp_cp.Field, loc); + from.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + + init.Add (new ElementInitializer (hp.Field.Name, from, loc)); + } + } + + if (init != null) { + new_storey = new NewInitialize (storey_type_expr, null, + new CollectionOrObjectInitializers (init, loc), loc); + } else { + new_storey = new New (storey_type_expr, null, loc); + } + + new_storey = new_storey.Resolve (ec); + if (new_storey != null) + new_storey = Convert.ImplicitConversionRequired (ec, new_storey, host_method.MemberType, loc); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + Label label_init = ec.DefineLabel (); + + ec.EmitThis (); + ec.Emit (OpCodes.Ldflda, host.PC.Spec); + ec.EmitInt ((int) State.Start); + ec.EmitInt ((int) State.Uninitialized); + + var m = ec.Module.PredefinedMembers.InterlockedCompareExchange.Resolve (loc); + if (m != null) + ec.Emit (OpCodes.Call, m); + + ec.EmitInt ((int) State.Uninitialized); + ec.Emit (OpCodes.Bne_Un_S, label_init); + + ec.EmitThis (); + ec.Emit (OpCodes.Ret); + + ec.MarkLabel (label_init); + + new_storey.Emit (ec); + ec.Emit (OpCodes.Ret); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + throw new NotImplementedException (); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + } + + GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name) + : base (host, null, returnType, Modifiers.DEBUGGER_HIDDEN, name, ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis) + { + } + + public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpression returnType, MemberName name) + { + return Create (host, returnType, name, null); + } + + public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpression returnType, MemberName name, Statement statement) + { + var m = new GetEnumeratorMethod (host, returnType, name); + var stmt = statement ?? new GetEnumeratorStatement (host, m); + m.block.AddStatement (stmt); + return m; + } + } + + class DisposeMethod : StateMachineMethod + { + sealed class DisposeMethodStatement : Statement + { + Iterator iterator; + + public DisposeMethodStatement (Iterator iterator) + { + this.iterator = iterator; + this.loc = iterator.Location; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + public override bool Resolve (BlockContext ec) + { + return true; + } + + protected override void DoEmit (EmitContext ec) + { + ec.CurrentAnonymousMethod = iterator; + iterator.EmitDispose (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + throw new NotImplementedException (); + } + } + + public DisposeMethod (IteratorStorey host) + : base (host, null, new TypeExpression (host.Compiler.BuiltinTypes.Void, host.Location), Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN, + new MemberName ("Dispose", host.Location), ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis) + { + host.Members.Add (this); + + Block.AddStatement (new DisposeMethodStatement (host.Iterator)); + } + } + + // + // Uses Method as method info + // + class DynamicMethodGroupExpr : MethodGroupExpr + { + readonly Method method; + + public DynamicMethodGroupExpr (Method method, Location loc) + : base ((IList) null, null, loc) + { + this.method = method; + eclass = ExprClass.Unresolved; + } + + protected override Expression DoResolve (ResolveContext ec) + { + Methods = new List (1) { method.Spec }; + type = method.Parent.Definition; + InstanceExpression = new CompilerGeneratedThis (type, Location); + return base.DoResolve (ec); + } + } + + class DynamicFieldExpr : FieldExpr + { + readonly Field field; + + public DynamicFieldExpr (Field field, Location loc) + : base (loc) + { + this.field = field; + } + + protected override Expression DoResolve (ResolveContext ec) + { + spec = field.Spec; + type = spec.MemberType; + InstanceExpression = new CompilerGeneratedThis (type, Location); + return base.DoResolve (ec); + } + } + + public readonly Iterator Iterator; + + List hoisted_params_copy; + + TypeExpr iterator_type_expr; + Field current_field; + Field disposing_field; + + TypeSpec generic_enumerator_type; + TypeSpec generic_enumerable_type; + + public IteratorStorey (Iterator iterator) + : base (iterator.Container.ParametersBlock, iterator.Host, + iterator.OriginalMethod as MemberBase, iterator.OriginalMethod.CurrentTypeParameters, "Iterator", MemberKind.Class) + { + this.Iterator = iterator; + } + + public Field CurrentField { + get { + return current_field; + } + } + + public Field DisposingField { + get { + return disposing_field; + } + } + + public IList HoistedParameters { + get { return hoisted_params; } + } + + protected override Constructor DefineDefaultConstructor (bool is_static) + { + var ctor = base.DefineDefaultConstructor (is_static); + ctor.ModFlags |= Modifiers.DEBUGGER_HIDDEN; + return ctor; + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + var mtype = Iterator.OriginalIteratorType; + if (Mutator != null) + mtype = Mutator.Mutate (mtype); + + iterator_type_expr = new TypeExpression (mtype, Location); + + var ifaces = new List (5); + if (Iterator.IsEnumerable) { + ifaces.Add (Compiler.BuiltinTypes.IEnumerable); + + if (Module.PredefinedTypes.IEnumerableGeneric.Define ()) { + generic_enumerable_type = Module.PredefinedTypes.IEnumerableGeneric.TypeSpec.MakeGenericType (Module, new[] { mtype }); + ifaces.Add (generic_enumerable_type); + } + } + + ifaces.Add (Compiler.BuiltinTypes.IEnumerator); + ifaces.Add (Compiler.BuiltinTypes.IDisposable); + + var ienumerator_generic = Module.PredefinedTypes.IEnumeratorGeneric; + if (ienumerator_generic.Define ()) { + generic_enumerator_type = ienumerator_generic.TypeSpec.MakeGenericType (Module, new [] { mtype }); + ifaces.Add (generic_enumerator_type); + } + + base_class = null; + + base_type = Compiler.BuiltinTypes.Object; + return ifaces.ToArray (); + } + + protected override bool DoDefineMembers () + { + current_field = AddCompilerGeneratedField ("$current", iterator_type_expr); + disposing_field = AddCompilerGeneratedField ("$disposing", new TypeExpression (Compiler.BuiltinTypes.Bool, Location)); + + if (Iterator.IsEnumerable && hoisted_params != null) { + // + // Iterators are independent, each GetEnumerator call has to + // create same enumerator therefore we have to keep original values + // around for re-initialization + // + hoisted_params_copy = new List (hoisted_params.Count); + foreach (HoistedParameter hp in hoisted_params) { + + // + // Don't create field copy for unmodified captured parameters + // + HoistedParameter hp_copy; + if (hp.IsAssigned) { + hp_copy = new HoistedParameter (hp, "<$>" + hp.Field.Name); + } else { + hp_copy = null; + } + + hoisted_params_copy.Add (hp_copy); + } + } + + if (generic_enumerator_type != null) + Define_Current (true); + + Define_Current (false); + new DisposeMethod (this); + Define_Reset (); + + if (Iterator.IsEnumerable) { + FullNamedExpression explicit_iface = new TypeExpression (Compiler.BuiltinTypes.IEnumerable, Location); + var name = new MemberName ("GetEnumerator", null, explicit_iface, Location.Null); + + if (generic_enumerator_type != null) { + explicit_iface = new TypeExpression (generic_enumerable_type, Location); + var gname = new MemberName ("GetEnumerator", null, explicit_iface, Location.Null); + Method gget_enumerator = GetEnumeratorMethod.Create (this, new TypeExpression (generic_enumerator_type, Location), gname); + + // + // Just call generic GetEnumerator implementation + // + var stmt = new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator, Location), null), Location); + Method get_enumerator = GetEnumeratorMethod.Create (this, new TypeExpression (Compiler.BuiltinTypes.IEnumerator, Location), name, stmt); + + Members.Add (get_enumerator); + Members.Add (gget_enumerator); + } else { + Members.Add (GetEnumeratorMethod.Create (this, new TypeExpression (Compiler.BuiltinTypes.IEnumerator, Location), name)); + } + } + + return base.DoDefineMembers (); + } + + void Define_Current (bool is_generic) + { + TypeExpr type; + FullNamedExpression explicit_iface; + + if (is_generic) { + explicit_iface = new TypeExpression (generic_enumerator_type, Location); + type = iterator_type_expr; + } else { + explicit_iface = new TypeExpression (Module.Compiler.BuiltinTypes.IEnumerator, Location); + type = new TypeExpression (Compiler.BuiltinTypes.Object, Location); + } + + var name = new MemberName ("Current", null, explicit_iface, Location); + + ToplevelBlock get_block = new ToplevelBlock (Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location, + Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis); + get_block.AddStatement (new Return (new DynamicFieldExpr (CurrentField, Location), Location)); + + Property current = new Property (this, type, Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED, name, null); + current.Get = new Property.GetMethod (current, Modifiers.COMPILER_GENERATED, null, Location); + current.Get.Block = get_block; + + Members.Add (current); + } + + void Define_Reset () + { + Method reset = new Method ( + this, new TypeExpression (Compiler.BuiltinTypes.Void, Location), + Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED, + new MemberName ("Reset", Location), + ParametersCompiled.EmptyReadOnlyParameters, null); + Members.Add (reset); + + reset.Block = new ToplevelBlock (Compiler, reset.ParameterInfo, Location, + Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis); + + TypeSpec ex_type = Module.PredefinedTypes.NotSupportedException.Resolve (); + if (ex_type == null) + return; + + reset.Block.AddStatement (new Throw (new New (new TypeExpression (ex_type, Location), null, Location), Location)); + } + + protected override void EmitHoistedParameters (EmitContext ec, List hoisted) + { + base.EmitHoistedParameters (ec, hoisted); + if (hoisted_params_copy != null) + base.EmitHoistedParameters (ec, hoisted_params_copy); + } + } + + public class StateMachineMethod : Method + { + readonly StateMachineInitializer expr; + + public StateMachineMethod (StateMachine host, StateMachineInitializer expr, FullNamedExpression returnType, + Modifiers mod, MemberName name, ToplevelBlock.Flags blockFlags) + : base (host, returnType, mod | Modifiers.COMPILER_GENERATED, + name, ParametersCompiled.EmptyReadOnlyParameters, null) + { + this.expr = expr; + Block = new ToplevelBlock (host.Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null, blockFlags); + } + + public override EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + EmitContext ec = new EmitContext (this, ig, MemberType, sourceMethod); + ec.CurrentAnonymousMethod = expr; + + if (expr is AsyncInitializer) + ec.With (BuilderContext.Options.AsyncBody, true); + + return ec; + } + } + + public abstract class StateMachineInitializer : AnonymousExpression + { + sealed class MoveNextBodyStatement : Statement + { + readonly StateMachineInitializer state_machine; + + public MoveNextBodyStatement (StateMachineInitializer stateMachine) + { + this.state_machine = stateMachine; + this.loc = stateMachine.Location; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + public override bool Resolve (BlockContext ec) + { + return true; + } + + protected override void DoEmit (EmitContext ec) + { + state_machine.EmitMoveNext (ec); + } + + public override void Emit (EmitContext ec) + { + // Don't create sequence point + DoEmit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return state_machine.ReturnType.Kind != MemberKind.Void; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + if (state_machine.ReturnType.Kind != MemberKind.Void) + rc = Reachability.CreateUnreachable (); + + return rc; + } + } + + public readonly TypeDefinition Host; + protected StateMachine storey; + + // + // The state as we generate the machine + // + protected Label move_next_ok; + protected Label move_next_error; + LocalBuilder skip_finally; + protected LocalBuilder current_pc; + protected List resume_points; + + protected StateMachineInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType) + : base (block, returnType, block.StartLocation) + { + this.Host = host; + } + + #region Properties + + public Label BodyEnd { get; set; } + + public LocalBuilder CurrentPC + { + get { + return current_pc; + } + } + + public LocalBuilder SkipFinally { + get { + return skip_finally; + } + } + + public override AnonymousMethodStorey Storey { + get { + return storey; + } + } + + #endregion + + public int AddResumePoint (ResumableStatement stmt) + { + if (resume_points == null) + resume_points = new List (); + + resume_points.Add (stmt); + return resume_points.Count; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected virtual BlockContext CreateBlockContext (BlockContext bc) + { + var ctx = new BlockContext (bc, block, bc.ReturnType); + ctx.CurrentAnonymousMethod = this; + + ctx.AssignmentInfoOffset = bc.AssignmentInfoOffset; + ctx.EnclosingLoop = bc.EnclosingLoop; + ctx.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch; + ctx.Switch = bc.Switch; + + return ctx; + } + + protected override Expression DoResolve (ResolveContext rc) + { + var bc = (BlockContext) rc; + var ctx = CreateBlockContext (bc); + + Block.Resolve (ctx); + + if (!rc.IsInProbingMode) { + var move_next = new StateMachineMethod (storey, this, new TypeExpression (ReturnType, loc), Modifiers.PUBLIC, new MemberName ("MoveNext", loc), 0); + move_next.Block.AddStatement (new MoveNextBodyStatement (this)); + storey.AddEntryMethod (move_next); + } + + bc.AssignmentInfoOffset = ctx.AssignmentInfoOffset; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + // + // Load state machine instance + // + storey.Instance.Emit (ec); + } + + void EmitMoveNext_NoResumePoints (EmitContext ec) + { + ec.EmitThis (); + ec.Emit (OpCodes.Ldfld, storey.PC.Spec); + + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + // We only care if the PC is zero (start executing) or non-zero (don't do anything) + ec.Emit (OpCodes.Brtrue, move_next_error); + + BodyEnd = ec.DefineLabel (); + + var async_init = this as AsyncInitializer; + if (async_init != null) + ec.BeginExceptionBlock (); + + block.EmitEmbedded (ec); + + if (async_init != null) + async_init.EmitCatchBlock (ec); + + ec.MarkLabel (BodyEnd); + + EmitMoveNextEpilogue (ec); + + ec.MarkLabel (move_next_error); + + if (ReturnType.Kind != MemberKind.Void) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ret); + } + + ec.MarkLabel (move_next_ok); + } + + void EmitMoveNext (EmitContext ec) + { + move_next_ok = ec.DefineLabel (); + move_next_error = ec.DefineLabel (); + + if (resume_points == null) { + EmitMoveNext_NoResumePoints (ec); + return; + } + + current_pc = ec.GetTemporaryLocal (ec.BuiltinTypes.UInt); + ec.EmitThis (); + ec.Emit (OpCodes.Ldfld, storey.PC.Spec); + ec.Emit (OpCodes.Stloc, current_pc); + + // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + Label[] labels = new Label[1 + resume_points.Count]; + labels[0] = ec.DefineLabel (); + + bool need_skip_finally = false; + for (int i = 0; i < resume_points.Count; ++i) { + ResumableStatement s = resume_points[i]; + need_skip_finally |= s is ExceptionStatement; + labels[i + 1] = s.PrepareForEmit (ec); + } + + if (need_skip_finally) { + skip_finally = ec.GetTemporaryLocal (ec.BuiltinTypes.Bool); + ec.EmitInt (0); + ec.Emit (OpCodes.Stloc, skip_finally); + } + + var async_init = this as AsyncInitializer; + if (async_init != null) + ec.BeginExceptionBlock (); + + ec.Emit (OpCodes.Ldloc, current_pc); + ec.Emit (OpCodes.Switch, labels); + + ec.Emit (async_init != null ? OpCodes.Leave : OpCodes.Br, move_next_error); + + ec.MarkLabel (labels[0]); + + BodyEnd = ec.DefineLabel (); + + block.EmitEmbedded (ec); + + ec.MarkLabel (BodyEnd); + + if (async_init != null) { + async_init.EmitCatchBlock (ec); + } + + ec.Mark (Block.Original.EndLocation); + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + EmitMoveNextEpilogue (ec); + + ec.MarkLabel (move_next_error); + + if (ReturnType.Kind != MemberKind.Void) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ret); + } + + ec.MarkLabel (move_next_ok); + + if (ReturnType.Kind != MemberKind.Void) { + ec.EmitInt (1); + ec.Emit (OpCodes.Ret); + } + } + + protected virtual void EmitMoveNextEpilogue (EmitContext ec) + { + } + + public void EmitLeave (EmitContext ec, bool unwind_protect) + { + // Return ok + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok); + } + + // + // Called back from YieldStatement + // + public virtual void InjectYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point) + { + // + // Guard against being disposed meantime + // + Label disposed = ec.DefineLabel (); + var iterator = storey as IteratorStorey; + if (iterator != null) { + ec.EmitThis (); + ec.Emit (OpCodes.Ldfld, iterator.DisposingField.Spec); + ec.Emit (OpCodes.Brtrue_S, disposed); + } + + // + // store resume program-counter + // + ec.EmitThis (); + ec.EmitInt (resume_pc); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + if (iterator != null) { + ec.MarkLabel (disposed); + } + + // mark finally blocks as disabled + if (unwind_protect && skip_finally != null) { + ec.EmitInt (1); + ec.Emit (OpCodes.Stloc, skip_finally); + } + } + + public void SetStateMachine (StateMachine stateMachine) + { + this.storey = stateMachine; + } + } + + // + // Iterators are implemented as state machine blocks + // + public class Iterator : StateMachineInitializer + { + sealed class TryFinallyBlockProxyStatement : Statement + { + TryFinallyBlock block; + Iterator iterator; + + public TryFinallyBlockProxyStatement (Iterator iterator, TryFinallyBlock block) + { + this.iterator = iterator; + this.block = block; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + throw new NotSupportedException (); + } + + protected override void DoEmit (EmitContext ec) + { + // + // Restore redirection for any captured variables + // + ec.CurrentAnonymousMethod = iterator; + + using (ec.With (BuilderContext.Options.OmitDebugInfo, !ec.HasMethodSymbolBuilder)) { + block.EmitFinallyBody (ec); + } + } + } + + public readonly IMethodData OriginalMethod; + public readonly bool IsEnumerable; + public readonly TypeSpec OriginalIteratorType; + int finally_hosts_counter; + + public Iterator (ParametersBlock block, IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable) + : base (block, host, host.Compiler.BuiltinTypes.Bool) + { + this.OriginalMethod = method; + this.OriginalIteratorType = iterator_type; + this.IsEnumerable = is_enumerable; + this.type = method.ReturnType; + } + + #region Properties + + public ToplevelBlock Container { + get { return OriginalMethod.Block; } + } + + public override string ContainerType { + get { return "iterator"; } + } + + public override bool IsIterator { + get { return true; } + } + + #endregion + + public Method CreateFinallyHost (TryFinallyBlock block) + { + var method = new Method (storey, new TypeExpression (storey.Compiler.BuiltinTypes.Void, loc), + Modifiers.COMPILER_GENERATED, new MemberName (CompilerGeneratedContainer.MakeName (null, null, "Finally", finally_hosts_counter++), loc), + ParametersCompiled.EmptyReadOnlyParameters, null); + + method.Block = new ToplevelBlock (method.Compiler, method.ParameterInfo, loc, + ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis); + method.Block.AddStatement (new TryFinallyBlockProxyStatement (this, block)); + + // Cannot it add to storey because it'd be emitted before nested + // anonoymous methods which could capture shared variable + + return method; + } + + public void EmitYieldBreak (EmitContext ec, bool unwind_protect) + { + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error); + } + + public override string GetSignatureForError () + { + return OriginalMethod.GetSignatureForError (); + } + + public override void Emit (EmitContext ec) + { + // + // Load Iterator storey instance + // + storey.Instance.Emit (ec); + + // + // Initialize iterator PC when it's unitialized + // + if (IsEnumerable) { + ec.Emit (OpCodes.Dup); + ec.EmitInt ((int)IteratorStorey.State.Uninitialized); + + var field = storey.PC.Spec; + if (storey.MemberName.IsGeneric) { + field = MemberCache.GetMember (Storey.Instance.Type, field); + } + + ec.Emit (OpCodes.Stfld, field); + } + } + + public void EmitDispose (EmitContext ec) + { + if (resume_points == null) + return; + + Label end = ec.DefineLabel (); + + Label[] labels = null; + for (int i = 0; i < resume_points.Count; ++i) { + ResumableStatement s = resume_points[i]; + Label ret = s.PrepareForDispose (ec, end); + if (ret.Equals (end) && labels == null) + continue; + if (labels == null) { + labels = new Label[resume_points.Count + 1]; + for (int j = 0; j <= i; ++j) + labels[j] = end; + } + + labels[i + 1] = ret; + } + + if (labels != null) { + current_pc = ec.GetTemporaryLocal (ec.BuiltinTypes.UInt); + ec.EmitThis (); + ec.Emit (OpCodes.Ldfld, storey.PC.Spec); + ec.Emit (OpCodes.Stloc, current_pc); + } + + ec.EmitThis (); + ec.EmitInt (1); + ec.Emit (OpCodes.Stfld, ((IteratorStorey) storey).DisposingField.Spec); + + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + if (labels != null) { + //SymbolWriter.StartIteratorDispatcher (ec.ig); + ec.Emit (OpCodes.Ldloc, current_pc); + ec.Emit (OpCodes.Switch, labels); + //SymbolWriter.EndIteratorDispatcher (ec.ig); + + foreach (ResumableStatement s in resume_points) + s.EmitForDispose (ec, current_pc, end, true); + } + + ec.MarkLabel (end); + } + + public override void EmitStatement (EmitContext ec) + { + throw new NotImplementedException (); + } + + public override void InjectYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point) + { + // Store the new value into current + var fe = new FieldExpr (((IteratorStorey) storey).CurrentField, loc); + fe.InstanceExpression = new CompilerGeneratedThis (storey.CurrentType, loc); + fe.EmitAssign (ec, expr, false, false); + + base.InjectYield (ec, expr, resume_pc, unwind_protect, resume_point); + + EmitLeave (ec, unwind_protect); + + ec.MarkLabel (resume_point); + } + + public static void CreateIterator (IMethodData method, TypeDefinition parent, Modifiers modifiers) + { + bool is_enumerable; + TypeSpec iterator_type; + + TypeSpec ret = method.ReturnType; + if (ret == null) + return; + + if (!CheckType (ret, parent, out iterator_type, out is_enumerable)) { + parent.Compiler.Report.Error (1624, method.Location, + "The body of `{0}' cannot be an iterator block " + + "because `{1}' is not an iterator interface type", + method.GetSignatureForError (), + ret.GetSignatureForError ()); + return; + } + + ParametersCompiled parameters = method.ParameterInfo; + for (int i = 0; i < parameters.Count; i++) { + Parameter p = parameters [i]; + Parameter.Modifier mod = p.ModFlags; + if ((mod & Parameter.Modifier.RefOutMask) != 0) { + parent.Compiler.Report.Error (1623, p.Location, + "Iterators cannot have ref or out parameters"); + return; + } + + if (p is ArglistParameter) { + parent.Compiler.Report.Error (1636, method.Location, + "__arglist is not allowed in parameter list of iterators"); + return; + } + + if (parameters.Types [i].IsPointer) { + parent.Compiler.Report.Error (1637, p.Location, + "Iterators cannot have unsafe parameters or yield types"); + return; + } + } + + if ((modifiers & Modifiers.UNSAFE) != 0) { + parent.Compiler.Report.Error (1629, method.Location, "Unsafe code may not appear in iterators"); + } + + method.Block = method.Block.ConvertToIterator (method, parent, iterator_type, is_enumerable); + } + + static bool CheckType (TypeSpec ret, TypeContainer parent, out TypeSpec original_iterator_type, out bool is_enumerable) + { + original_iterator_type = null; + is_enumerable = false; + + if (ret.BuiltinType == BuiltinTypeSpec.Type.IEnumerable) { + original_iterator_type = parent.Compiler.BuiltinTypes.Object; + is_enumerable = true; + return true; + } + if (ret.BuiltinType == BuiltinTypeSpec.Type.IEnumerator) { + original_iterator_type = parent.Compiler.BuiltinTypes.Object; + is_enumerable = false; + return true; + } + + InflatedTypeSpec inflated = ret as InflatedTypeSpec; + if (inflated == null) + return false; + + var member_definition = inflated.MemberDefinition; + PredefinedType ptype = parent.Module.PredefinedTypes.IEnumerableGeneric; + + if (ptype.Define () && ptype.TypeSpec.MemberDefinition == member_definition) { + original_iterator_type = inflated.TypeArguments[0]; + is_enumerable = true; + return true; + } + + ptype = parent.Module.PredefinedTypes.IEnumeratorGeneric; + if (ptype.Define () && ptype.TypeSpec.MemberDefinition == member_definition) { + original_iterator_type = inflated.TypeArguments[0]; + is_enumerable = false; + return true; + } + + return false; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/lambda.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/lambda.cs new file mode 100644 index 000000000..7868c6a2c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/lambda.cs @@ -0,0 +1,229 @@ +// +// lambda.cs: support for lambda expressions +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2007-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + public class LambdaExpression : AnonymousMethodExpression + { + // + // The parameters can either be: + // A list of Parameters (explicitly typed parameters) + // An ImplicitLambdaParameter + // + public LambdaExpression (Location loc) + : base (loc) + { + } + + protected override Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type) + { + if (ec.IsInProbingMode) + return this; + + BlockContext bc = new BlockContext (ec.MemberContext, ec.ConstructorBlock, ec.BuiltinTypes.Void) { + CurrentAnonymousMethod = ec.CurrentAnonymousMethod + }; + + Expression args = Parameters.CreateExpressionTree (bc, loc); + Expression expr = Block.CreateExpressionTree (ec); + if (expr == null) + return null; + + Arguments arguments = new Arguments (2); + arguments.Add (new Argument (expr)); + arguments.Add (new Argument (args)); + return CreateExpressionFactoryCall (ec, "Lambda", + new TypeArguments (new TypeExpression (delegate_type, loc)), + arguments); + } + + public override bool HasExplicitParameters { + get { + return Parameters.Count > 0 && !(Parameters.FixedParameters [0] is ImplicitLambdaParameter); + } + } + + protected override ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegateType) + { + if (!delegateType.IsDelegate) + return null; + + AParametersCollection d_params = Delegate.GetParameters (delegateType); + + if (HasExplicitParameters) { + if (!VerifyExplicitParameters (ec, tic, delegateType, d_params)) + return null; + + return Parameters; + } + + // + // If L has an implicitly typed parameter list we make implicit parameters explicit + // Set each parameter of L is given the type of the corresponding parameter in D + // + if (!VerifyParameterCompatibility (ec, tic, delegateType, d_params, ec.IsInProbingMode)) + return null; + + TypeSpec [] ptypes = new TypeSpec [Parameters.Count]; + for (int i = 0; i < d_params.Count; i++) { + // D has no ref or out parameters + if ((d_params.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != 0) + return null; + + TypeSpec d_param = d_params.Types [i]; + + // + // When type inference context exists try to apply inferred type arguments + // + if (tic != null) { + d_param = tic.InflateGenericArgument (ec, d_param); + } + + ptypes [i] = d_param; + ImplicitLambdaParameter ilp = (ImplicitLambdaParameter) Parameters.FixedParameters [i]; + ilp.SetParameterType (d_param); + ilp.Resolve (null, i); + } + + Parameters.Types = ptypes; + return Parameters; + } + + protected override AnonymousMethodBody CompatibleMethodFactory (TypeSpec returnType, TypeSpec delegateType, ParametersCompiled p, ParametersBlock b) + { + return new LambdaMethod (p, b, returnType, delegateType, loc); + } + + protected override bool DoResolveParameters (ResolveContext rc) + { + // + // Only explicit parameters can be resolved at this point + // + if (HasExplicitParameters) { + return Parameters.Resolve (rc); + } + + return true; + } + + public override string GetSignatureForError () + { + return "lambda expression"; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + class LambdaMethod : AnonymousMethodBody + { + public LambdaMethod (ParametersCompiled parameters, + ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type, + Location loc) + : base (parameters, block, return_type, delegate_type, loc) + { + } + + #region Properties + + public override string ContainerType { + get { + return "lambda expression"; + } + } + + #endregion + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + // TODO: nothing ?? + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + BlockContext bc = new BlockContext (ec.MemberContext, Block, ReturnType); + Expression args = parameters.CreateExpressionTree (bc, loc); + Expression expr = Block.CreateExpressionTree (ec); + if (expr == null) + return null; + + Arguments arguments = new Arguments (2); + arguments.Add (new Argument (expr)); + arguments.Add (new Argument (args)); + return CreateExpressionFactoryCall (ec, "Lambda", + new TypeArguments (new TypeExpression (type, loc)), + arguments); + } + } + + // + // This is a return statement that is prepended lambda expression bodies that happen + // to be expressions. Depending on the return type of the delegate this will behave + // as either { expr (); return (); } or { return expr (); } + // + public class ContextualReturn : Return + { + ExpressionStatement statement; + + public ContextualReturn (Expression expr) + : base (expr, expr.StartLocation) + { + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return Expr.CreateExpressionTree (ec); + } + + protected override void DoEmit (EmitContext ec) + { + if (statement != null) { + statement.EmitStatement (ec); + if (unwind_protect) + ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ()); + else { + ec.Emit (OpCodes.Ret); + } + return; + } + + base.DoEmit (ec); + } + + protected override bool DoResolve (BlockContext ec) + { + // + // When delegate returns void, only expression statements can be used + // + if (ec.ReturnType.Kind == MemberKind.Void) { + Expr = Expr.Resolve (ec); + if (Expr == null) + return false; + + statement = Expr as ExpressionStatement; + if (statement == null) + Expr.Error_InvalidExpressionStatement (ec); + + return true; + } + + return base.DoResolve (ec); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/linq.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/linq.cs new file mode 100644 index 000000000..11f01009d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/linq.cs @@ -0,0 +1,904 @@ +// +// linq.cs: support for query expressions +// +// Authors: Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2007-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; + +namespace Mono.CSharp.Linq +{ + public class QueryExpression : AQueryClause + { + public QueryExpression (AQueryClause start) + : base (null, null, start.Location) + { + this.next = start; + } + + public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parentParameter) + { + return next.BuildQueryClause (ec, lSide, parentParameter); + } + + protected override Expression DoResolve (ResolveContext ec) + { + int counter = QueryBlock.TransparentParameter.Counter; + + Expression e = BuildQueryClause (ec, null, null); + if (e != null) + e = e.Resolve (ec); + + // + // Reset counter in probing mode to ensure that all transparent + // identifier anonymous types are created only once + // + if (ec.IsInProbingMode) + QueryBlock.TransparentParameter.Counter = counter; + + return e; + } + + protected override string MethodName { + get { throw new NotSupportedException (); } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public abstract class AQueryClause : ShimExpression + { + protected class QueryExpressionAccess : MemberAccess + { + public QueryExpressionAccess (Expression expr, string methodName, Location loc) + : base (expr, methodName, loc) + { + } + + public QueryExpressionAccess (Expression expr, string methodName, TypeArguments typeArguments, Location loc) + : base (expr, methodName, typeArguments, loc) + { + } + + protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name) + { + ec.Report.Error (1935, loc, "An implementation of `{0}' query expression pattern could not be found. " + + "Are you missing `System.Linq' using directive or `System.Core.dll' assembly reference?", + name); + } + } + + protected class QueryExpressionInvocation : Invocation, OverloadResolver.IErrorHandler + { + public QueryExpressionInvocation (QueryExpressionAccess expr, Arguments arguments) + : base (expr, arguments) + { + } + + protected override MethodGroupExpr DoResolveOverload (ResolveContext ec) + { + MethodGroupExpr rmg = mg.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.None); + return rmg; + } + + protected override Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr) + { + ec.Report.Error (1979, loc, + "Query expressions with a source or join sequence of type `dynamic' are not allowed"); + return null; + } + + #region IErrorHandler Members + + bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous) + { + ec.Report.SymbolRelatedToPreviousError (best); + ec.Report.SymbolRelatedToPreviousError (ambiguous); + ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'", + best.Name, mg.InstanceExpression.GetSignatureForError ()); + return true; + } + + bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index) + { + return false; + } + + bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best) + { + return false; + } + + bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best) + { + var ms = (MethodSpec) best; + TypeSpec source_type = ms.Parameters.ExtensionMethodType; + if (source_type != null) { + Argument a = arguments[0]; + + if (TypeManager.IsGenericType (source_type) && InflatedTypeSpec.ContainsTypeParameter (source_type)) { + TypeInferenceContext tic = new TypeInferenceContext (source_type.TypeArguments); + tic.OutputTypeInference (rc, a.Expr, source_type); + if (tic.FixAllTypes (rc)) { + source_type = source_type.GetDefinition ().MakeGenericType (rc, tic.InferredTypeArguments); + } + } + + if (!Convert.ImplicitConversionExists (rc, a.Expr, source_type)) { + rc.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found", + best.Name, a.Type.GetSignatureForError ()); + return true; + } + } + + if (best.Name == "SelectMany") { + rc.Report.Error (1943, loc, + "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'", + arguments[0].GetSignatureForError ()); + } else { + rc.Report.Error (1942, loc, + "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'", + best.Name.ToLowerInvariant (), best.Name); + } + + return true; + } + + #endregion + } + + public AQueryClause next; + public QueryBlock block; + + protected AQueryClause (QueryBlock block, Expression expr, Location loc) + : base (expr) + { + this.block = block; + this.loc = loc; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + base.CloneTo (clonectx, target); + + AQueryClause t = (AQueryClause) target; + + if (block != null) + t.block = (QueryBlock) clonectx.LookupBlock (block); + + if (next != null) + t.next = (AQueryClause) next.Clone (clonectx); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return expr.Resolve (ec); + } + + public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter) + { + Arguments args = null; + CreateArguments (ec, parameter, ref args); + lSide = CreateQueryExpression (lSide, args); + if (next != null) { + parameter = CreateChildrenParameters (parameter); + + Select s = next as Select; + if (s == null || s.IsRequired (parameter)) + return next.BuildQueryClause (ec, lSide, parameter); + + // Skip transparent select clause if any clause follows + if (next.next != null) + return next.next.BuildQueryClause (ec, lSide, parameter); + } + + return lSide; + } + + protected virtual Parameter CreateChildrenParameters (Parameter parameter) + { + // Have to clone the parameter for any children use, it carries block sensitive data + return parameter.Clone (); + } + + protected virtual void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + args = new Arguments (2); + + LambdaExpression selector = new LambdaExpression (loc); + + block.SetParameter (parameter); + selector.Block = block; + selector.Block.AddStatement (new ContextualReturn (expr)); + + args.Add (new Argument (selector)); + } + + protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments) + { + return new QueryExpressionInvocation ( + new QueryExpressionAccess (lSide, MethodName, loc), arguments); + } + + protected abstract string MethodName { get; } + + public AQueryClause Next { + set { + next = value; + } + } + + public AQueryClause Tail { + get { + return next == null ? this : next.Tail; + } + } + } + + // + // A query clause with an identifier (range variable) + // + public abstract class ARangeVariableQueryClause : AQueryClause + { + sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter + { + public RangeAnonymousTypeParameter (Expression initializer, RangeVariable parameter) + : base (initializer, parameter.Name, parameter.Location) + { + } + + protected override void Error_InvalidInitializer (ResolveContext ec, string initializer) + { + ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'", + Name, initializer); + } + } + + class RangeParameterReference : ParameterReference + { + Parameter parameter; + + public RangeParameterReference (Parameter p) + : base (null, p.Location) + { + this.parameter = p; + } + + protected override Expression DoResolve (ResolveContext ec) + { + pi = ec.CurrentBlock.ParametersBlock.GetParameterInfo (parameter); + return base.DoResolve (ec); + } + } + + protected RangeVariable identifier; + + public RangeVariable IntoVariable { + get { + return identifier; + } + } + + protected ARangeVariableQueryClause (QueryBlock block, RangeVariable identifier, Expression expr, Location loc) + : base (block, expr, loc) + { + this.identifier = identifier; + } + + public RangeVariable Identifier { + get { + return identifier; + } + } + + public FullNamedExpression IdentifierType { get; set; } + + protected Invocation CreateCastExpression (Expression lSide) + { + return new QueryExpressionInvocation ( + new QueryExpressionAccess (lSide, "Cast", new TypeArguments (IdentifierType), loc), null); + } + + protected override Parameter CreateChildrenParameters (Parameter parameter) + { + return new QueryBlock.TransparentParameter (parameter.Clone (), GetIntoVariable ()); + } + + protected static Expression CreateRangeVariableType (ResolveContext rc, Parameter parameter, RangeVariable name, Expression init) + { + var args = new List (2); + + // + // The first argument is the reference to the parameter + // + args.Add (new AnonymousTypeParameter (new RangeParameterReference (parameter), parameter.Name, parameter.Location)); + + // + // The second argument is the linq expression + // + args.Add (new RangeAnonymousTypeParameter (init, name)); + + // + // Create unique anonymous type + // + return new NewAnonymousType (args, rc.MemberContext.CurrentMemberDefinition.Parent, name.Location); + } + + protected virtual RangeVariable GetIntoVariable () + { + return identifier; + } + } + + public sealed class RangeVariable : INamedBlockVariable + { + Block block; + + public RangeVariable (string name, Location loc) + { + Name = name; + Location = loc; + } + + #region Properties + + public Block Block { + get { + return block; + } + set { + block = value; + } + } + + public bool IsDeclared { + get { + return true; + } + } + + public bool IsParameter { + get { + return false; + } + } + + public Location Location { get; private set; } + + public string Name { get; private set; } + + #endregion + + public Expression CreateReferenceExpression (ResolveContext rc, Location loc) + { + // + // We know the variable name is somewhere in the scope. This generates + // an access expression from current block + // + var pb = rc.CurrentBlock.ParametersBlock; + while (true) { + if (pb is QueryBlock) { + for (int i = pb.Parameters.Count - 1; i >= 0; --i) { + var p = pb.Parameters[i]; + if (p.Name == Name) + return pb.GetParameterReference (i, loc); + + Expression expr = null; + var tp = p as QueryBlock.TransparentParameter; + while (tp != null) { + if (expr == null) + expr = pb.GetParameterReference (i, loc); + else + expr = new TransparentMemberAccess (expr, tp.Name); + + if (tp.Identifier == Name) + return new TransparentMemberAccess (expr, Name); + + if (tp.Parent.Name == Name) + return new TransparentMemberAccess (expr, Name); + + tp = tp.Parent as QueryBlock.TransparentParameter; + } + } + } + + if (pb == block) + return null; + + pb = pb.Parent.ParametersBlock; + } + } + } + + public class QueryStartClause : ARangeVariableQueryClause + { + public QueryStartClause (QueryBlock block, Expression expr, RangeVariable identifier, Location loc) + : base (block, identifier, expr, loc) + { + block.AddRangeVariable (identifier); + } + + public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter) + { + if (IdentifierType != null) + expr = CreateCastExpression (expr); + + if (parameter == null) + lSide = expr; + + return next.BuildQueryClause (ec, lSide, new ImplicitLambdaParameter (identifier.Name, identifier.Location)); + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression e = BuildQueryClause (ec, null, null); + return e.Resolve (ec); + } + + protected override string MethodName { + get { throw new NotSupportedException (); } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + + public class GroupBy : AQueryClause + { + Expression element_selector; + QueryBlock element_block; + + public Expression ElementSelector { + get { return this.element_selector; } + } + + public GroupBy (QueryBlock block, Expression elementSelector, QueryBlock elementBlock, Expression keySelector, Location loc) + : base (block, keySelector, loc) + { + // + // Optimizes clauses like `group A by A' + // + if (!elementSelector.Equals (keySelector)) { + this.element_selector = elementSelector; + this.element_block = elementBlock; + } + } + + public Expression SelectorExpression { + get { + return element_selector; + } + } + + protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + base.CreateArguments (ec, parameter, ref args); + + if (element_selector != null) { + LambdaExpression lambda = new LambdaExpression (element_selector.Location); + + element_block.SetParameter (parameter.Clone ()); + lambda.Block = element_block; + lambda.Block.AddStatement (new ContextualReturn (element_selector)); + args.Add (new Argument (lambda)); + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + GroupBy t = (GroupBy) target; + if (element_selector != null) { + t.element_selector = element_selector.Clone (clonectx); + t.element_block = (QueryBlock) element_block.Clone (clonectx); + } + + base.CloneTo (clonectx, t); + } + + protected override string MethodName { + get { return "GroupBy"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Join : SelectMany + { + QueryBlock inner_selector, outer_selector; + + public RangeVariable JoinVariable { + get { return this.GetIntoVariable (); } + } + + public Join (QueryBlock block, RangeVariable lt, Expression inner, QueryBlock outerSelector, QueryBlock innerSelector, Location loc) + : base (block, lt, inner, loc) + { + this.outer_selector = outerSelector; + this.inner_selector = innerSelector; + } + + public QueryBlock InnerSelector { + get { + return inner_selector; + } + } + + public QueryBlock OuterSelector { + get { + return outer_selector; + } + } + + protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + args = new Arguments (4); + + if (IdentifierType != null) + expr = CreateCastExpression (expr); + + args.Add (new Argument (expr)); + + outer_selector.SetParameter (parameter.Clone ()); + var lambda = new LambdaExpression (outer_selector.StartLocation); + lambda.Block = outer_selector; + args.Add (new Argument (lambda)); + + inner_selector.SetParameter (new ImplicitLambdaParameter (identifier.Name, identifier.Location)); + lambda = new LambdaExpression (inner_selector.StartLocation); + lambda.Block = inner_selector; + args.Add (new Argument (lambda)); + + base.CreateArguments (ec, parameter, ref args); + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + Join t = (Join) target; + t.inner_selector = (QueryBlock) inner_selector.Clone (clonectx); + t.outer_selector = (QueryBlock) outer_selector.Clone (clonectx); + base.CloneTo (clonectx, t); + } + + protected override string MethodName { + get { return "Join"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class GroupJoin : Join + { + readonly RangeVariable into; + + public GroupJoin (QueryBlock block, RangeVariable lt, Expression inner, + QueryBlock outerSelector, QueryBlock innerSelector, RangeVariable into, Location loc) + : base (block, lt, inner, outerSelector, innerSelector, loc) + { + this.into = into; + } + + protected override RangeVariable GetIntoVariable () + { + return into; + } + + protected override string MethodName { + get { return "GroupJoin"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Let : ARangeVariableQueryClause + { + public Let (QueryBlock block, RangeVariable identifier, Expression expr, Location loc) + : base (block, identifier, expr, loc) + { + } + + protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + expr = CreateRangeVariableType (ec, parameter, identifier, expr); + base.CreateArguments (ec, parameter, ref args); + } + + protected override string MethodName { + get { return "Select"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Select : AQueryClause + { + public Select (QueryBlock block, Expression expr, Location loc) + : base (block, expr, loc) + { + } + + // + // For queries like `from a orderby a select a' + // the projection is transparent and select clause can be safely removed + // + public bool IsRequired (Parameter parameter) + { + SimpleName sn = expr as SimpleName; + if (sn == null) + return true; + + return sn.Name != parameter.Name; + } + + protected override string MethodName { + get { return "Select"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + public class SelectMany : ARangeVariableQueryClause + { + public SelectMany (QueryBlock block, RangeVariable identifier, Expression expr, Location loc) + : base (block, identifier, expr, loc) + { + } + + protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + if (args == null) { + if (IdentifierType != null) + expr = CreateCastExpression (expr); + + base.CreateArguments (ec, parameter.Clone (), ref args); + } + + Expression result_selector_expr; + QueryBlock result_block; + + var target = GetIntoVariable (); + var target_param = new ImplicitLambdaParameter (target.Name, target.Location); + + // + // When select follows use it as a result selector + // + if (next is Select) { + result_selector_expr = next.Expr; + + result_block = next.block; + result_block.SetParameters (parameter, target_param); + + next = next.next; + } else { + result_selector_expr = CreateRangeVariableType (ec, parameter, target, new SimpleName (target.Name, target.Location)); + + result_block = new QueryBlock (block.Parent, block.StartLocation); + result_block.SetParameters (parameter, target_param); + } + + LambdaExpression result_selector = new LambdaExpression (Location); + result_selector.Block = result_block; + result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr)); + + args.Add (new Argument (result_selector)); + } + + protected override string MethodName { + get { return "SelectMany"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Where : AQueryClause + { + public Where (QueryBlock block, Expression expr, Location loc) + : base (block, expr, loc) + { + } + + protected override string MethodName { + get { return "Where"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class OrderByAscending : AQueryClause + { + public OrderByAscending (QueryBlock block, Expression expr) + : base (block, expr, expr.Location) + { + } + + protected override string MethodName { + get { return "OrderBy"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class OrderByDescending : AQueryClause + { + public OrderByDescending (QueryBlock block, Expression expr) + : base (block, expr, expr.Location) + { + } + + protected override string MethodName { + get { return "OrderByDescending"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ThenByAscending : OrderByAscending + { + public ThenByAscending (QueryBlock block, Expression expr) + : base (block, expr) + { + } + + protected override string MethodName { + get { return "ThenBy"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ThenByDescending : OrderByDescending + { + public ThenByDescending (QueryBlock block, Expression expr) + : base (block, expr) + { + } + + protected override string MethodName { + get { return "ThenByDescending"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Implicit query block + // + public class QueryBlock : ParametersBlock + { + // + // Transparent parameters are used to package up the intermediate results + // and pass them onto next clause + // + public sealed class TransparentParameter : ImplicitLambdaParameter + { + public static int Counter; + const string ParameterNamePrefix = "<>__TranspIdent"; + + public readonly Parameter Parent; + public readonly string Identifier; + + public TransparentParameter (Parameter parent, RangeVariable identifier) + : base (ParameterNamePrefix + Counter++, identifier.Location) + { + Parent = parent; + Identifier = identifier.Name; + } + + public static void Reset () + { + Counter = 0; + } + } + + public QueryBlock (Block parent, Location start) + : base (parent, ParametersCompiled.EmptyReadOnlyParameters, start, Flags.CompilerGenerated) + { + } + + public void AddRangeVariable (RangeVariable variable) + { + variable.Block = this; + TopBlock.AddLocalName (variable.Name, variable, true); + } + + public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason) + { + TopBlock.Report.Error (1931, variable.Location, + "A range variable `{0}' conflicts with a previous declaration of `{0}'", + name); + } + + public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable) + { + TopBlock.Report.Error (1930, variable.Location, + "A range variable `{0}' has already been declared in this scope", + name); + } + + public override void Error_AlreadyDeclaredTypeParameter (string name, Location loc) + { + TopBlock.Report.Error (1948, loc, + "A range variable `{0}' conflicts with a method type parameter", + name); + } + + public void SetParameter (Parameter parameter) + { + base.parameters = new ParametersCompiled (parameter); + base.parameter_info = new ParameterInfo[] { + new ParameterInfo (this, 0) + }; + } + + public void SetParameters (Parameter first, Parameter second) + { + base.parameters = new ParametersCompiled (first, second); + base.parameter_info = new ParameterInfo[] { + new ParameterInfo (this, 0), + new ParameterInfo (this, 1) + }; + } + } + + sealed class TransparentMemberAccess : MemberAccess + { + public TransparentMemberAccess (Expression expr, string name) + : base (expr, name) + { + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + rc.Report.Error (1947, loc, + "A range variable `{0}' cannot be assigned to. Consider using `let' clause to store the value", + Name); + + return null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/literal.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/literal.cs new file mode 100644 index 000000000..1af2d0c9e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/literal.cs @@ -0,0 +1,333 @@ +// +// literal.cs: Literal representation for the IL tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Copyright 2001 Ximian, Inc. +// Copyright 2011 Xamarin Inc +// +// +// Notice that during parsing we create objects of type Literal, but the +// types are not loaded (thats why the Resolve method has to assign the +// type at that point). +// +// Literals differ from the constants in that we know we encountered them +// as a literal in the source code (and some extra rules apply there) and +// they have to be resolved (since during parsing we have not loaded the +// types yet) while constants are created only after types have been loaded +// and are fully resolved when born. +// + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public interface ILiteralConstant + { +#if FULL_AST + char[] ParsedValue { get; set; } +#endif + } + + // + // The null literal + // + // Note: C# specification null-literal is NullLiteral of NullType type + // + public class NullLiteral : NullConstant + { + // + // Default type of null is an object + // + public NullLiteral (Location loc) + : base (InternalType.NullLiteral, loc) + { + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec t, bool expl) + { + if (t.IsGenericParameter) { + ec.Report.Error(403, loc, + "Cannot convert null to the type parameter `{0}' because it could be a value " + + "type. Consider using `default ({0})' instead", t.Name); + return; + } + + if (TypeSpec.IsValueType (t)) { + ec.Report.Error(37, loc, "Cannot convert null to `{0}' because it is a value type", + t.GetSignatureForError ()); + return; + } + + base.Error_ValueCannotBeConverted (ec, t, expl); + } + + public override string GetValueAsLiteral () + { + return "null"; + } + + public override bool IsLiteral { + get { return true; } + } + + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { + return System.Linq.Expressions.Expression.Constant (null); + } + } + + public class BoolLiteral : BoolConstant, ILiteralConstant + { + public BoolLiteral (BuiltinTypes types, bool val, Location loc) + : base (types, val, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class CharLiteral : CharConstant, ILiteralConstant + { + public CharLiteral (BuiltinTypes types, char c, Location loc) + : base (types, c, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class IntLiteral : IntConstant, ILiteralConstant + { + public IntLiteral (BuiltinTypes types, int l, Location loc) + : base (types, l, loc) + { + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + // + // The 0 literal can be converted to an enum value + // + if (Value == 0 && type.IsEnum) { + Constant c = ConvertImplicitly (EnumSpec.GetUnderlyingType (type)); + if (c == null) + return null; + + return new EnumConstant (c, type); + } + + return base.ConvertImplicitly (type); + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class UIntLiteral : UIntConstant, ILiteralConstant + { + public UIntLiteral (BuiltinTypes types, uint l, Location loc) + : base (types, l, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class LongLiteral : LongConstant, ILiteralConstant + { + public LongLiteral (BuiltinTypes types, long l, Location loc) + : base (types, l, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ULongLiteral : ULongConstant, ILiteralConstant + { + public ULongLiteral (BuiltinTypes types, ulong l, Location loc) + : base (types, l, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class FloatLiteral : FloatConstant, ILiteralConstant + { + public FloatLiteral (BuiltinTypes types, float f, Location loc) + : base (types, f, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class DoubleLiteral : DoubleConstant, ILiteralConstant + { + public DoubleLiteral (BuiltinTypes types, double d, Location loc) + : base (types, d, loc) + { + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + if (target.BuiltinType == BuiltinTypeSpec.Type.Float) { + Error_664 (ec, loc, "float", "f"); + return; + } + + if (target.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + Error_664 (ec, loc, "decimal", "m"); + return; + } + + base.Error_ValueCannotBeConverted (ec, target, expl); + } + + static void Error_664 (ResolveContext ec, Location loc, string type, string suffix) + { + ec.Report.Error (664, loc, + "Literal of type double cannot be implicitly converted to type `{0}'. Add suffix `{1}' to create a literal of this type", + type, suffix); + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class DecimalLiteral : DecimalConstant, ILiteralConstant + { + public DecimalLiteral (BuiltinTypes types, decimal d, Location loc) + : base (types, d, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class StringLiteral : StringConstant, ILiteralConstant + { + public StringLiteral (BuiltinTypes types, string s, Location loc) + : base (types, s, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/location.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/location.cs new file mode 100644 index 000000000..a27ebf819 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/location.cs @@ -0,0 +1,883 @@ +// +// location.cs: Keeps track of the location of source code entity +// +// Author: +// Miguel de Icaza +// Atsushi Enomoto +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001 Ximian, Inc. +// Copyright 2005 Novell, Inc. +// + +using System; +using System.Collections.Generic; +using Mono.CompilerServices.SymbolWriter; +using System.Diagnostics; +using System.Linq; + +namespace Mono.CSharp +{ + // + // This is one single source file. + // + public class SourceFile : IEquatable + { + // + // Used by #line directive to track hidden sequence point + // regions + // + struct LocationRegion : IComparable + { + public readonly Location Start; + public readonly Location End; + + public LocationRegion (Location start, Location end) + { + this.Start = start; + this.End = end; + } + + public int CompareTo (LocationRegion other) + { + if (Start.Row == other.Start.Row) + return Start.Column.CompareTo (other.Start.Column); + + return Start.Row.CompareTo (other.Start.Row); + } + + public override string ToString () + { + return Start.ToString () + " - " + End.ToString (); + } + } + + static readonly byte[] MD5Algorith = { 96, 166, 110, 64, 207, 100, 130, 76, 182, 240, 66, 212, 129, 114, 167, 153 }; + + public readonly string Name; + public readonly string FullPathName; + public readonly int Index; + public bool AutoGenerated; + + SourceFileEntry file; + byte[] algGuid, checksum; + List hidden_lines; + + public SourceFile (string name, string path, int index) + { + this.Index = index; + this.Name = name; + this.FullPathName = path; + } + + public byte[] Checksum { + get { + return checksum; + } + } + + public bool HasChecksum { + get { + return checksum != null; + } + } + + public SourceFileEntry SourceFileEntry { + get { + return file; + } + } + + public void SetChecksum (byte[] checksum) + { + SetChecksum (MD5Algorith, checksum); + } + + public void SetChecksum (byte[] algorithmGuid, byte[] checksum) + { + this.algGuid = algorithmGuid; + this.checksum = checksum; + } + + public SourceFileEntry CreateSymbolInfo (MonoSymbolFile symwriter) + { + if (hidden_lines != null) + hidden_lines.Sort (); + + file = new SourceFileEntry (symwriter, FullPathName, algGuid, checksum); + if (AutoGenerated) + file.SetAutoGenerated (); + + return file; + } + + public bool Equals (SourceFile other) + { + return FullPathName == other.FullPathName; + } + + public bool IsHiddenLocation (Location loc) + { + if (hidden_lines == null) + return false; + + int index = hidden_lines.BinarySearch (new LocationRegion (loc, loc)); + index = ~index; + if (index > 0) { + var found = hidden_lines[index - 1]; + if (loc.Row < found.End.Row) + return true; + } + + return false; + } + + public void RegisterHiddenScope (Location start, Location end) + { + if (hidden_lines == null) + hidden_lines = new List (); + + hidden_lines.Add (new LocationRegion (start, end)); + } + + public override string ToString () + { + return String.Format ("SourceFile ({0}:{1}:{2})", Name, FullPathName, Index); + } + } + + /// + /// Keeps track of the location in the program + /// + /// + /// + /// This uses a compact representation and a couple of auxiliary + /// structures to keep track of tokens to (file,line and column) + /// mappings. The usage of the bits is: + /// + /// - 16 bits for "checkpoint" which is a mixed concept of + /// file and "line segment" + /// - 8 bits for line delta (offset) from the line segment + /// - 8 bits for column number. + /// + /// http://lists.ximian.com/pipermail/mono-devel-list/2004-December/009508.html + /// + public struct Location : IEquatable + { + struct Checkpoint { + public readonly int LineOffset; + public readonly int File; + + public Checkpoint (int file, int line) + { + File = file; + LineOffset = line - (int) (line % (1 << line_delta_bits)); + } + } + +#if FULL_AST + readonly long token; + + const int column_bits = 24; + const int line_delta_bits = 24; +#else + readonly int token; + + const int column_bits = 8; + const int line_delta_bits = 8; +#endif + const int checkpoint_bits = 16; + + const int column_mask = (1 << column_bits) - 1; + const int max_column = column_mask; + + static List source_list; + static Checkpoint [] checkpoints; + static int checkpoint_index; + + public readonly static Location Null = new Location (); + public static bool InEmacs; + + static Location () + { + Reset (); + } + + public static void Reset () + { + source_list = new List (); + checkpoint_index = 0; + } + + public static void AddFile (SourceFile file) + { + source_list.Add (file); + } + + // + // After adding all source files we want to compile with AddFile(), this method + // must be called to `reserve' an appropriate number of bits in the token for the + // source file. We reserve some extra space for files we encounter via #line + // directives while parsing. + // + static public void Initialize (List files) + { +#if NET_4_0 || MOBILE_DYNAMIC + source_list.AddRange (files); +#else + source_list.AddRange (files.ToArray ()); +#endif + + checkpoints = new Checkpoint [System.Math.Max (1, source_list.Count * 2)]; + if (checkpoints.Length > 0) + checkpoints [0] = new Checkpoint (0, 0); + } + + public Location (SourceFile file, int row, int column) + { + if (row <= 0) + token = 0; + else { + if (column > max_column) + column = max_column; + + long target = -1; + long delta = 0; + + // TODO: For eval only, need better handling of empty + int file_index = file == null ? 0 : file.Index; + + // FIXME: This value is certainly wrong but what was the intension + int max = checkpoint_index < 10 ? + checkpoint_index : 10; + for (int i = 0; i < max; i++) { + int offset = checkpoints [checkpoint_index - i].LineOffset; + delta = row - offset; + if (delta >= 0 && + delta < (1 << line_delta_bits) && + checkpoints[checkpoint_index - i].File == file_index) { + target = checkpoint_index - i; + break; + } + } + if (target == -1) { + AddCheckpoint (file_index, row); + target = checkpoint_index; + delta = row % (1 << line_delta_bits); + } + + long l = column + + (delta << column_bits) + + (target << (line_delta_bits + column_bits)); +#if FULL_AST + token = l; +#else + token = l > 0xFFFFFFFF ? 0 : (int) l; +#endif + } + } + + public static Location operator - (Location loc, int columns) + { + return new Location (loc.SourceFile, loc.Row, loc.Column - columns); + } + + static void AddCheckpoint (int file, int row) + { + if (checkpoints.Length == ++checkpoint_index) { + Array.Resize (ref checkpoints, checkpoint_index * 2); + } + checkpoints [checkpoint_index] = new Checkpoint (file, row); + } + + string FormatLocation (string fileName) + { + if (column_bits == 0 || InEmacs) + return fileName + "(" + Row.ToString () + "):"; + + return fileName + "(" + Row.ToString () + "," + Column.ToString () + + (Column == max_column ? "+):" : "):"); + } + + public override string ToString () + { + return FormatLocation (Name); + } + + public string ToStringFullName () + { + return FormatLocation (NameFullPath); + } + + /// + /// Whether the Location is Null + /// + public bool IsNull { + get { return token == 0; } + } + + public string Name { + get { + int index = File; + + if (token == 0 || index <= 0) + return null; + + SourceFile file = source_list [index - 1]; + return file.Name; + } + } + + public string NameFullPath { + get { + int index = File; + if (token == 0 || index <= 0) + return null; + + return source_list[index - 1].FullPathName; + } + } + + int CheckpointIndex { + get { + const int checkpoint_mask = (1 << checkpoint_bits) - 1; + return ((int) (token >> (line_delta_bits + column_bits))) & checkpoint_mask; + } + } + + public int Row { + get { + if (token == 0) + return 1; + + int offset = checkpoints[CheckpointIndex].LineOffset; + + const int line_delta_mask = (1 << column_bits) - 1; + return offset + (((int)(token >> column_bits)) & line_delta_mask); + } + } + + public int Column { + get { + if (token == 0) + return 1; + return (int) (token & column_mask); + } + } + + public int File { + get { + if (token == 0) + return 0; +if (checkpoints.Length <= CheckpointIndex) throw new Exception (String.Format ("Should not happen. Token is {0:X04}, checkpoints are {1}, index is {2}", token, checkpoints.Length, CheckpointIndex)); + return checkpoints [CheckpointIndex].File; + } + } + + // The ISymbolDocumentWriter interface is used by the symbol writer to + // describe a single source file - for each source file there's exactly + // one corresponding ISymbolDocumentWriter instance. + // + // This class has an internal hash table mapping source document names + // to such ISymbolDocumentWriter instances - so there's exactly one + // instance per document. + // + // This property returns the ISymbolDocumentWriter instance which belongs + // to the location's source file. + // + // If we don't have a symbol writer, this property is always null. + public SourceFile SourceFile { + get { + int index = File; + if (index == 0) + return null; + return source_list [index - 1]; + } + } + + #region IEquatable Members + + public bool Equals (Location other) + { + return this.token == other.token; + } + + #endregion + } + + public class SpecialsBag + { + public enum CommentType + { + Single, + Multi, + Documentation, + InactiveCode + } + + public bool Suppress { + get; + set; + } + + public class SpecialVisitor + { + public virtual void Visit (Comment comment) + { + } + public virtual void Visit (NewLineToken newLineToken) + { + } + public virtual void Visit (PreProcessorDirective preProcessorDirective) + { + } + } + public abstract class SpecialBase + { + public abstract void Accept (SpecialVisitor visitor); + } + + public class Comment : SpecialBase + { + public readonly CommentType CommentType; + public readonly bool StartsLine; + public readonly int Line; + public readonly int Col; + public readonly int EndLine; + public readonly int EndCol; + public readonly string Content; + + public Comment (CommentType commentType, bool startsLine, int line, int col, int endLine, int endCol, string content) + { + this.CommentType = commentType; + this.StartsLine = startsLine; + this.Line = line; + this.Col = col; + this.EndLine = endLine; + this.EndCol = endCol; + this.Content = content; + } + + public override string ToString () + { + return string.Format ("[Comment: CommentType={0}, Line={1}, Col={2}, EndLine={3}, EndCol={4}, Content={5}]", CommentType, Line, Col, EndLine, EndCol, Content); + } + + public override void Accept (SpecialVisitor visitor) + { + visitor.Visit (this); + } + } + + public class NewLineToken : SpecialBase + { + public readonly int Line; + public readonly int Col; + public readonly NewLine NewLine; + + public NewLineToken (int line, int col, NewLine newLine) + { + this.Line = line; + this.Col = col; + this.NewLine = newLine; + } + + public override void Accept (SpecialVisitor visitor) + { + visitor.Visit (this); + } + } + + public class PragmaPreProcessorDirective : PreProcessorDirective + { + public bool Disalbe { get; set; } + + public int WarningColumn { + get; + set; + } + + public int DisableRestoreColumn { + get; + set; + } + + public List Codes = new List (); + + public PragmaPreProcessorDirective (int line, int col, int endLine, int endCol, Tokenizer.PreprocessorDirective cmd, string arg) : base (line, col, endLine, endCol, cmd, arg) + { + } + } + + public class LineProcessorDirective : PreProcessorDirective + { + public int LineNumber { get; set; } + public string FileName { get; set; } + + public LineProcessorDirective (int line, int col, int endLine, int endCol, Tokenizer.PreprocessorDirective cmd, string arg) : base (line, col, endLine, endCol, cmd, arg) + { + } + } + + public class PreProcessorDirective : SpecialBase + { + public readonly int Line; + public readonly int Col; + public readonly int EndLine; + public readonly int EndCol; + + public readonly Tokenizer.PreprocessorDirective Cmd; + public readonly string Arg; + + public bool Take = true; + + public PreProcessorDirective (int line, int col, int endLine, int endCol, Tokenizer.PreprocessorDirective cmd, string arg) + { + this.Line = line; + this.Col = col; + this.EndLine = endLine; + this.EndCol = endCol; + this.Cmd = cmd; + this.Arg = arg; + } + + public override void Accept (SpecialVisitor visitor) + { + visitor.Visit (this); + } + + public override string ToString () + { + return string.Format ("[PreProcessorDirective: Line={0}, Col={1}, EndLine={2}, EndCol={3}, Cmd={4}, Arg={5}]", Line, Col, EndLine, EndCol, Cmd, Arg); + } + } + + public readonly List Specials = new List (); + + CommentType curComment; + bool startsLine; + int startLine, startCol; + System.Text.StringBuilder contentBuilder = new System.Text.StringBuilder (); + + [Conditional ("FULL_AST")] + public void StartComment (CommentType type, bool startsLine, int startLine, int startCol) + { + if (Suppress) + return; + inComment = true; + curComment = type; + this.startsLine = startsLine; + this.startLine = startLine; + this.startCol = startCol; + contentBuilder.Length = 0; + } + + [Conditional ("FULL_AST")] + public void PushCommentChar (int ch) + { + if (Suppress) + return; + if (ch < 0) + return; + contentBuilder.Append ((char)ch); + } + [Conditional ("FULL_AST")] + public void PushCommentString (string str) + { + if (Suppress) + return; + contentBuilder.Append (str); + } + + bool inComment; + [Conditional ("FULL_AST")] + public void EndComment (int endLine, int endColumn) + { + if (Suppress) + return; + if (!inComment) + return; + inComment = false; + // Ignore empty comments + if (startLine == endLine && startCol == endColumn) + return; + Specials.Add (new Comment (curComment, startsLine, startLine, startCol, endLine, endColumn, contentBuilder.ToString ())); + } + + [Conditional ("FULL_AST")] + public void AddPreProcessorDirective (int startLine, int startCol, int endLine, int endColumn, Tokenizer.PreprocessorDirective cmd, string arg) + { + if (Suppress) + return; + if (inComment) + EndComment (startLine, startCol); + switch (cmd) { + case Tokenizer.PreprocessorDirective.Pragma: + Specials.Add (new PragmaPreProcessorDirective (startLine, startCol, endLine, endColumn, cmd, arg)); + break; + case Tokenizer.PreprocessorDirective.Line: + Specials.Add (new LineProcessorDirective (startLine, startCol, endLine, endColumn, cmd, arg)); + break; + default: + Specials.Add (new PreProcessorDirective (startLine, startCol, endLine, endColumn, cmd, arg)); + break; + } + } + + #if FULL_AST + public PragmaPreProcessorDirective SetPragmaDisable(bool disable) + { + if (Suppress) + return null; + var pragmaDirective = Specials [Specials.Count - 1] as PragmaPreProcessorDirective; + if (pragmaDirective == null) + return null; + pragmaDirective.Disalbe = disable; + return pragmaDirective; + } + #endif + + public PragmaPreProcessorDirective GetPragmaPreProcessorDirective() + { + if (Suppress) + return null; + return Specials [Specials.Count - 1] as PragmaPreProcessorDirective; + } + + + public LineProcessorDirective GetCurrentLineProcessorDirective() + { + if (Suppress) + return null; + return Specials [Specials.Count - 1] as LineProcessorDirective; + } + + public enum NewLine { Unix, Windows } + + int lastNewLine = -1; + int lastNewCol = -1; + [Conditional ("FULL_AST")] + public void AddNewLine (int line, int col, NewLine newLine) + { + if (Suppress) + return; + if (line == lastNewLine && col == lastNewCol) + return; + lastNewLine = line; + lastNewCol = col; + Specials.Add (new NewLineToken (line, col, newLine)); + } + + public void SkipIf () + { + if (Specials.Count > 0) { + var directive = Specials[Specials.Count - 1] as PreProcessorDirective; + if (directive != null) + directive.Take = false; + } + } + } + + // + // A bag of additional locations to support full ast tree + // + public class LocationsBag + { + public class MemberLocations + { + public IList> Modifiers { get; internal set; } + List locations; + + public MemberLocations (IList> mods, IEnumerable locs) + { + Modifiers = mods; + locations = locs != null ? new List (locs) : null; +/* + public readonly IList> Modifiers; + List locations; + + public MemberLocations (IList> mods) + { + Modifiers = mods; + } + + public MemberLocations (IList> mods, Location loc) + : this (mods) + { + AddLocations (loc); + } + + public MemberLocations (IList> mods, Location[] locs) + : this (mods) + { + AddLocations (locs); + } + + public MemberLocations (IList> mods, List locs) + : this (mods) + { + locations = locs;*/ + } + + #region Properties + + public Location this [int index] { + get { + return locations [index]; + } + } + + public int Count { + get { + return locations != null ? locations.Count : 0; + } + } + + #endregion + + public void AddLocations (Location loc) + { + if (locations == null) { + locations = new List (); + } + + locations.Add (loc); + } + + public void AddLocations (params Location[] additional) + + { + + AddLocations ((IEnumerable)additional); + + } + public void AddLocations (IEnumerable additional) + { + if (additional == null) + return; + if (locations == null) { + locations = new List (additional); + } else { + locations.AddRange (additional); + } + } + } + + public MemberCore LastMember { + get; + private set; + } + + Dictionary> simple_locs = new Dictionary> (ReferenceEquality.Default); + Dictionary member_locs = new Dictionary (ReferenceEquality.Default); + + [Conditional ("FULL_AST")] + public void AddLocation (object element, params Location[] locations) + { + AddLocation (element, (IEnumerable)locations); + } + + [Conditional ("FULL_AST")] + public void AddLocation (object element, IEnumerable locations) + { + if (element == null || locations == null) + return; + List found; + if (!simple_locs.TryGetValue (element, out found)) { + simple_locs.Add (element, new List (locations)); + return; + } + found.AddRange(locations); + } + + [Conditional ("FULL_AST")] + public void InsertLocation (object element, int index, Location location) + { + List found; + if (!simple_locs.TryGetValue (element, out found)) { + found = new List (); + simple_locs.Add (element, found); + } + + found.Insert (index, location); + } + + [Conditional ("FULL_AST")] + public void AddStatement (object element, params Location[] locations) + { + if (element == null) + return; + if (locations.Length == 0) + throw new ArgumentException ("Statement is missing semicolon location"); + simple_locs.Add (element, new List(locations)); + } + + [Conditional ("FULL_AST")] + public void AddMember (MemberCore member, IList> modLocations, params Location[] locations) + { + LastMember = member; + if (member == null) + return; + + MemberLocations existing; + if (member_locs.TryGetValue (member, out existing)) { + existing.Modifiers = modLocations; + existing.AddLocations (locations); + return; + } + member_locs.Add (member, new MemberLocations (modLocations, locations)); + } + + [Conditional ("FULL_AST")] + public void AddMember (MemberCore member, IList> modLocations, IEnumerable locations) + { + LastMember = member; + if (member == null) + return; + + MemberLocations existing; + if (member_locs.TryGetValue (member, out existing)) { + existing.Modifiers = modLocations; + existing.AddLocations (locations); + return; + } + member_locs.Add (member, new MemberLocations (modLocations, locations)); + } + + [Conditional ("FULL_AST")] + public void AppendToMember (MemberCore existing, params Location[] locations) + { + AppendToMember (existing, (IEnumerable)locations); + + } + + [Conditional ("FULL_AST")] + public void AppendToMember (MemberCore existing, IEnumerable locations) + { + if (existing == null) + return; + MemberLocations member; + if (member_locs.TryGetValue (existing, out member)) { + member.AddLocations (locations); + return; + } + member_locs.Add (existing, new MemberLocations (null, locations)); + } + + public List GetLocations (object element) + { + if (element == null) + return null; + List found; + simple_locs.TryGetValue (element, out found); + return found; + } + + public MemberLocations GetMemberLocation (MemberCore element) + { + MemberLocations found; + member_locs.TryGetValue (element, out found); + return found; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/membercache.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/membercache.cs new file mode 100644 index 000000000..3837af2c2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/membercache.cs @@ -0,0 +1,1501 @@ +// +// membercache.cs: A container for all member lookups +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2010 Novell, Inc +// Copyright 2011 Xamarin Inc +// +// + +using System; +using System.Collections.Generic; + +namespace Mono.CSharp { + + [Flags] + public enum MemberKind + { + Constructor = 1, + Event = 1 << 1, + Field = 1 << 2, + Method = 1 << 3, + Property = 1 << 4, + Indexer = 1 << 5, + Operator = 1 << 6, + Destructor = 1 << 7, + + Class = 1 << 11, + Struct = 1 << 12, + Delegate = 1 << 13, + Enum = 1 << 14, + Interface = 1 << 15, + TypeParameter = 1 << 16, + + ArrayType = 1 << 19, + PointerType = 1 << 20, + InternalCompilerType = 1 << 21, + MissingType = 1 << 22, + Void = 1 << 23, + Namespace = 1 << 24, + + NestedMask = Class | Struct | Delegate | Enum | Interface, + GenericMask = Method | Class | Struct | Delegate | Interface, + MaskType = Constructor | Event | Field | Method | Property | Indexer | Operator | Destructor | NestedMask + } + + [Flags] + public enum BindingRestriction + { + None = 0, + + // Inspect only queried type members + DeclaredOnly = 1 << 1, + + // Exclude static + InstanceOnly = 1 << 2, + + NoAccessors = 1 << 3, + + // Member has to be override + OverrideOnly = 1 << 4 + } + + public struct MemberFilter : IEquatable + { + public readonly string Name; + public readonly MemberKind Kind; + public readonly AParametersCollection Parameters; + public readonly TypeSpec MemberType; + public readonly int Arity; // -1 to ignore the check + + public MemberFilter (MethodSpec m) + { + Name = m.Name; + Kind = MemberKind.Method; + Parameters = m.Parameters; + MemberType = m.ReturnType; + Arity = m.Arity; + } + + public MemberFilter (string name, int arity, MemberKind kind, AParametersCollection param, TypeSpec type) + { + Name = name; + Kind = kind; + Parameters = param; + MemberType = type; + this.Arity = arity; + } + + public static MemberFilter Constructor (AParametersCollection param) + { + return new MemberFilter (Mono.CSharp.Constructor.ConstructorName, 0, MemberKind.Constructor, param, null); + } + + public static MemberFilter Property (string name, TypeSpec type) + { + return new MemberFilter (name, 0, MemberKind.Property, null, type); + } + + public static MemberFilter Field (string name, TypeSpec type) + { + return new MemberFilter (name, 0, MemberKind.Field, null, type); + } + + public static MemberFilter Method (string name, int arity, AParametersCollection param, TypeSpec type) + { + return new MemberFilter (name, arity, MemberKind.Method, param, type); + } + + #region IEquatable Members + + public bool Equals (MemberSpec other) + { + // Is the member of the correct type ? + // TODO: Isn't this redundant ? + if ((other.Kind & Kind & MemberKind.MaskType) == 0) + return false; + + // Check arity when not disabled + if (Arity >= 0 && Arity != other.Arity) + return false; + + if (Parameters != null) { + if (other is IParametersMember) { + var other_param = ((IParametersMember) other).Parameters; + if (!TypeSpecComparer.Override.IsEqual (Parameters, other_param)) + return false; + } else { + return false; + } + } + + if (MemberType != null) { + if (other is IInterfaceMemberSpec) { + var other_type = ((IInterfaceMemberSpec) other).MemberType; + if (!TypeSpecComparer.Override.IsEqual (other_type, MemberType)) + return false; + } else { + return false; + } + } + + return true; + } + + #endregion + } + + // + // The MemberCache is the main members container used by compiler. It contains + // all members imported or defined during compilation using on demand filling + // process. Inflated containers are also using MemberCache to make inflated + // members look like normal definition. + // + // All of the methods are performance and memory sensitive as the MemberCache + // is the underlying engine of all member based operations. + // + public class MemberCache + { + [Flags] + enum StateFlags + { + HasConversionOperator = 1 << 1, + HasUserOperator = 1 << 2 + } + + readonly Dictionary> member_hash; + Dictionary locase_members; + IList missing_abstract; + StateFlags state; // TODO: Move to TypeSpec or ITypeDefinition + + public static readonly string IndexerNameAlias = ""; + + public static readonly MemberCache Empty = new MemberCache (0); + + public MemberCache () + : this (16) + { + } + + public MemberCache (int capacity) + { + member_hash = new Dictionary> (capacity); + } + + public MemberCache (MemberCache cache) + : this (cache.member_hash.Count) + { + this.state = cache.state; + } + + // + // Creates a new MemberCache for the given `container'. + // + public MemberCache (TypeContainer container) + : this () // TODO: Optimize the size + { + } + + // + // For cases where we need to union cache members + // + public void AddBaseType (TypeSpec baseType) + { + var cache = baseType.MemberCache; + + IList list; + foreach (var entry in cache.member_hash) { + if (!member_hash.TryGetValue (entry.Key, out list)) { + if (entry.Value.Count == 1) { + list = entry.Value; + } else { + list = new List (entry.Value); + } + + member_hash.Add (entry.Key, list); + continue; + } + + foreach (var ce in entry.Value) { + if (list.Contains (ce)) + continue; + + if (list is MemberSpec[]) { + list = new List { list [0] }; + member_hash[entry.Key] = list; + } + + list.Add (ce); + } + } + } + + // + // Member-cache does not contain base members but it does + // contain all base interface members, so the Lookup code + // can use simple inheritance rules. + // + // Does not work recursively because of generic interfaces + // + public void AddInterface (TypeSpec iface) + { + var cache = iface.MemberCache; + + IList list; + foreach (var entry in cache.member_hash) { + if (!member_hash.TryGetValue (entry.Key, out list)) { + if (entry.Value.Count == 1) { + list = entry.Value; + } else { + list = new List (entry.Value); + } + + member_hash.Add (entry.Key, list); + continue; + } + + foreach (var ce in entry.Value) { + // + // When two or more different base interfaces implemenent common + // interface + // + // I : IA, IFoo + // IA : IFoo + // + if (list.Contains (ce)) + continue; + + if (AddInterfaceMember (ce, ref list)) + member_hash[entry.Key] = list; + } + } + } + + public void AddMember (InterfaceMemberBase imb, string exlicitName, MemberSpec ms) + { + // Explicit names cannot be looked-up but can be used for + // collision checking (no name mangling needed) + if (imb.IsExplicitImpl) + AddMember (exlicitName, ms, false); + else + AddMember (ms); + } + + // + // Add non-explicit member to member cache + // + public void AddMember (MemberSpec ms) + { + AddMember (GetLookupName (ms), ms, false); + } + + void AddMember (string name, MemberSpec member, bool removeHiddenMembers) + { + if (member.Kind == MemberKind.Operator) { + var dt = member.DeclaringType; + + + // + // Some core types have user operators but they cannot be used like normal + // user operators as they are predefined and therefore having different + // rules (e.g. binary operators) by not setting the flag we hide them for + // user conversions + // + if (!BuiltinTypeSpec.IsPrimitiveType (dt) || dt.BuiltinType == BuiltinTypeSpec.Type.Char) { + switch (dt.BuiltinType) { + case BuiltinTypeSpec.Type.String: + case BuiltinTypeSpec.Type.Delegate: + case BuiltinTypeSpec.Type.MulticastDelegate: + break; + default: + if (name == Operator.GetMetadataName (Operator.OpType.Implicit) || name == Operator.GetMetadataName (Operator.OpType.Explicit)) { + state |= StateFlags.HasConversionOperator; + } else { + state |= StateFlags.HasUserOperator; + } + + break; + } + } + } + + IList list; + if (!member_hash.TryGetValue (name, out list)) { + member_hash.Add (name, new MemberSpec[] { member }); + return; + } + + if (removeHiddenMembers && member.DeclaringType.IsInterface) { + if (AddInterfaceMember (member, ref list)) + member_hash[name] = list; + } else { + if (list.Count == 1) { + list = new List { list[0] }; + member_hash[name] = list; + } + + list.Add (member); + } + } + + public void AddMemberImported (MemberSpec ms) + { + AddMember (GetLookupName (ms), ms, true); + } + + // + // Ignores any base interface member which can be hidden + // by this interface + // + static bool AddInterfaceMember (MemberSpec member, ref IList existing) + { + var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : ParametersCompiled.EmptyReadOnlyParameters; + + // + // interface IA : IB { int Prop { set; } } + // interface IB { bool Prop { get; } } + // + // IB.Prop is never accessible from IA interface + // + for (int i = 0; i < existing.Count; ++i) { + var entry = existing[i]; + + if (entry.Arity != member.Arity) + continue; + + if (entry is IParametersMember) { + var entry_param = ((IParametersMember) entry).Parameters; + if (!TypeSpecComparer.Override.IsEqual (entry_param, member_param)) + continue; + } + + if (member.DeclaringType.ImplementsInterface (entry.DeclaringType, false)) { + if (existing.Count == 1) { + existing = new MemberSpec[] { member }; + return true; + } + + existing.RemoveAt (i--); + continue; + } + + if ((entry.DeclaringType == member.DeclaringType && entry.IsAccessor == member.IsAccessor) || + entry.DeclaringType.ImplementsInterface (member.DeclaringType, false)) + return false; + } + + if (existing.Count == 1) { + existing = new List { existing[0], member }; + return true; + } + + existing.Add (member); + return false; + } + + public static MemberSpec FindMember (TypeSpec container, MemberFilter filter, BindingRestriction restrictions) + { + do { + IList applicable; + if (container.MemberCache.member_hash.TryGetValue (filter.Name, out applicable)) { + // Start from the end because interface members are in reverse order + for (int i = applicable.Count - 1; i >= 0; i--) { + var entry = applicable [i]; + + if ((restrictions & BindingRestriction.InstanceOnly) != 0 && entry.IsStatic) + continue; + + if ((restrictions & BindingRestriction.NoAccessors) != 0 && entry.IsAccessor) + continue; + + if ((restrictions & BindingRestriction.OverrideOnly) != 0 && (entry.Modifiers & Modifiers.OVERRIDE) == 0) + continue; + + if (!filter.Equals (entry)) + continue; + + if ((restrictions & BindingRestriction.DeclaredOnly) != 0 && container.IsInterface && entry.DeclaringType != container) + continue; + + return entry; + } + } + + if ((restrictions & BindingRestriction.DeclaredOnly) != 0) + break; + + container = container.BaseType; + } while (container != null); + + return null; + } + + // + // A special method to work with member lookup only. It returns a list of all members named @name + // starting from @container. It's very performance sensitive + // + // declaredOnlyClass cannot be used interfaces. Manual filtering is required because names are + // compacted + // + public static IList FindMembers (TypeSpec container, string name, bool declaredOnlyClass) + { + IList applicable; + + do { + if (container.MemberCache.member_hash.TryGetValue (name, out applicable) || declaredOnlyClass) + return applicable; + + container = container.BaseType; + } while (container != null); + + return null; + } + + // + // Finds the nested type in container + // + public static TypeSpec FindNestedType (TypeSpec container, string name, int arity) + { + IList applicable; + TypeSpec best_match = null; + do { +#if !FULL_AOT_RUNTIME + // TODO: Don't know how to handle this yet + // When resolving base type of nested type, parent type must have + // base type resolved to scan full hierarchy correctly + // Similarly MemberCacheTypes will inflate BaseType and Interfaces + // based on type definition + var tc = container.MemberDefinition as TypeContainer; + if (tc != null) + tc.DefineContainer (); +#endif + + if (container.MemberCacheTypes.member_hash.TryGetValue (name, out applicable)) { + for (int i = applicable.Count - 1; i >= 0; i--) { + var entry = applicable[i]; + if ((entry.Kind & MemberKind.NestedMask) == 0) + continue; + + var ts = (TypeSpec) entry; + if (arity == ts.Arity) + return ts; + + if (arity < 0) { + if (best_match == null) { + best_match = ts; + } else if (System.Math.Abs (ts.Arity + arity) < System.Math.Abs (ts.Arity + arity)) { + best_match = ts; + } + } + } + } + + container = container.BaseType; + } while (container != null); + + return best_match; + } + + // + // Looks for extension methods with defined name and extension type + // + public List FindExtensionMethods (IMemberContext invocationContext, string name, int arity) + { + IList entries; + if (!member_hash.TryGetValue (name, out entries)) + return null; + + List candidates = null; + foreach (var entry in entries) { + if (entry.Kind != MemberKind.Method || (arity > 0 && entry.Arity != arity)) + continue; + + var ms = (MethodSpec) entry; + if (!ms.IsExtensionMethod) + continue; + + if (!ms.IsAccessible (invocationContext)) + continue; + + // + // Extension methods cannot be nested hence checking parent is enough + // + if ((ms.DeclaringType.Modifiers & Modifiers.INTERNAL) != 0 && !ms.DeclaringType.MemberDefinition.IsInternalAsPublic (invocationContext.Module.DeclaringAssembly)) + continue; + + if (candidates == null) + candidates = new List (); + candidates.Add (ms); + } + + return candidates; + } + + // + // Returns base members of @member member if no exact match is found @bestCandidate returns + // the best match + // + public static MemberSpec FindBaseMember (MemberCore member, out MemberSpec bestCandidate, ref bool overrides) + { + bestCandidate = null; + var container = member.Parent.PartialContainer.Definition; + if (!container.IsInterface) { + container = container.BaseType; + + // It can happen for a user definition of System.Object + if (container == null) + return null; + } + + string name = GetLookupName (member); + var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : null; + + var mkind = GetMemberCoreKind (member); + bool member_with_accessors = mkind == MemberKind.Indexer || mkind == MemberKind.Property; + + IList applicable; + MemberSpec ambig_candidate = null; + + do { + if (container.MemberCache.member_hash.TryGetValue (name, out applicable)) { + for (int i = 0; i < applicable.Count; ++i) { + var entry = applicable [i]; + + if ((entry.Modifiers & Modifiers.PUBLIC) == 0 && !entry.IsAccessible (member)) + continue; + + // + // Isn't the member of same kind ? + // + if ((entry.Kind & ~MemberKind.Destructor & mkind & MemberKind.MaskType) == 0) { + // Destructors are ignored as they cannot be overridden by user + if ((entry.Kind & MemberKind.Destructor) != 0) + continue; + + // A method with different arity does not hide base member + if (mkind != MemberKind.Method && member.MemberName.Arity != entry.Arity) + continue; + + bestCandidate = entry; + return null; + } + + // + // Same kind of different arity is valid + // + if (member.MemberName.Arity != entry.Arity) { + continue; + } + + if ((entry.Kind & mkind & (MemberKind.Method | MemberKind.Indexer)) != 0) { + if (entry.IsAccessor != member is AbstractPropertyEventMethod) + continue; + + var pm = entry as IParametersMember; + if (!TypeSpecComparer.Override.IsEqual (pm.Parameters, member_param)) + continue; + } + + // + // Skip override for member with accessors. It may not fully implement the base member + // but keep flag we found an implementation in case the base member is abstract + // + if (member_with_accessors && ((entry.Modifiers & (Modifiers.OVERRIDE | Modifiers.SEALED)) == Modifiers.OVERRIDE)) { + // + // Set candidate to override implementation to flag we found an implementation + // + overrides = true; + continue; + } + + // + // For members with parameters we can encounter an ambiguous candidates (they match exactly) + // because generic type parameters could be inflated into same types + // + if (ambig_candidate == null && (entry.Kind & mkind & (MemberKind.Method | MemberKind.Indexer)) != 0) { + bestCandidate = null; + ambig_candidate = entry; + continue; + } + + bestCandidate = ambig_candidate; + return entry; + } + } + + if (container.IsInterface || ambig_candidate != null) + break; + + container = container.BaseType; + } while (container != null); + + return ambig_candidate; + } + + // + // Returns inflated version of MemberSpec, it works similarly to + // SRE TypeBuilder.GetMethod + // + public static T GetMember (TypeSpec container, T spec) where T : MemberSpec + { + IList applicable; + if (container.MemberCache.member_hash.TryGetValue (GetLookupName (spec), out applicable)) { + for (int i = applicable.Count - 1; i >= 0; i--) { + var entry = applicable[i]; + if (entry.MemberDefinition == spec.MemberDefinition) + return (T) entry; + } + } + + throw new InternalErrorException ("Missing member `{0}' on inflated type `{1}'", + spec.GetSignatureForError (), container.GetSignatureForError ()); + } + + static MemberKind GetMemberCoreKind (MemberCore member) + { + if (member is FieldBase) + return MemberKind.Field; + if (member is Indexer) + return MemberKind.Indexer; + if (member is Class) + return MemberKind.Class; + if (member is Struct) + return MemberKind.Struct; + if (member is Destructor) + return MemberKind.Destructor; + if (member is Method) + return MemberKind.Method; + if (member is Property) + return MemberKind.Property; + if (member is EventField) + return MemberKind.Event; + if (member is Interface) + return MemberKind.Interface; + if (member is EventProperty) + return MemberKind.Event; + if (member is Delegate) + return MemberKind.Delegate; + if (member is Enum) + return MemberKind.Enum; + + throw new NotImplementedException (member.GetType ().ToString ()); + } + + public static List GetAllFieldsForDefiniteAssignment (TypeSpec container) + { + List fields = null; + foreach (var entry in container.MemberCache.member_hash) { + foreach (var name_entry in entry.Value) { + if (name_entry.Kind != MemberKind.Field) + continue; + + if ((name_entry.Modifiers & Modifiers.STATIC) != 0) + continue; + + // + // Fixed size buffers are not subject to definite assignment checking + // + if (name_entry is FixedFieldSpec || name_entry is ConstSpec) + continue; + + var fs = (FieldSpec) name_entry; + + // + // LAMESPEC: Very bizzare hack, definitive assignment is not done + // for imported non-public reference fields except array. No idea what the + // actual csc rule is + // + if (!fs.IsPublic && container.MemberDefinition.IsImported && (!fs.MemberType.IsArray && TypeSpec.IsReferenceType (fs.MemberType))) + continue; + + //if ((fs.Modifiers & (Modifiers.BACKING_FIELD) != 0) + // continue; + + if (fields == null) + fields = new List (); + + fields.Add (fs); + break; + } + } + + return fields ?? new List (0); + } + + public static IList GetCompletitionMembers (IMemberContext ctx, TypeSpec container, string name) + { + var matches = new List (); + foreach (var entry in container.MemberCache.member_hash) { + foreach (var name_entry in entry.Value) { + if (name_entry.IsAccessor) + continue; + + if ((name_entry.Kind & (MemberKind.Constructor | MemberKind.Destructor | MemberKind.Operator)) != 0) + continue; + + if (!name_entry.IsAccessible (ctx)) + continue; + + if (name == null || name_entry.Name.StartsWith (name)) { + matches.Add (name_entry); + } + } + } + + return matches; + } + + // + // Returns members of @iface only, base members are ignored + // + public static List GetInterfaceMethods (TypeSpec iface) + { + // + // MemberCache flatten interfaces, therefore in cases like this one + // + // interface IA : IB {} + // interface IB { void Foo () } + // + // we would return Foo inside IA which is not expected in this case + // + var methods = new List (); + foreach (var entry in iface.MemberCache.member_hash.Values) { + foreach (var name_entry in entry) { + if (iface == name_entry.DeclaringType) { + if (name_entry.Kind == MemberKind.Method) { + methods.Add ((MethodSpec) name_entry); + } + } + } + } + + return methods; + } + + // + // Returns all not implememted abstract members inside abstract type + // NOTE: Returned list is shared and must not be modified + // + public static IList GetNotImplementedAbstractMethods (TypeSpec type) + { + if (type.MemberCache.missing_abstract != null) + return type.MemberCache.missing_abstract; + + var abstract_methods = new List (); + List hierarchy = null; + + // + // Stage 1: top-to-bottom scan for abstract members + // + var abstract_type = type; + while (true) { + foreach (var entry in abstract_type.MemberCache.member_hash) { + foreach (var name_entry in entry.Value) { + if ((name_entry.Modifiers & Modifiers.ABSTRACT) == 0) + continue; + + var ms = name_entry as MethodSpec; + if (ms == null) + continue; + + abstract_methods.Add (ms); + } + } + + var base_type = abstract_type.BaseType; + if (!base_type.IsAbstract) + break; + + if (hierarchy == null) + hierarchy = new List (); + + hierarchy.Add (abstract_type); + abstract_type = base_type; + } + + int not_implemented_count = abstract_methods.Count; + if (not_implemented_count == 0 || hierarchy == null) { + type.MemberCache.missing_abstract = abstract_methods; + return type.MemberCache.missing_abstract; + } + + // + // Stage 2: Remove already implemented methods + // + foreach (var type_up in hierarchy) { + var members = type_up.MemberCache.member_hash; + if (members.Count == 0) + continue; + + for (int i = 0; i < abstract_methods.Count; ++i) { + var candidate = abstract_methods [i]; + if (candidate == null) + continue; + + IList applicable; + if (!members.TryGetValue (candidate.Name, out applicable)) + continue; + + var filter = new MemberFilter (candidate); + foreach (var item in applicable) { + if ((item.Modifiers & (Modifiers.OVERRIDE | Modifiers.VIRTUAL)) == 0) + continue; + + // + // Abstract override does not override anything + // + if ((item.Modifiers & Modifiers.ABSTRACT) != 0) + continue; + + if (filter.Equals (item)) { + --not_implemented_count; + abstract_methods [i] = null; + break; + } + } + } + } + + if (not_implemented_count == abstract_methods.Count) { + type.MemberCache.missing_abstract = abstract_methods; + return type.MemberCache.missing_abstract; + } + + var not_implemented = new MethodSpec[not_implemented_count]; + int counter = 0; + foreach (var m in abstract_methods) { + if (m == null) + continue; + + not_implemented[counter++] = m; + } + + type.MemberCache.missing_abstract = not_implemented; + return type.MemberCache.missing_abstract; + } + + static string GetLookupName (MemberSpec ms) + { + if (ms.Kind == MemberKind.Indexer) + return IndexerNameAlias; + + if (ms.Kind == MemberKind.Constructor) { + if (ms.IsStatic) + return Constructor.TypeConstructorName; + + return Constructor.ConstructorName; + } + + return ms.Name; + } + + static string GetLookupName (MemberCore mc) + { + if (mc is Indexer) + return IndexerNameAlias; + + if (mc is Constructor) + return mc.IsStatic ? Constructor.TypeConstructorName : Constructor.ConstructorName; + + return mc.MemberName.Name; + } + + // + // Returns all operators declared on container and its base types (until declaredOnly is used) + // + public static IList GetUserOperator (TypeSpec container, Operator.OpType op, bool declaredOnly) + { + IList found = null; + bool shared_list = true; + IList applicable; + do { + var mc = container.MemberCache; + + if (((op == Operator.OpType.Implicit || op == Operator.OpType.Explicit) && (mc.state & StateFlags.HasConversionOperator) != 0) || + (mc.state & StateFlags.HasUserOperator) != 0) { + + if (mc.member_hash.TryGetValue (Operator.GetMetadataName (op), out applicable)) { + int i; + for (i = 0; i < applicable.Count; ++i) { + if (applicable[i].Kind != MemberKind.Operator) { + break; + } + } + + // + // Handles very rare case where a method with same name as operator (op_xxxx) exists + // and we have to resize the applicable list + // + if (i != applicable.Count) { + for (i = 0; i < applicable.Count; ++i) { + if (applicable[i].Kind != MemberKind.Operator) { + continue; + } + + if (found == null) { + found = new List (); + found.Add (applicable[i]); + } else { + List prev; + if (shared_list) { + shared_list = false; + prev = new List (found.Count + 1); + prev.AddRange (found); + } else { + prev = (List) found; + } + + prev.Add (applicable[i]); + } + } + } else { + if (found == null) { + found = applicable; + shared_list = true; + } else { + List merged; + if (shared_list) { + shared_list = false; + merged = new List (found.Count + applicable.Count); + merged.AddRange (found); + found = merged; + } else { + merged = (List) found; + } + + merged.AddRange (applicable); + } + } + } + } + + // BaseType call can be expensive + if (declaredOnly) + break; + + container = container.BaseType; + } while (container != null); + + return found; + } + + // + // Inflates all member cache nested types + // + public void InflateTypes (MemberCache inflated_cache, TypeParameterInflator inflator) + { + foreach (var item in member_hash) { + IList inflated_members = null; + for (int i = 0; i < item.Value.Count; ++i ) { + var member = item.Value[i]; + + // FIXME: When inflating members refering nested types before they are inflated + if (member == null) + continue; + + if ((member.Kind & MemberKind.NestedMask) != 0 && + (member.Modifiers & Modifiers.COMPILER_GENERATED) == 0) { + if (inflated_members == null) { + inflated_members = new MemberSpec[item.Value.Count]; + inflated_cache.member_hash.Add (item.Key, inflated_members); + } + + inflated_members [i] = member.InflateMember (inflator); + } + } + } + } + + // + // Inflates all open type members, requires InflateTypes to be called before + // + public void InflateMembers (MemberCache cacheToInflate, TypeSpec inflatedType, TypeParameterInflator inflator) + { + var inflated_member_hash = cacheToInflate.member_hash; + Dictionary accessor_relation = null; + List accessor_members = null; + + // Copy member specific flags when all members were added + cacheToInflate.state = state; + + foreach (var item in member_hash) { + var members = item.Value; + IList inflated_members = null; + for (int i = 0; i < members.Count; ++i ) { + var member = members[i]; + + // + // All nested types have been inflated earlier except for + // compiler types which are created later and could miss InflateTypes + // + if ((member.Kind & MemberKind.NestedMask) != 0 && + (member.Modifiers & Modifiers.COMPILER_GENERATED) == 0) { + if (inflated_members == null) + inflated_members = inflated_member_hash[item.Key]; + + continue; + } + + // + // Clone the container first + // + if (inflated_members == null) { + inflated_members = new MemberSpec [item.Value.Count]; + inflated_member_hash.Add (item.Key, inflated_members); + } + + var local_inflator = inflator; + + if (member.DeclaringType != inflatedType) { + // + // Don't inflate top-level non-generic interface members + // merged into generic interface + // + if (!member.DeclaringType.IsGeneric && !member.DeclaringType.IsNested) { + inflated_members [i] = member; + continue; + } + + // + // Needed when inflating flatten interfaces. It inflates + // container type only, type parameters are already done + // + // Handles cases like: + // + // interface I {} + // interface I : I {} + // + // class C: I {} + // + var inflated_parent = inflator.Inflate (member.DeclaringType); + if (inflated_parent != inflator.TypeInstance) + local_inflator = new TypeParameterInflator (inflator, inflated_parent); + } + + // + // Inflate every member, its parent is now different + // + var inflated = member.InflateMember (local_inflator); + inflated_members [i] = inflated; + + if (member is PropertySpec || member is EventSpec) { + if (accessor_members == null) + accessor_members = new List (); + + accessor_members.Add (inflated); + continue; + } + + if (member.IsAccessor) { + if (accessor_relation == null) + accessor_relation = new Dictionary (); + accessor_relation.Add (member, (MethodSpec) inflated); + } + } + } + + if (accessor_members != null) { + foreach (var member in accessor_members) { + var prop = member as PropertySpec; + if (prop != null) { + if (prop.Get != null) + prop.Get = accessor_relation[prop.Get]; + if (prop.Set != null) + prop.Set = accessor_relation[prop.Set]; + + continue; + } + + var ev = (EventSpec) member; + ev.AccessorAdd = accessor_relation[ev.AccessorAdd]; + ev.AccessorRemove = accessor_relation[ev.AccessorRemove]; + } + } + } + + // + // Removes hidden base members of an interface. For compiled interfaces we cannot + // do name filtering during Add (as we do for import) because we need all base + // names to be valid during type definition. + // Add replaces hidden base member with current one which means any name collision + // (CS0108) of non-first name would be unnoticed because the name was replaced + // with the one from compiled type + // + public void RemoveHiddenMembers (TypeSpec container) + { + foreach (var entry in member_hash) { + var values = entry.Value; + + int container_members_start_at = 0; + while (values[container_members_start_at].DeclaringType != container && ++container_members_start_at < entry.Value.Count); + + if (container_members_start_at == 0 || container_members_start_at == values.Count) + continue; + + for (int i = 0; i < container_members_start_at; ++i) { + var member = values[i]; + + if (!container.ImplementsInterface (member.DeclaringType, false)) + continue; + + var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : ParametersCompiled.EmptyReadOnlyParameters; + + for (int ii = container_members_start_at; ii < values.Count; ++ii) { + var container_entry = values[ii]; + + if (container_entry.Arity != member.Arity) + continue; + + if (container_entry is IParametersMember) { + if (!TypeSpecComparer.Override.IsEqual (((IParametersMember) container_entry).Parameters, member_param)) + continue; + } + + values.RemoveAt (i); + --container_members_start_at; + --ii; + --i; + } + } + } + } + + // + // Checks all appropriate container members for CLS compliance + // + public void VerifyClsCompliance (TypeSpec container, Report report) + { + if (locase_members != null) + return; + + if (container.BaseType == null) { + locase_members = new Dictionary (member_hash.Count); // StringComparer.OrdinalIgnoreCase); + } else { + var btype = container.BaseType.GetDefinition (); + btype.MemberCache.VerifyClsCompliance (btype, report); + locase_members = new Dictionary (btype.MemberCache.locase_members); //, StringComparer.OrdinalIgnoreCase); + } + + var is_imported_type = container.MemberDefinition.IsImported; + foreach (var entry in container.MemberCache.member_hash) { + for (int i = 0; i < entry.Value.Count; ++i ) { + var name_entry = entry.Value[i]; + if ((name_entry.Modifiers & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0) + continue; + + if ((name_entry.Modifiers & (Modifiers.OVERRIDE | Modifiers.COMPILER_GENERATED)) != 0) + continue; + + if ((name_entry.Kind & MemberKind.MaskType) == 0) + continue; + + if (name_entry.MemberDefinition.CLSAttributeValue == false) + continue; + + IParametersMember p_a = null; + if (!is_imported_type) { + p_a = name_entry as IParametersMember; + if (p_a != null && !name_entry.IsAccessor) { + var p_a_pd = p_a.Parameters; + // + // Check differing overloads in @container + // + for (int ii = i + 1; ii < entry.Value.Count; ++ii) { + var checked_entry = entry.Value[ii]; + IParametersMember p_b = checked_entry as IParametersMember; + if (p_b == null) + continue; + + if (p_a_pd.Count != p_b.Parameters.Count) + continue; + + if (checked_entry.IsAccessor) + continue; + + var res = ParametersCompiled.IsSameClsSignature (p_a.Parameters, p_b.Parameters); + if (res != 0) { + ReportOverloadedMethodClsDifference (name_entry, checked_entry, res, report); + } + } + } + } + + if (i > 0 || name_entry.Kind == MemberKind.Constructor || name_entry.Kind == MemberKind.Indexer) + continue; + + var name_entry_locase = name_entry.Name.ToLowerInvariant (); + + MemberSpec[] found; + if (!locase_members.TryGetValue (name_entry_locase, out found)) { + found = new MemberSpec[] { name_entry }; + locase_members.Add (name_entry_locase, found); + } else { + bool same_names_only = true; + foreach (var f in found) { + if (f.Name == name_entry.Name) { + if (p_a != null) { + IParametersMember p_b = f as IParametersMember; + if (p_b == null) + continue; + + if (p_a.Parameters.Count != p_b.Parameters.Count) + continue; + + if (f.IsAccessor) + continue; + + var res = ParametersCompiled.IsSameClsSignature (p_a.Parameters, p_b.Parameters); + if (res != 0) { + ReportOverloadedMethodClsDifference (f, name_entry, res, report); + } + } + + continue; + } + + same_names_only = false; + if (!is_imported_type) { + var last = GetLaterDefinedMember (f, name_entry); + if (last == f.MemberDefinition) { + report.SymbolRelatedToPreviousError (name_entry); + } else { + report.SymbolRelatedToPreviousError (f); + } + + report.Warning (3005, 1, last.Location, + "Identifier `{0}' differing only in case is not CLS-compliant", last.GetSignatureForError ()); + } + } + + if (!same_names_only) { + Array.Resize (ref found, found.Length + 1); + found[found.Length - 1] = name_entry; + locase_members[name_entry_locase] = found; + } + } + } + } + } + + // + // Local report helper to issue correctly ordered members stored in hashtable + // + static MemberCore GetLaterDefinedMember (MemberSpec a, MemberSpec b) + { + var mc_a = a.MemberDefinition as MemberCore; + var mc_b = b.MemberDefinition as MemberCore; + if (mc_a == null) + return mc_b; + + if (mc_b == null) + return mc_a; + + if (a.DeclaringType.MemberDefinition != b.DeclaringType.MemberDefinition) + return mc_b; + + if (mc_a.Location.File != mc_a.Location.File) + return mc_b; + + return mc_b.Location.Row > mc_a.Location.Row ? mc_b : mc_a; + } + + static void ReportOverloadedMethodClsDifference (MemberSpec a, MemberSpec b, int res, Report report) + { + var last = GetLaterDefinedMember (a, b); + if (last == a.MemberDefinition) { + report.SymbolRelatedToPreviousError (b); + } else { + report.SymbolRelatedToPreviousError (a); + } + + if ((res & 1) != 0) { + report.Warning (3006, 1, last.Location, + "Overloaded method `{0}' differing only in ref or out, or in array rank, is not CLS-compliant", + last.GetSignatureForError ()); + } + + if ((res & 2) != 0) { + report.Warning (3007, 1, last.Location, + "Overloaded method `{0}' differing only by unnamed array types is not CLS-compliant", + last.GetSignatureForError ()); + } + } + + public bool CheckExistingMembersOverloads (MemberCore member, AParametersCollection parameters) + { + var name = GetLookupName (member); + var imb = member as InterfaceMemberBase; + if (imb != null && imb.IsExplicitImpl) { + name = imb.GetFullName (name); + } + + return CheckExistingMembersOverloads (member, name, parameters); + } + + public bool CheckExistingMembersOverloads (MemberCore member, string name, AParametersCollection parameters) + { + IList entries; + if (!member_hash.TryGetValue (name, out entries)) + return false; + + var Report = member.Compiler.Report; + + int method_param_count = parameters.Count; + for (int i = entries.Count - 1; i >= 0; --i) { + var ce = entries[i]; + var pm = ce as IParametersMember; + var pd = pm == null ? ParametersCompiled.EmptyReadOnlyParameters : pm.Parameters; + if (pd.Count != method_param_count) + continue; + + if (ce.Arity != member.MemberName.Arity) + continue; + + // Ignore merged interface members + if (member.Parent.PartialContainer != ce.DeclaringType.MemberDefinition) + continue; + + var p_types = pd.Types; + if (method_param_count > 0) { + int ii = method_param_count - 1; + TypeSpec type_a, type_b; + do { + type_a = parameters.Types [ii]; + type_b = p_types [ii]; + + var a_byref = (pd.FixedParameters[ii].ModFlags & Parameter.Modifier.RefOutMask) != 0; + var b_byref = (parameters.FixedParameters[ii].ModFlags & Parameter.Modifier.RefOutMask) != 0; + + if (a_byref != b_byref) + break; + + } while (TypeSpecComparer.Override.IsEqual (type_a, type_b) && ii-- != 0); + + if (ii >= 0) + continue; + + // + // Operators can differ in return type only + // + if (member is Operator && ce.Kind == MemberKind.Operator && ((MethodSpec) ce).ReturnType != ((Operator) member).ReturnType) + continue; + + // + // Report difference in parameter modifiers only + // + if (pd != null && member is MethodCore) { + ii = method_param_count; + while (ii-- != 0 && + (parameters.FixedParameters[ii].ModFlags & Parameter.Modifier.ModifierMask) == + (pd.FixedParameters[ii].ModFlags & Parameter.Modifier.ModifierMask) && + parameters.ExtensionMethodType == pd.ExtensionMethodType) ; + + if (ii >= 0) { + var mc = ce as MethodSpec; + member.Compiler.Report.SymbolRelatedToPreviousError (ce); + if ((member.ModFlags & Modifiers.PARTIAL) != 0 && (mc.Modifiers & Modifiers.PARTIAL) != 0) { + if (parameters.HasParams || pd.HasParams) { + Report.Error (758, member.Location, + "A partial method declaration and partial method implementation cannot differ on use of `params' modifier"); + } else { + Report.Error (755, member.Location, + "A partial method declaration and partial method implementation must be both an extension method or neither"); + } + } else if (member is Constructor) { + Report.Error (851, member.Location, + "Overloaded contructor `{0}' cannot differ on use of parameter modifiers only", + member.GetSignatureForError ()); + } else { + Report.Error (663, member.Location, + "Overloaded method `{0}' cannot differ on use of parameter modifiers only", + member.GetSignatureForError ()); + } + return false; + } + } + } + + if ((ce.Kind & MemberKind.Method) != 0) { + Method method_a = member as Method; + Method method_b = ce.MemberDefinition as Method; + if (method_a != null && method_b != null && (method_a.ModFlags & method_b.ModFlags & Modifiers.PARTIAL) != 0) { + const Modifiers partial_modifiers = Modifiers.STATIC | Modifiers.UNSAFE; + if (method_a.IsPartialDefinition == method_b.IsPartialImplementation) { + if ((method_a.ModFlags & partial_modifiers) == (method_b.ModFlags & partial_modifiers) || + method_a.Parent.IsUnsafe && method_b.Parent.IsUnsafe) { + if (method_a.IsPartialImplementation) { + method_a.SetPartialDefinition (method_b); + if (entries.Count == 1) + member_hash.Remove (name); + else + entries.RemoveAt (i); + } else { + method_b.SetPartialDefinition (method_a); + method_a.caching_flags |= MemberCore.Flags.PartialDefinitionExists; + } + continue; + } + + if (method_a.IsStatic != method_b.IsStatic) { + Report.SymbolRelatedToPreviousError (ce); + Report.Error (763, member.Location, + "A partial method declaration and partial method implementation must be both `static' or neither"); + } + + if ((method_a.ModFlags & Modifiers.UNSAFE) != (method_b.ModFlags & Modifiers.UNSAFE)) { + Report.SymbolRelatedToPreviousError (ce); + Report.Error (764, member.Location, + "A partial method declaration and partial method implementation must be both `unsafe' or neither"); + } + + return false; + } + + Report.SymbolRelatedToPreviousError (ce); + if (method_a.IsPartialDefinition) { + Report.Error (756, member.Location, "A partial method `{0}' declaration is already defined", + member.GetSignatureForError ()); + } + + Report.Error (757, member.Location, "A partial method `{0}' implementation is already defined", + member.GetSignatureForError ()); + return false; + } + + Report.SymbolRelatedToPreviousError (ce); + + bool is_reserved_a = member is AbstractPropertyEventMethod || member is Operator; + bool is_reserved_b = ((MethodSpec) ce).IsReservedMethod; + + if (is_reserved_a || is_reserved_b) { + Report.Error (82, member.Location, "A member `{0}' is already reserved", + is_reserved_a ? + ce.GetSignatureForError () : + member.GetSignatureForError ()); + return false; + } + } else { + Report.SymbolRelatedToPreviousError (ce); + } + + if (member is Operator && ce.Kind == MemberKind.Operator) { + Report.Error (557, member.Location, "Duplicate user-defined conversion in type `{0}'", + member.Parent.GetSignatureForError ()); + return false; + } + + Report.Error (111, member.Location, + "A member `{0}' is already defined. Rename this member or use different parameter types", + member.GetSignatureForError ()); + return false; + } + + return true; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/method.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/method.cs new file mode 100644 index 000000000..7c42dbc81 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/method.cs @@ -0,0 +1,2890 @@ +// +// method.cs: Method based declarations +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.Linq; +using Mono.CompilerServices.SymbolWriter; +using System.Runtime.CompilerServices; + +#if NET_2_1 +using XmlElement = System.Object; +#else +using System.Xml; +#endif + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using SecurityType = System.Collections.Generic.List; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using SecurityType = System.Collections.Generic.Dictionary; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + public abstract class MethodCore : InterfaceMemberBase, IParametersMember + { + protected ParametersCompiled parameters; + protected ToplevelBlock block; + protected MethodSpec spec; + + protected MethodCore (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, + MemberName name, Attributes attrs, ParametersCompiled parameters) + : base (parent, type, mod, allowed_mod, name, attrs) + { + this.parameters = parameters; + } + + public override Variance ExpectedMemberTypeVariance { + get { + return Variance.Covariant; + } + } + + // + // Returns the System.Type array for the parameters of this method + // + public TypeSpec [] ParameterTypes { + get { + return parameters.Types; + } + } + + public ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + + AParametersCollection IParametersMember.Parameters { + get { return parameters; } + } + + public ToplevelBlock Block { + get { + return block; + } + + set { + block = value; + } + } + + public CallingConventions CallingConventions { + get { + CallingConventions cc = parameters.CallingConvention; + if (!IsInterface) + if ((ModFlags & Modifiers.STATIC) == 0) + cc |= CallingConventions.HasThis; + + // FIXME: How is `ExplicitThis' used in C#? + + return cc; + } + } + + protected override bool CheckOverrideAgainstBase (MemberSpec base_member) + { + bool res = base.CheckOverrideAgainstBase (base_member); + + // + // Check that the permissions are not being changed + // + if (!CheckAccessModifiers (this, base_member)) { + Error_CannotChangeAccessModifiers (this, base_member); + res = false; + } + + return res; + } + + protected override bool CheckBase () + { + // Check whether arguments were correct. + if (!DefineParameters (parameters)) + return false; + + return base.CheckBase (); + } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader + { + get { return "M:"; } + } + + public override void Emit () + { + if ((ModFlags & Modifiers.COMPILER_GENERATED) == 0) { + parameters.CheckConstraints (this); + } + + base.Emit (); + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + if (overload is MethodCore) { + caching_flags |= Flags.MethodOverloadsExist; + return true; + } + + if (overload is AbstractPropertyEventMethod) + return true; + + return base.EnableOverloadChecks (overload); + } + + public override string GetSignatureForDocumentation () + { + string s = base.GetSignatureForDocumentation (); + if (MemberName.Arity > 0) + s += "``" + MemberName.Arity.ToString (); + + return s + parameters.GetSignatureForDocumentation (); + } + + public MethodSpec Spec { + get { return spec; } + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if (parameters.HasArglist) { + Report.Warning (3000, 1, Location, "Methods with variable arguments are not CLS-compliant"); + } + + if (member_type != null && !member_type.IsCLSCompliant ()) { + Report.Warning (3002, 1, Location, "Return type of `{0}' is not CLS-compliant", + GetSignatureForError ()); + } + + parameters.VerifyClsCompliance (this); + return true; + } + } + + public interface IGenericMethodDefinition : IMethodDefinition + { + TypeParameterSpec[] TypeParameters { get; } + int TypeParametersCount { get; } + +// MethodInfo MakeGenericMethod (TypeSpec[] targs); + } + + public sealed class MethodSpec : MemberSpec, IParametersMember + { + MethodBase inflatedMetaInfo; + AParametersCollection parameters; + TypeSpec returnType; + + TypeSpec[] targs; + TypeParameterSpec[] constraints; + + public static readonly MethodSpec Excluded = new MethodSpec (MemberKind.Method, InternalType.FakeInternalType, null, null, ParametersCompiled.EmptyReadOnlyParameters, 0); + + public MethodSpec (MemberKind kind, TypeSpec declaringType, IMethodDefinition details, TypeSpec returnType, + AParametersCollection parameters, Modifiers modifiers) + : base (kind, declaringType, details, modifiers) + { + this.parameters = parameters; + this.returnType = returnType; + } + + #region Properties + + public override int Arity { + get { + return IsGeneric ? GenericDefinition.TypeParametersCount : 0; + } + } + + public TypeParameterSpec[] Constraints { + get { + if (constraints == null && IsGeneric) + constraints = GenericDefinition.TypeParameters; + + return constraints; + } + } + + public bool IsConstructor { + get { + return Kind == MemberKind.Constructor; + } + } + + public new IMethodDefinition MemberDefinition { + get { + return (IMethodDefinition) definition; + } + } + + public IGenericMethodDefinition GenericDefinition { + get { + return (IGenericMethodDefinition) definition; + } + } + + public bool IsAsync { + get { + return (Modifiers & Modifiers.ASYNC) != 0; + } + } + + public bool IsExtensionMethod { + get { + return IsStatic && parameters.HasExtensionMethodType; + } + } + + public bool IsSealed { + get { + return (Modifiers & Modifiers.SEALED) != 0; + } + } + + // When is virtual or abstract + public bool IsVirtual { + get { + return (Modifiers & (Modifiers.VIRTUAL | Modifiers.ABSTRACT | Modifiers.OVERRIDE)) != 0; + } + } + + public bool IsReservedMethod { + get { + return Kind == MemberKind.Operator || IsAccessor; + } + } + + TypeSpec IInterfaceMemberSpec.MemberType { + get { + return returnType; + } + } + + public AParametersCollection Parameters { + get { + return parameters; + } + } + + public TypeSpec ReturnType { + get { + return returnType; + } + } + + public TypeSpec[] TypeArguments { + get { + return targs; + } + } + + #endregion + + public MethodSpec GetGenericMethodDefinition () + { + if (!IsGeneric && !DeclaringType.IsGeneric) + return this; + + return MemberCache.GetMember (declaringType, this); + } + + public MethodBase GetMetaInfo () + { + // + // inflatedMetaInfo is extra field needed for cases where we + // inflate method but another nested type can later inflate + // again (the cache would be build with inflated metaInfo) and + // TypeBuilder can work with method definitions only + // + if (inflatedMetaInfo == null) { + if ((state & StateFlags.PendingMetaInflate) != 0) { + var dt_meta = DeclaringType.GetMetaInfo (); + + if (DeclaringType.IsTypeBuilder) { + if (IsConstructor) + inflatedMetaInfo = TypeBuilder.GetConstructor (dt_meta, (ConstructorInfo) MemberDefinition.Metadata); + else + inflatedMetaInfo = TypeBuilder.GetMethod (dt_meta, (MethodInfo) MemberDefinition.Metadata); + } else { +#if STATIC + // it should not be reached + throw new NotImplementedException (); +#else + inflatedMetaInfo = MethodInfo.GetMethodFromHandle (MemberDefinition.Metadata.MethodHandle, dt_meta.TypeHandle); +#endif + } + + state &= ~StateFlags.PendingMetaInflate; + } else { + inflatedMetaInfo = MemberDefinition.Metadata; + } + } + + if ((state & StateFlags.PendingMakeMethod) != 0) { + var sre_targs = new MetaType[targs.Length]; + for (int i = 0; i < sre_targs.Length; ++i) + sre_targs[i] = targs[i].GetMetaInfo (); + + inflatedMetaInfo = ((MethodInfo) inflatedMetaInfo).MakeGenericMethod (sre_targs); + state &= ~StateFlags.PendingMakeMethod; + } + + return inflatedMetaInfo; + } + + public override string GetSignatureForDocumentation () + { + string name; + switch (Kind) { + case MemberKind.Constructor: + name = "#ctor"; + break; + case MemberKind.Method: + if (Arity > 0) + name = Name + "``" + Arity.ToString (); + else + name = Name; + + break; + default: + name = Name; + break; + } + + name = DeclaringType.GetSignatureForDocumentation () + "." + name + parameters.GetSignatureForDocumentation (); + if (Kind == MemberKind.Operator) { + var op = Operator.GetType (Name).Value; + if (op == Operator.OpType.Explicit || op == Operator.OpType.Implicit) { + name += "~" + ReturnType.GetSignatureForDocumentation (); + } + } + + return name; + } + + public override string GetSignatureForError () + { + string name; + if (IsConstructor) { + name = DeclaringType.GetSignatureForError () + "." + DeclaringType.Name; + } else if (Kind == MemberKind.Operator) { + var op = Operator.GetType (Name).Value; + if (op == Operator.OpType.Implicit || op == Operator.OpType.Explicit) { + name = DeclaringType.GetSignatureForError () + "." + Operator.GetName (op) + " operator " + returnType.GetSignatureForError (); + } else { + name = DeclaringType.GetSignatureForError () + ".operator " + Operator.GetName (op); + } + } else if (IsAccessor) { + int split = Name.IndexOf ('_'); + name = Name.Substring (split + 1); + var postfix = Name.Substring (0, split); + if (split == 3) { + var pc = parameters.Count; + if (pc > 0 && postfix == "get") { + name = "this" + parameters.GetSignatureForError ("[", "]", pc); + } else if (pc > 1 && postfix == "set") { + name = "this" + parameters.GetSignatureForError ("[", "]", pc - 1); + } + } + + return DeclaringType.GetSignatureForError () + "." + name + "." + postfix; + } else { + name = base.GetSignatureForError (); + if (targs != null) + name += "<" + TypeManager.CSharpName (targs) + ">"; + else if (IsGeneric) + name += "<" + TypeManager.CSharpName (GenericDefinition.TypeParameters) + ">"; + } + + return name + parameters.GetSignatureForError (); + } + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var ms = (MethodSpec) base.InflateMember (inflator); + ms.inflatedMetaInfo = null; + ms.returnType = inflator.Inflate (returnType); + ms.parameters = parameters.Inflate (inflator); + if (IsGeneric) + ms.constraints = TypeParameterSpec.InflateConstraints (inflator, Constraints); + + return ms; + } + + public MethodSpec MakeGenericMethod (IMemberContext context, params TypeSpec[] targs) + { + if (targs == null) + throw new ArgumentNullException (); +// TODO MemberCache +// if (generic_intances != null && generic_intances.TryGetValue (targs, out ginstance)) +// return ginstance; + + //if (generic_intances == null) + // generic_intances = new Dictionary (TypeSpecArrayComparer.Default); + + var inflator = new TypeParameterInflator (context, DeclaringType, GenericDefinition.TypeParameters, targs); + + var inflated = (MethodSpec) MemberwiseClone (); + inflated.declaringType = inflator.TypeInstance; + inflated.returnType = inflator.Inflate (returnType); + inflated.parameters = parameters.Inflate (inflator); + inflated.targs = targs; + inflated.constraints = TypeParameterSpec.InflateConstraints (inflator, constraints ?? GenericDefinition.TypeParameters); + inflated.state |= StateFlags.PendingMakeMethod; + + // if (inflated.parent == null) + // inflated.parent = parent; + + //generic_intances.Add (targs, inflated); + return inflated; + } + + public MethodSpec Mutate (TypeParameterMutator mutator) + { + var targs = TypeArguments; + if (targs != null) + targs = mutator.Mutate (targs); + + var decl = DeclaringType; + if (DeclaringType.IsGenericOrParentIsGeneric) { + decl = mutator.Mutate (decl); + } + + if (targs == TypeArguments && decl == DeclaringType) + return this; + + var ms = (MethodSpec) MemberwiseClone (); + if (decl != DeclaringType) { + ms.inflatedMetaInfo = null; + ms.declaringType = decl; + ms.state |= StateFlags.PendingMetaInflate; + } + + if (targs != null) { + ms.targs = targs; + ms.state |= StateFlags.PendingMakeMethod; + } + + return ms; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + var missing = returnType.ResolveMissingDependencies (this); + foreach (var pt in parameters.Types) { + var m = pt.GetMissingDependencies (this); + if (m == null) + continue; + + if (missing == null) + missing = new List (); + + missing.AddRange (m); + } + + if (Arity > 0) { + foreach (var tp in GenericDefinition.TypeParameters) { + var m = tp.GetMissingDependencies (this); + + if (m == null) + continue; + + if (missing == null) + missing = new List (); + + missing.AddRange (m); + } + } + + return missing; + } + } + + public abstract class MethodOrOperator : MethodCore, IMethodData, IMethodDefinition + { + ReturnParameter return_attributes; + SecurityType declarative_security; + protected MethodData MethodData; + + static readonly string[] attribute_targets = new string [] { "method", "return" }; + + protected MethodOrOperator (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, + Attributes attrs, ParametersCompiled parameters) + : base (parent, type, mod, allowed_mod, name, attrs, parameters) + { + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.ReturnValue) { + if (return_attributes == null) + return_attributes = new ReturnParameter (this, MethodBuilder, Location); + + return_attributes.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + if (a.Type == pa.MethodImpl) { + if ((ModFlags & Modifiers.ASYNC) != 0 && (a.GetMethodImplOptions () & MethodImplOptions.Synchronized) != 0) { + Report.Error (4015, a.Location, "`{0}': Async methods cannot use `MethodImplOptions.Synchronized'", + GetSignatureForError ()); + } + + is_external_implementation = a.IsInternalCall (); + } else if (a.Type == pa.DllImport) { + const Modifiers extern_static = Modifiers.EXTERN | Modifiers.STATIC; + if ((ModFlags & extern_static) != extern_static) { + Report.Error (601, a.Location, "The DllImport attribute must be specified on a method marked `static' and `extern'"); + } + + if (MemberName.IsGeneric || Parent.IsGenericOrParentIsGeneric) { + Report.Error (7042, a.Location, + "The DllImport attribute cannot be applied to a method that is generic or contained in a generic type"); + } + + is_external_implementation = true; + } + + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (MethodBuilder != null) + MethodBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Method; + } + } + + MethodBase IMethodDefinition.Metadata { + get { + return MethodData.MethodBuilder; + } + } + + // TODO: Remove and use MethodData abstraction + public MethodBuilder MethodBuilder { + get { + return MethodData.MethodBuilder; + } + } + + protected override bool CheckForDuplications () + { + return Parent.MemberCache.CheckExistingMembersOverloads (this, parameters); + } + + public virtual EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + return new EmitContext (this, ig, MemberType, sourceMethod); + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!CheckBase ()) + return false; + + MemberKind kind; + if (this is Operator) + kind = MemberKind.Operator; + else if (this is Destructor) + kind = MemberKind.Destructor; + else + kind = MemberKind.Method; + + string explicit_name; + + if (IsPartialDefinition) { + caching_flags &= ~Flags.Excluded_Undetected; + caching_flags |= Flags.Excluded; + + // Add to member cache only when a partial method implementation has not been found yet + if ((caching_flags & Flags.PartialDefinitionExists) != 0) + return true; + + if (IsExplicitImpl) + return true; + + explicit_name = null; + } else { + MethodData = new MethodData (this, ModFlags, flags, this, base_method); + + if (!MethodData.Define (Parent.PartialContainer, GetFullName (MemberName))) + return false; + + explicit_name = MethodData.MetadataName; + } + + spec = new MethodSpec (kind, Parent.Definition, this, ReturnType, parameters, ModFlags); + if (MemberName.Arity > 0) + spec.IsGeneric = true; + + Parent.MemberCache.AddMember (this, explicit_name, spec); + + return true; + } + + protected override void DoMemberTypeIndependentChecks () + { + base.DoMemberTypeIndependentChecks (); + + CheckAbstractAndExtern (block != null); + + if ((ModFlags & Modifiers.PARTIAL) != 0) { + for (int i = 0; i < parameters.Count; ++i) { + IParameterData p = parameters.FixedParameters [i]; + if ((p.ModFlags & Parameter.Modifier.OUT) != 0) { + Report.Error (752, Location, "`{0}': A partial method parameters cannot use `out' modifier", + GetSignatureForError ()); + } + + if (p.HasDefaultValue && IsPartialImplementation) + ((Parameter) p).Warning_UselessOptionalParameter (Report); + } + } + } + + protected override void DoMemberTypeDependentChecks () + { + base.DoMemberTypeDependentChecks (); + + if (MemberType.IsStatic) { + Error_StaticReturnType (); + } + } + + public override void Emit () + { + if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated) + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (MethodBuilder); + if ((ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0) + Module.PredefinedAttributes.DebuggerHidden.EmitAttribute (MethodBuilder); + if ((ModFlags & Modifiers.DEBUGGER_STEP_THROUGH) != 0) + Module.PredefinedAttributes.DebuggerStepThrough.EmitAttribute (MethodBuilder); + + if (ReturnType.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + return_attributes = new ReturnParameter (this, MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder); + } else if (ReturnType.HasDynamicElement) { + return_attributes = new ReturnParameter (this, MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder, ReturnType, Location); + } + + if (OptAttributes != null) + OptAttributes.Emit (); + + if (declarative_security != null) { + foreach (var de in declarative_security) { +#if STATIC + MethodBuilder.__AddDeclarativeSecurity (de); +#else + MethodBuilder.AddDeclarativeSecurity (de.Key, de.Value); +#endif + } + } + + if (type_expr != null) + ConstraintChecker.Check (this, member_type, type_expr.Location); + + base.Emit (); + + if (MethodData != null) + MethodData.Emit (Parent); + + if (block != null && block.StateMachine is AsyncTaskStorey) { + var psm = Module.PredefinedAttributes.AsyncStateMachine; + psm.EmitAttribute (MethodBuilder, block.StateMachine); + } + + if ((ModFlags & Modifiers.PARTIAL) == 0) + Block = null; + } + + protected void Error_ConditionalAttributeIsNotValid () + { + Report.Error (577, Location, + "Conditional not valid on `{0}' because it is a constructor, destructor, operator or explicit interface implementation", + GetSignatureForError ()); + } + + public bool IsPartialDefinition { + get { + return (ModFlags & Modifiers.PARTIAL) != 0 && Block == null; + } + } + + public bool IsPartialImplementation { + get { + return (ModFlags & Modifiers.PARTIAL) != 0 && Block != null; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + #region IMethodData Members + + bool IMethodData.IsAccessor { + get { + return false; + } + } + + public TypeSpec ReturnType { + get { + return MemberType; + } + } + + public MemberName MethodName { + get { + return MemberName; + } + } + + /// + /// Returns true if method has conditional attribute and the conditions is not defined (method is excluded). + /// + public override string[] ConditionalConditions () + { + if ((caching_flags & (Flags.Excluded_Undetected | Flags.Excluded)) == 0) + return null; + + if ((ModFlags & Modifiers.PARTIAL) != 0 && (caching_flags & Flags.Excluded) != 0) + return new string [0]; + + caching_flags &= ~Flags.Excluded_Undetected; + string[] conditions; + + if (base_method == null) { + if (OptAttributes == null) + return null; + + Attribute[] attrs = OptAttributes.SearchMulti (Module.PredefinedAttributes.Conditional); + if (attrs == null) + return null; + + conditions = new string[attrs.Length]; + for (int i = 0; i < conditions.Length; ++i) + conditions[i] = attrs[i].GetConditionalAttributeValue (); + } else { + conditions = base_method.MemberDefinition.ConditionalConditions(); + } + + if (conditions != null) + caching_flags |= Flags.Excluded; + + return conditions; + } + + #endregion + + public virtual void PrepareEmit () + { + var mb = MethodData.DefineMethodBuilder (Parent); + + if (CurrentTypeParameters != null) { + string[] gnames = new string[CurrentTypeParameters.Count]; + for (int i = 0; i < gnames.Length; ++i) { + gnames[i] = CurrentTypeParameters[i].Name; + } + + var gen_params = MethodBuilder.DefineGenericParameters (gnames); + + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp = CurrentTypeParameters[i]; + + tp.Define (gen_params[i]); + } + } + + // + // Generic method has been already defined to resolve method parameters + // correctly when they use type parameters + // + mb.SetParameters (parameters.GetMetaInfo ()); + mb.SetReturnType (ReturnType.GetMetaInfo ()); + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (MethodData != null && !IsPartialDefinition) + MethodData.WriteDebugSymbol (file); + } + } + + public class Method : MethodOrOperator, IGenericMethodDefinition + { + Method partialMethodImplementation; + + public Method (TypeDefinition parent, FullNamedExpression return_type, Modifiers mod, MemberName name, ParametersCompiled parameters, Attributes attrs) + : base (parent, return_type, mod, + parent.PartialContainer.Kind == MemberKind.Interface ? AllowedModifiersInterface : + parent.PartialContainer.Kind == MemberKind.Struct ? AllowedModifiersStruct | Modifiers.ASYNC : + AllowedModifiersClass | Modifiers.ASYNC, + name, attrs, parameters) + { + } + + protected Method (TypeDefinition parent, FullNamedExpression return_type, Modifiers mod, Modifiers amod, + MemberName name, ParametersCompiled parameters, Attributes attrs) + : base (parent, return_type, mod, amod, name, attrs, parameters) + { + } + + #region Properties + + public override TypeParameters CurrentTypeParameters { + get { + return MemberName.TypeParameters; + } + } + + public TypeParameterSpec[] TypeParameters { + get { + return CurrentTypeParameters.Types; + } + } + + public int TypeParametersCount { + get { + return CurrentTypeParameters == null ? 0 : CurrentTypeParameters.Count; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public static Method Create (TypeDefinition parent, FullNamedExpression returnType, Modifiers mod, + MemberName name, ParametersCompiled parameters, Attributes attrs) + { + var m = new Method (parent, returnType, mod, name, parameters, attrs); + + if ((mod & Modifiers.PARTIAL) != 0) { + const Modifiers invalid_partial_mod = Modifiers.AccessibilityMask | Modifiers.ABSTRACT | Modifiers.EXTERN | + Modifiers.NEW | Modifiers.OVERRIDE | Modifiers.SEALED | Modifiers.VIRTUAL; + + if ((mod & invalid_partial_mod) != 0) { + m.Report.Error (750, m.Location, + "A partial method cannot define access modifier or any of abstract, extern, new, override, sealed, or virtual modifiers"); + mod &= ~invalid_partial_mod; + } + + if ((parent.ModFlags & Modifiers.PARTIAL) == 0) { + m.Report.Error (751, m.Location, + "A partial method must be declared within a partial class or partial struct"); + } + } + + if ((mod & Modifiers.STATIC) == 0 && parameters.HasExtensionMethodType) { + m.Report.Error (1105, m.Location, "`{0}': Extension methods must be declared static", + m.GetSignatureForError ()); + } + + + return m; + } + + public override string GetSignatureForError() + { + return base.GetSignatureForError () + parameters.GetSignatureForError (); + } + + void Error_DuplicateEntryPoint (Method b) + { + Report.Error (17, b.Location, + "Program `{0}' has more than one entry point defined: `{1}'", + b.Module.Builder.ScopeName, b.GetSignatureForError ()); + } + + bool IsEntryPoint () + { + if (ReturnType.Kind != MemberKind.Void && ReturnType.BuiltinType != BuiltinTypeSpec.Type.Int) + return false; + + if (parameters.IsEmpty) + return true; + + if (parameters.Count > 1) + return false; + + var ac = parameters.Types [0] as ArrayContainer; + return ac != null && ac.Rank == 1 && ac.Element.BuiltinType == BuiltinTypeSpec.Type.String && + (parameters[0].ModFlags & Parameter.Modifier.RefOutMask) == 0; + } + + public override FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + if (arity == 0) { + var tp = CurrentTypeParameters; + if (tp != null) { + TypeParameter t = tp.Find (name); + if (t != null) + return new TypeParameterExpr (t, loc); + } + } + + return base.LookupNamespaceOrType (name, arity, mode, loc); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.Conditional) { + if (IsExplicitImpl) { + Error_ConditionalAttributeIsNotValid (); + return; + } + + if ((ModFlags & Modifiers.OVERRIDE) != 0) { + Report.Error (243, Location, "Conditional not valid on `{0}' because it is an override method", GetSignatureForError ()); + return; + } + + if (ReturnType.Kind != MemberKind.Void) { + Report.Error (578, Location, "Conditional not valid on `{0}' because its return type is not void", GetSignatureForError ()); + return; + } + + if (IsInterface) { + Report.Error (582, Location, "Conditional not valid on interface members"); + return; + } + + if (MethodData.implementing != null) { + Report.SymbolRelatedToPreviousError (MethodData.implementing.DeclaringType); + Report.Error (629, Location, "Conditional member `{0}' cannot implement interface member `{1}'", + GetSignatureForError (), TypeManager.CSharpSignature (MethodData.implementing)); + return; + } + + for (int i = 0; i < parameters.Count; ++i) { + if ((parameters.FixedParameters [i].ModFlags & Parameter.Modifier.OUT) != 0) { + Report.Error (685, Location, "Conditional method `{0}' cannot have an out parameter", GetSignatureForError ()); + return; + } + } + } + + if (a.Type == pa.Extension) { + a.Error_MisusedExtensionAttribute (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + void CreateTypeParameters () + { + var tparams = MemberName.TypeParameters; + var parent_tparams = Parent.TypeParametersAll; + + for (int i = 0; i < MemberName.Arity; i++) { + string type_argument_name = tparams[i].MemberName.Name; + + if (block == null) { + int idx = parameters.GetParameterIndexByName (type_argument_name); + if (idx >= 0) { + var b = block; + if (b == null) + b = new ToplevelBlock (Compiler, Location); + + b.Error_AlreadyDeclaredTypeParameter (type_argument_name, parameters[i].Location); + } + } else { + INamedBlockVariable variable = null; + block.GetLocalName (type_argument_name, block, ref variable); + if (variable != null) + variable.Block.Error_AlreadyDeclaredTypeParameter (type_argument_name, variable.Location); + } + + if (parent_tparams != null) { + var tp = parent_tparams.Find (type_argument_name); + if (tp != null) { + tparams[i].WarningParentNameConflict (tp); + } + } + } + + tparams.Create (null, 0, Parent); + } + + protected virtual void DefineTypeParameters () + { + var tparams = CurrentTypeParameters; + + TypeParameterSpec[] base_tparams = null; + TypeParameterSpec[] base_decl_tparams = TypeParameterSpec.EmptyTypes; + TypeSpec[] base_targs = TypeSpec.EmptyTypes; + if (((ModFlags & Modifiers.OVERRIDE) != 0 || IsExplicitImpl)) { + MethodSpec base_override = base_method ?? MethodData.implementing; + + if (base_override != null) { + base_tparams = base_override.GenericDefinition.TypeParameters; + + if (base_override.DeclaringType.IsGeneric) { + base_decl_tparams = base_override.DeclaringType.MemberDefinition.TypeParameters; + + if (base_method != null) { + var base_type_parent = CurrentType; + while (base_type_parent.BaseType != base_override.DeclaringType) { + base_type_parent = base_type_parent.BaseType; + } + + base_targs = base_type_parent.BaseType.TypeArguments; + } else { + foreach (var iface in Parent.CurrentType.Interfaces) { + if (iface == base_override.DeclaringType) { + base_targs = iface.TypeArguments; + break; + } + } + } + } + + if (base_override.IsGeneric) { + ObsoleteAttribute oa; + foreach (var base_tp in base_tparams) { + oa = base_tp.BaseType.GetAttributeObsolete (); + if (oa != null) { + AttributeTester.Report_ObsoleteMessage (oa, base_tp.BaseType.GetSignatureForError (), Location, Report); + } + + if (base_tp.InterfacesDefined != null) { + foreach (var iface in base_tp.InterfacesDefined) { + oa = iface.GetAttributeObsolete (); + if (oa != null) { + AttributeTester.Report_ObsoleteMessage (oa, iface.GetSignatureForError (), Location, Report); + } + } + } + } + + if (base_decl_tparams.Length != 0) { + base_decl_tparams = base_decl_tparams.Concat (base_tparams).ToArray (); + base_targs = base_targs.Concat (tparams.Types).ToArray (); + } else { + base_decl_tparams = base_tparams; + base_targs = tparams.Types; + } + } + } + } + + for (int i = 0; i < tparams.Count; ++i) { + var tp = tparams [i]; + + if (base_tparams == null) { + tp.ResolveConstraints (this); + continue; + } + + // + // Copy base constraints for override/explicit methods + // + var base_tparam = base_tparams [i]; + var local_tparam = tp.Type; + local_tparam.SpecialConstraint = base_tparam.SpecialConstraint; + + var inflator = new TypeParameterInflator (this, CurrentType, base_decl_tparams, base_targs); + base_tparam.InflateConstraints (inflator, local_tparam); + + // + // Check all type argument constraints for possible collision or unification + // introduced by inflating inherited constraints in this context + // + // Conflict example: + // + // class A { virtual void Foo () where U : class, T {} } + // class B : A { override void Foo {} } + // + var local_tparam_targs = local_tparam.TypeArguments; + if (local_tparam_targs != null) { + for (int ii = 0; ii < local_tparam_targs.Length; ++ii) { + var ta = local_tparam_targs [ii]; + if (!ta.IsClass && !ta.IsStruct) + continue; + + TypeSpec[] unique_tparams = null; + for (int iii = ii + 1; iii < local_tparam_targs.Length; ++iii) { + // + // Remove any identical or unified constraint types + // + var tparam_checked = local_tparam_targs [iii]; + if (TypeSpecComparer.IsEqual (ta, tparam_checked) || TypeSpec.IsBaseClass (ta, tparam_checked, false)) { + unique_tparams = new TypeSpec[local_tparam_targs.Length - 1]; + Array.Copy (local_tparam_targs, 0, unique_tparams, 0, iii); + Array.Copy (local_tparam_targs, iii + 1, unique_tparams, iii, local_tparam_targs.Length - iii - 1); + } else if (!TypeSpec.IsBaseClass (tparam_checked, ta, false)) { + Constraints.Error_ConflictingConstraints (this, local_tparam, ta, tparam_checked, Location); + } + } + + if (unique_tparams != null) { + local_tparam_targs = unique_tparams; + local_tparam.TypeArguments = local_tparam_targs; + continue; + } + + Constraints.CheckConflictingInheritedConstraint (local_tparam, ta, this, Location); + } + } + } + + if (base_tparams == null && MethodData != null && MethodData.implementing != null) { + CheckImplementingMethodConstraints (Parent, spec, MethodData.implementing); + } + } + + public static bool CheckImplementingMethodConstraints (TypeContainer container, MethodSpec method, MethodSpec baseMethod) + { + var tparams = method.Constraints; + var base_tparams = baseMethod.Constraints; + for (int i = 0; i < tparams.Length; ++i) { + if (!tparams[i].HasSameConstraintsImplementation (base_tparams[i])) { + container.Compiler.Report.SymbolRelatedToPreviousError (method); + container.Compiler.Report.SymbolRelatedToPreviousError (baseMethod); + + // Using container location because the interface can be implemented + // by base class + var tp = (tparams [i].MemberDefinition as MemberCore) ?? container; + container.Compiler.Report.Error (425, tp.Location, + "The constraints for type parameter `{0}' of method `{1}' must match the constraints for type parameter `{2}' of interface method `{3}'. Consider using an explicit interface implementation instead", + tparams[i].GetSignatureForError (), method.GetSignatureForError (), + base_tparams[i].GetSignatureForError (), baseMethod.GetSignatureForError ()); + + return false; + } + } + + return true; + } + + // + // Creates the type + // + public override bool Define () + { + if (!base.Define ()) + return false; + + if (member_type.Kind == MemberKind.Void && parameters.IsEmpty && MemberName.Arity == 0 && MemberName.Name == Destructor.MetadataName) { + Report.Warning (465, 1, Location, + "Introducing `Finalize' method can interfere with destructor invocation. Did you intend to declare a destructor?"); + } + + if (Compiler.Settings.StdLib && ReturnType.IsSpecialRuntimeType) { + Error1599 (Location, ReturnType, Report); + return false; + } + + if (CurrentTypeParameters == null) { + if (base_method != null && !IsExplicitImpl) { + if (parameters.Count == 1 && ParameterTypes[0].BuiltinType == BuiltinTypeSpec.Type.Object && MemberName.Name == "Equals") + Parent.PartialContainer.Mark_HasEquals (); + else if (parameters.IsEmpty && MemberName.Name == "GetHashCode") + Parent.PartialContainer.Mark_HasGetHashCode (); + } + + } else { + DefineTypeParameters (); + } + + if (block != null) { + if (block.IsIterator) { + // + // Current method is turned into automatically generated + // wrapper which creates an instance of iterator + // + Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags); + ModFlags |= Modifiers.DEBUGGER_HIDDEN; + } + + if ((ModFlags & Modifiers.ASYNC) != 0) { + if (ReturnType.Kind != MemberKind.Void && + ReturnType != Module.PredefinedTypes.Task.TypeSpec && + !ReturnType.IsGenericTask) { + Report.Error (1983, Location, "The return type of an async method must be void, Task, or Task"); + } + + block = (ToplevelBlock) block.ConvertToAsyncTask (this, Parent.PartialContainer, parameters, ReturnType, null, Location); + ModFlags |= Modifiers.DEBUGGER_STEP_THROUGH; + } + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + } + + if ((ModFlags & Modifiers.STATIC) == 0) + return true; + + if (parameters.HasExtensionMethodType) { + if (Parent.PartialContainer.IsStatic && !Parent.IsGenericOrParentIsGeneric) { + if (!Parent.IsTopLevel) + Report.Error (1109, Location, "`{0}': Extension methods cannot be defined in a nested class", + GetSignatureForError ()); + + PredefinedAttribute pa = Module.PredefinedAttributes.Extension; + if (!pa.IsDefined) { + Report.Error (1110, Location, + "`{0}': Extension methods require `System.Runtime.CompilerServices.ExtensionAttribute' type to be available. Are you missing an assembly reference?", + GetSignatureForError ()); + } + + ModFlags |= Modifiers.METHOD_EXTENSION; + Parent.PartialContainer.ModFlags |= Modifiers.METHOD_EXTENSION; + Spec.DeclaringType.SetExtensionMethodContainer (); + Parent.Module.HasExtensionMethod = true; + } else { + Report.Error (1106, Location, "`{0}': Extension methods must be defined in a non-generic static class", + GetSignatureForError ()); + } + } + + // + // This is used to track the Entry Point, + // + var settings = Compiler.Settings; + if (settings.NeedsEntryPoint && MemberName.Name == "Main" && !IsPartialDefinition && (settings.MainClass == null || settings.MainClass == Parent.TypeBuilder.FullName)) { + if (IsEntryPoint ()) { + if (Parent.DeclaringAssembly.EntryPoint == null) { + if (Parent.IsGenericOrParentIsGeneric || MemberName.IsGeneric) { + Report.Warning (402, 4, Location, "`{0}': an entry point cannot be generic or in a generic type", + GetSignatureForError ()); + } else if ((ModFlags & Modifiers.ASYNC) != 0) { + Report.Error (4009, Location, "`{0}': an entry point cannot be async method", + GetSignatureForError ()); + } else { + SetIsUsed (); + Parent.DeclaringAssembly.EntryPoint = this; + } + } else { + Error_DuplicateEntryPoint (Parent.DeclaringAssembly.EntryPoint); + Error_DuplicateEntryPoint (this); + } + } else { + Report.Warning (28, 4, Location, "`{0}' has the wrong signature to be an entry point", + GetSignatureForError ()); + } + } + + return true; + } + + public override void PrepareEmit () + { + if (IsPartialDefinition) { + // + // Use partial method implementation builder for partial method declaration attributes + // + if (partialMethodImplementation != null) { + MethodData = partialMethodImplementation.MethodData; + } + + return; + } + + base.PrepareEmit (); + } + + // + // Emits the code + // + public override void Emit () + { + try { + if (IsPartialDefinition) { + if (partialMethodImplementation != null && CurrentTypeParameters != null) { + CurrentTypeParameters.CheckPartialConstraints (partialMethodImplementation); + + var otp = partialMethodImplementation.CurrentTypeParameters; + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp = CurrentTypeParameters [i]; + tp.Define (otp[i]); + } + } + + return; + } + + if ((ModFlags & Modifiers.PARTIAL) != 0 && (caching_flags & Flags.PartialDefinitionExists) == 0) { + Report.Error (759, Location, "A partial method `{0}' implementation is missing a partial method declaration", + GetSignatureForError ()); + } + + if (CurrentTypeParameters != null) { + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp = CurrentTypeParameters [i]; + + tp.CheckGenericConstraints (false); + tp.Emit (); + } + } + + if ((ModFlags & Modifiers.METHOD_EXTENSION) != 0) + Module.PredefinedAttributes.Extension.EmitAttribute (MethodBuilder); + + base.Emit (); + } catch (Exception e) { + throw new InternalErrorException (this, e); + } + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + if (overload is Indexer) + return false; + + return base.EnableOverloadChecks (overload); + } + + public static void Error1599 (Location loc, TypeSpec t, Report Report) + { + Report.Error (1599, loc, "Method or delegate cannot return type `{0}'", t.GetSignatureForError ()); + } + + protected override bool ResolveMemberType () + { + if (CurrentTypeParameters != null) { + CreateTypeParameters (); + } + + return base.ResolveMemberType (); + } + + public void SetPartialDefinition (Method methodDefinition) + { + caching_flags |= Flags.PartialDefinitionExists; + methodDefinition.partialMethodImplementation = this; + + // Ensure we are always using method declaration parameters + for (int i = 0; i < methodDefinition.parameters.Count; ++i ) { + var md_p = methodDefinition.parameters [i]; + var p = parameters [i]; + p.Name = md_p.Name; + p.DefaultValue = md_p.DefaultValue; + if (md_p.OptAttributes != null) { + if (p.OptAttributes == null) { + p.OptAttributes = md_p.OptAttributes; + } else { + p.OptAttributes.Attrs.AddRange (md_p.OptAttributes.Attrs); + } + } + } + + if (methodDefinition.attributes != null) { + if (attributes == null) { + attributes = methodDefinition.attributes; + } else { + attributes.Attrs.AddRange (methodDefinition.attributes.Attrs); + } + } + + if (CurrentTypeParameters != null) { + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp_other = methodDefinition.CurrentTypeParameters [i]; + if (tp_other.OptAttributes == null) + continue; + + var tp = CurrentTypeParameters [i]; + if (tp.OptAttributes == null) { + tp.OptAttributes = tp_other.OptAttributes; + } else { + tp.OptAttributes.Attrs.AddRange (tp.OptAttributes.Attrs); + } + } + } + } + } + + public abstract class ConstructorInitializer : ExpressionStatement + { + Arguments argument_list; + MethodSpec base_ctor; + + protected ConstructorInitializer (Arguments argument_list, Location loc) + { + this.argument_list = argument_list; + this.loc = loc; + } + + public Arguments Arguments { + get { + return argument_list; + } + } + + public override bool ContainsEmitWithAwait () + { + throw new NotSupportedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Value; + + // FIXME: Hack + var caller_builder = (Constructor) ec.MemberContext; + + // + // Spec mandates that constructor initializer will not have `this' access + // + using (ec.Set (ResolveContext.Options.BaseInitializer)) { + if (argument_list != null) { + bool dynamic; + argument_list.Resolve (ec, out dynamic); + + if (dynamic) { + ec.Report.Error (1975, loc, + "The constructor call cannot be dynamically dispatched within constructor initializer"); + + return null; + } + } + + type = ec.CurrentType; + if (this is ConstructorBaseInitializer) { + if (ec.CurrentType.BaseType == null) + return this; + + type = ec.CurrentType.BaseType; + if (ec.CurrentType.IsStruct) { + ec.Report.Error (522, loc, + "`{0}': Struct constructors cannot call base constructors", caller_builder.GetSignatureForError ()); + return this; + } + } else { + // + // It is legal to have "this" initializers that take no arguments + // in structs + // + // struct D { public D (int a) : this () {} + // + if (ec.CurrentType.IsStruct && argument_list == null) + return this; + } + + base_ctor = ConstructorLookup (ec, type, ref argument_list, loc); + } + + if (base_ctor != null && base_ctor.MemberDefinition == caller_builder.Spec.MemberDefinition) { + ec.Report.Error (516, loc, "Constructor `{0}' cannot call itself", + caller_builder.GetSignatureForError ()); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + // + // It can be null for struct initializers or System.Object + // + if (base_ctor == null) { + if (type == ec.BuiltinTypes.Object) + return; + + ec.Emit (OpCodes.Ldarg_0); + ec.Emit (OpCodes.Initobj, type); + return; + } + + var call = new CallEmitter (); + call.InstanceExpression = new CompilerGeneratedThis (type, loc); + call.EmitPredefined (ec, base_ctor, argument_list, false); + } + + public override void EmitStatement (EmitContext ec) + { + Emit (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (argument_list != null) + argument_list.FlowAnalysis (fc); + } + } + + public class ConstructorBaseInitializer : ConstructorInitializer { + public ConstructorBaseInitializer (Arguments argument_list, Location l) : + base (argument_list, l) + { + } + } + + class GeneratedBaseInitializer: ConstructorBaseInitializer { + public GeneratedBaseInitializer (Location loc, Arguments arguments) + : base (arguments, loc) + { + } + } + + public class ConstructorThisInitializer : ConstructorInitializer { + public ConstructorThisInitializer (Arguments argument_list, Location l) : + base (argument_list, l) + { + } + } + + public class Constructor : MethodCore, IMethodData, IMethodDefinition + { + public ConstructorBuilder ConstructorBuilder; + public ConstructorInitializer Initializer; + SecurityType declarative_security; + bool has_compliant_args; + SourceMethodBuilder debug_builder; + + // + // Modifiers allowed for a constructor. + // + public const Modifiers AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.STATIC | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.PRIVATE; + + static readonly string[] attribute_targets = new string [] { "method" }; + + public static readonly string ConstructorName = ".ctor"; + public static readonly string TypeConstructorName = ".cctor"; + + public Constructor (TypeDefinition parent, string name, Modifiers mod, Attributes attrs, ParametersCompiled args, Location loc) + : base (parent, null, mod, AllowedModifiers, new MemberName (name, loc), attrs, args) + { + } + + public bool HasCompliantArgs { + get { + return has_compliant_args; + } + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Constructor; + } + } + + bool IMethodData.IsAccessor { + get { + return false; + } + } + + public bool IsPrimaryConstructor { get; set; } + + MethodBase IMethodDefinition.Metadata { + get { + return ConstructorBuilder; + } + } + + // + // Returns true if this is a default constructor + // + public bool IsDefault () + { + if ((ModFlags & Modifiers.STATIC) != 0) + return parameters.IsEmpty; + + return parameters.IsEmpty && + (Initializer is ConstructorBaseInitializer) && + (Initializer.Arguments == null); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (a.Type == pa.MethodImpl) { + is_external_implementation = a.IsInternalCall (); + } + + ConstructorBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + protected override bool CheckBase () + { + if ((ModFlags & Modifiers.STATIC) != 0) { + if (!parameters.IsEmpty) { + Report.Error (132, Location, "`{0}': The static constructor must be parameterless", + GetSignatureForError ()); + return false; + } + + if ((caching_flags & Flags.MethodOverloadsExist) != 0) + Parent.MemberCache.CheckExistingMembersOverloads (this, parameters); + + // the rest can be ignored + return true; + } + + // Check whether arguments were correct. + if (!DefineParameters (parameters)) + return false; + + if ((caching_flags & Flags.MethodOverloadsExist) != 0) + Parent.MemberCache.CheckExistingMembersOverloads (this, parameters); + + if (Parent.PartialContainer.Kind == MemberKind.Struct && parameters.IsEmpty) { + Report.Error (568, Location, + "Structs cannot contain explicit parameterless constructors"); + return false; + } + + CheckProtectedModifier (); + + return true; + } + + // + // Creates the ConstructorBuilder + // + public override bool Define () + { + if (ConstructorBuilder != null) + return true; + + if (!CheckAbstractAndExtern (block != null)) + return false; + + // Check if arguments were correct. + if (!CheckBase ()) + return false; + + if (Parent.PrimaryConstructorParameters != null && !IsPrimaryConstructor && !IsStatic) { + if (Parent.Kind == MemberKind.Struct && Initializer is ConstructorThisInitializer && Initializer.Arguments == null) { + Report.Error (8043, Location, "`{0}': Structs with primary constructor cannot specify default constructor initializer", + GetSignatureForError ()); + } else if (Initializer == null || Initializer is ConstructorBaseInitializer) { + Report.Error (8037, Location, "`{0}': Instance constructor of type with primary constructor must specify `this' constructor initializer", + GetSignatureForError ()); + } + } + + var ca = ModifiersExtensions.MethodAttr (ModFlags) | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName; + + ConstructorBuilder = Parent.TypeBuilder.DefineConstructor ( + ca, CallingConventions, + parameters.GetMetaInfo ()); + + spec = new MethodSpec (MemberKind.Constructor, Parent.Definition, this, Compiler.BuiltinTypes.Void, parameters, ModFlags); + + Parent.MemberCache.AddMember (spec); + + if (block != null) { + // It's here only to report an error + if (block.IsIterator) { + member_type = Compiler.BuiltinTypes.Void; + Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags); + } + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + } + + return true; + } + + // + // Emits the code + // + public override void Emit () + { + if (Parent.PartialContainer.IsComImport) { + if (!IsDefault ()) { + Report.Error (669, Location, "`{0}': A class with the ComImport attribute cannot have a user-defined constructor", + Parent.GetSignatureForError ()); + } + + // Set as internal implementation and reset block data + // to ensure no IL is generated + ConstructorBuilder.SetImplementationFlags (MethodImplAttributes.InternalCall); + block = null; + } + + if ((ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0) + Module.PredefinedAttributes.DebuggerHidden.EmitAttribute (ConstructorBuilder); + + if (OptAttributes != null) + OptAttributes.Emit (); + + base.Emit (); + parameters.ApplyAttributes (this, ConstructorBuilder); + + + BlockContext bc = new BlockContext (this, block, Compiler.BuiltinTypes.Void); + bc.Set (ResolveContext.Options.ConstructorScope); + + if (block != null) { + if (!IsStatic && Initializer == null && Parent.PartialContainer.Kind == MemberKind.Struct) { + // + // If this is a non-static `struct' constructor and doesn't have any + // initializer, it must initialize all of the struct's fields. + // + block.AddThisVariable (bc); + } + + // + // If we use a "this (...)" constructor initializer, then + // do not emit field initializers, they are initialized in the other constructor + // + if (!(Initializer is ConstructorThisInitializer)) + Parent.PartialContainer.ResolveFieldInitializers (bc); + + if (!IsStatic) { + if (Initializer == null && Parent.PartialContainer.Kind == MemberKind.Class) { + Initializer = new GeneratedBaseInitializer (Location, null); + } + + if (Initializer != null) { + // + // mdb format does not support reqions. Try to workaround this by emitting the + // sequence point at initializer. Any breakpoint at constructor header should + // be adjusted to this sequence point as it's the next one which follows. + // + block.AddScopeStatement (new StatementExpression (Initializer)); + } + } + + if (block.Resolve (bc, this)) { + debug_builder = Parent.CreateMethodSymbolEntry (); + EmitContext ec = new EmitContext (this, ConstructorBuilder.GetILGenerator (), bc.ReturnType, debug_builder); + ec.With (EmitContext.Options.ConstructorScope, true); + + block.Emit (ec); + } + } + + if (declarative_security != null) { + foreach (var de in declarative_security) { +#if STATIC + ConstructorBuilder.__AddDeclarativeSecurity (de); +#else + ConstructorBuilder.AddDeclarativeSecurity (de.Key, de.Value); +#endif + } + } + + block = null; + } + + protected override MemberSpec FindBaseMember (out MemberSpec bestCandidate, ref bool overrides) + { + // Is never override + bestCandidate = null; + return null; + } + + public override string GetCallerMemberName () + { + return IsStatic ? TypeConstructorName : ConstructorName; + } + + public override string GetSignatureForDocumentation () + { + return Parent.GetSignatureForDocumentation () + ".#ctor" + parameters.GetSignatureForDocumentation (); + } + + public override string GetSignatureForError() + { + return base.GetSignatureForError () + parameters.GetSignatureForError (); + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance () || !IsExposedFromAssembly ()) { + return false; + } + + if (!parameters.IsEmpty && Parent.Definition.IsAttribute) { + foreach (TypeSpec param in parameters.Types) { + if (param.IsArray) { + return true; + } + } + } + + has_compliant_args = true; + return true; + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (debug_builder == null) + return; + + var token = ConstructorBuilder.GetToken (); + int t = token.Token; +#if STATIC + if (ModuleBuilder.IsPseudoToken (t)) + t = Module.Builder.ResolvePseudoToken (t); +#endif + + debug_builder.DefineMethod (file, t); + } + + #region IMethodData Members + + public MemberName MethodName { + get { + return MemberName; + } + } + + public TypeSpec ReturnType { + get { + return MemberType; + } + } + + EmitContext IMethodData.CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + throw new NotImplementedException (); + } + + #endregion + } + + /// + /// Interface for MethodData class. Holds links to parent members to avoid member duplication. + /// + public interface IMethodData : IMemberContext + { + CallingConventions CallingConventions { get; } + Location Location { get; } + MemberName MethodName { get; } + TypeSpec ReturnType { get; } + ParametersCompiled ParameterInfo { get; } + MethodSpec Spec { get; } + bool IsAccessor { get; } + + Attributes OptAttributes { get; } + ToplevelBlock Block { get; set; } + + EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod); + } + + // + // Encapsulates most of the Method's state + // + public class MethodData + { + public readonly IMethodData method; + + // + // Are we implementing an interface ? + // + public MethodSpec implementing; + + // + // Protected data. + // + protected InterfaceMemberBase member; + protected Modifiers modifiers; + protected MethodAttributes flags; + protected TypeSpec declaring_type; + protected MethodSpec parent_method; + SourceMethodBuilder debug_builder; + string full_name; + + MethodBuilder builder; + public MethodBuilder MethodBuilder { + get { + return builder; + } + } + + public TypeSpec DeclaringType { + get { + return declaring_type; + } + } + + public string MetadataName { + get { + return full_name; + } + } + + public MethodData (InterfaceMemberBase member, + Modifiers modifiers, MethodAttributes flags, IMethodData method) + { + this.member = member; + this.modifiers = modifiers; + this.flags = flags; + + this.method = method; + } + + public MethodData (InterfaceMemberBase member, + Modifiers modifiers, MethodAttributes flags, + IMethodData method, + MethodSpec parent_method) + : this (member, modifiers, flags, method) + { + this.parent_method = parent_method; + } + + public bool Define (TypeDefinition container, string method_full_name) + { + PendingImplementation pending = container.PendingImplementations; + MethodSpec ambig_iface_method; + bool optional = false; + + if (pending != null) { + implementing = pending.IsInterfaceMethod (method.MethodName, member.InterfaceType, this, out ambig_iface_method, ref optional); + + if (member.InterfaceType != null) { + if (implementing == null) { + if (member is PropertyBase) { + container.Compiler.Report.Error (550, method.Location, + "`{0}' is an accessor not found in interface member `{1}{2}'", + method.GetSignatureForError (), member.InterfaceType.GetSignatureForError (), + member.GetSignatureForError ().Substring (member.GetSignatureForError ().LastIndexOf ('.'))); + + } else { + container.Compiler.Report.Error (539, method.Location, + "`{0}.{1}' in explicit interface declaration is not a member of interface", + member.InterfaceType.GetSignatureForError (), member.ShortName); + } + return false; + } + if (implementing.IsAccessor && !method.IsAccessor) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (683, method.Location, + "`{0}' explicit method implementation cannot implement `{1}' because it is an accessor", + member.GetSignatureForError (), implementing.GetSignatureForError ()); + return false; + } + } else { + if (implementing != null && !optional) { + if (!method.IsAccessor) { + if (implementing.IsAccessor) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (470, method.Location, + "Method `{0}' cannot implement interface accessor `{1}'", + method.GetSignatureForError (), TypeManager.CSharpSignature (implementing)); + } + } else if (implementing.DeclaringType.IsInterface) { + if (!implementing.IsAccessor) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (686, method.Location, + "Accessor `{0}' cannot implement interface member `{1}' for type `{2}'. Use an explicit interface implementation", + method.GetSignatureForError (), TypeManager.CSharpSignature (implementing), container.GetSignatureForError ()); + } else { + PropertyBase.PropertyMethod pm = method as PropertyBase.PropertyMethod; + if (pm != null && pm.HasCustomAccessModifier && (pm.ModFlags & Modifiers.PUBLIC) == 0) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (277, method.Location, + "Accessor `{0}' must be declared public to implement interface member `{1}'", + method.GetSignatureForError (), implementing.GetSignatureForError ()); + } + } + } + } + } + } else { + ambig_iface_method = null; + } + + // + // For implicit implementations, make sure we are public, for + // explicit implementations, make sure we are private. + // + if (implementing != null){ + if (member.IsExplicitImpl) { + if (method.ParameterInfo.HasParams && !implementing.Parameters.HasParams) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (466, method.Location, + "`{0}': the explicit interface implementation cannot introduce the params modifier", + method.GetSignatureForError ()); + } + + if (ambig_iface_method != null) { + container.Compiler.Report.SymbolRelatedToPreviousError (ambig_iface_method); + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Warning (473, 2, method.Location, + "Explicit interface implementation `{0}' matches more than one interface member. Consider using a non-explicit implementation instead", + method.GetSignatureForError ()); + } + } else { + // + // Setting implementin to null inside this block will trigger a more + // verbose error reporting for missing interface implementations + // + if (implementing.DeclaringType.IsInterface) { + // + // If this is an interface method implementation, + // check for public accessibility + // + if ((flags & MethodAttributes.MemberAccessMask) != MethodAttributes.Public) { + implementing = null; + } else if (optional && (container.Interfaces == null || !container.Definition.Interfaces.Contains (implementing.DeclaringType))) { + // + // We are not implementing interface when base class already implemented it + // + implementing = null; + } + } else if ((flags & MethodAttributes.MemberAccessMask) == MethodAttributes.Private) { + // We may never be private. + implementing = null; + + } else if ((modifiers & Modifiers.OVERRIDE) == 0) { + // + // We may be protected if we're overriding something. + // + implementing = null; + } + } + + // + // Static is not allowed + // + if ((modifiers & Modifiers.STATIC) != 0){ + implementing = null; + } + } + + // + // If implementing is still valid, set flags + // + if (implementing != null){ + // + // When implementing interface methods, set NewSlot + // unless, we are overwriting a method. + // + if ((modifiers & Modifiers.OVERRIDE) == 0 && implementing.DeclaringType.IsInterface) { + flags |= MethodAttributes.NewSlot; + } + + flags |= MethodAttributes.Virtual | MethodAttributes.HideBySig; + + // Set Final unless we're virtual, abstract or already overriding a method. + if ((modifiers & (Modifiers.VIRTUAL | Modifiers.ABSTRACT | Modifiers.OVERRIDE)) == 0) + flags |= MethodAttributes.Final; + + // + // clear the pending implementation flag (requires explicit methods to be defined first) + // + pending.ImplementMethod (method.MethodName, + member.InterfaceType, this, member.IsExplicitImpl, out ambig_iface_method, ref optional); + + // + // Update indexer accessor name to match implementing abstract accessor + // + if (!implementing.DeclaringType.IsInterface && !member.IsExplicitImpl && implementing.IsAccessor) + method_full_name = implementing.MemberDefinition.Name; + } + + full_name = method_full_name; + declaring_type = container.Definition; + + return true; + } + + void DefineOverride (TypeDefinition container) + { + if (implementing == null) + return; + + if (!member.IsExplicitImpl) + return; + + container.TypeBuilder.DefineMethodOverride (builder, (MethodInfo) implementing.GetMetaInfo ()); + } + + // + // Creates partial MethodBuilder for the method when has generic parameters used + // as arguments or return type + // + public MethodBuilder DefineMethodBuilder (TypeDefinition container) + { + if (builder != null) + throw new InternalErrorException (); + + builder = container.TypeBuilder.DefineMethod (full_name, flags, method.CallingConventions); + return builder; + } + + // + // Creates full MethodBuilder for the method + // + public MethodBuilder DefineMethodBuilder (TypeDefinition container, ParametersCompiled param) + { + DefineMethodBuilder (container); + builder.SetReturnType (method.ReturnType.GetMetaInfo ()); + builder.SetParameters (param.GetMetaInfo ()); + return builder; + } + + // + // Emits the code + // + public void Emit (TypeDefinition parent) + { + DefineOverride (parent); + + method.ParameterInfo.ApplyAttributes (method, MethodBuilder); + + ToplevelBlock block = method.Block; + if (block != null) { + BlockContext bc = new BlockContext (method, block, method.ReturnType); + if (block.Resolve (bc, method)) { + debug_builder = member.Parent.CreateMethodSymbolEntry (); + EmitContext ec = method.CreateEmitContext (MethodBuilder.GetILGenerator (), debug_builder); + + block.Emit (ec); + } + } + } + + public void WriteDebugSymbol (MonoSymbolFile file) + { + if (debug_builder == null) + return; + + var token = builder.GetToken (); + int t = token.Token; +#if STATIC + if (ModuleBuilder.IsPseudoToken (t)) + t = member.Module.Builder.ResolvePseudoToken (t); +#endif + + debug_builder.DefineMethod (file, t); + } + } + + public class Destructor : MethodOrOperator + { + const Modifiers AllowedModifiers = + Modifiers.UNSAFE | + Modifiers.EXTERN; + + static readonly string[] attribute_targets = new string [] { "method" }; + + public static readonly string MetadataName = "Finalize"; + + public string Identifier { + get; + set; + } + + public Destructor (TypeDefinition parent, Modifiers mod, ParametersCompiled parameters, Attributes attrs, Location l) + : base (parent, null, mod, AllowedModifiers, new MemberName (MetadataName, l), attrs, parameters) + { + ModFlags &= ~Modifiers.PRIVATE; + ModFlags |= Modifiers.PROTECTED | Modifiers.OVERRIDE; + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.Conditional) { + Error_ConditionalAttributeIsNotValid (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + protected override bool CheckBase () + { + if ((caching_flags & Flags.MethodOverloadsExist) != 0) + CheckForDuplications (); + + // Don't check base, destructors have special syntax + return true; + } + + public override bool Define () + { + base.Define (); + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + + return true; + } + + public override void Emit() + { + var base_type = Parent.PartialContainer.BaseType; + if (base_type != null && Block != null) { + var base_dtor = MemberCache.FindMember (base_type, + new MemberFilter (MetadataName, 0, MemberKind.Destructor, null, null), BindingRestriction.InstanceOnly) as MethodSpec; + + if (base_dtor == null) + throw new NotImplementedException (); + + MethodGroupExpr method_expr = MethodGroupExpr.CreatePredefined (base_dtor, base_type, Location); + method_expr.InstanceExpression = new BaseThis (base_type, Location); + + var try_block = new ExplicitBlock (block, block.StartLocation, block.EndLocation) { + IsCompilerGenerated = true + }; + var finaly_block = new ExplicitBlock (block, Location, Location) { + IsCompilerGenerated = true + }; + + // + // 0-size arguments to avoid CS0250 error + // TODO: Should use AddScopeStatement or something else which emits correct + // debugger scope + // + finaly_block.AddStatement (new StatementExpression (new Invocation (method_expr, new Arguments (0)), Location.Null)); + + var tf = new TryFinally (try_block, finaly_block, Location); + block.WrapIntoDestructor (tf, try_block); + } + + base.Emit (); + } + + public override string GetSignatureForError () + { + return Parent.GetSignatureForError () + ".~" + Parent.MemberName.Name + "()"; + } + + protected override bool ResolveMemberType () + { + member_type = Compiler.BuiltinTypes.Void; + return true; + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } + + // Ooouh Martin, templates are missing here. + // When it will be possible move here a lot of child code and template method type. + public abstract class AbstractPropertyEventMethod : MemberCore, IMethodData, IMethodDefinition { + protected MethodData method_data; + protected ToplevelBlock block; + protected SecurityType declarative_security; + + protected readonly string prefix; + + ReturnParameter return_attributes; + + protected AbstractPropertyEventMethod (InterfaceMemberBase member, string prefix, Attributes attrs, Location loc) + : base (member.Parent, SetupName (prefix, member, loc), attrs) + { + this.prefix = prefix; + } + + static MemberName SetupName (string prefix, InterfaceMemberBase member, Location loc) + { + return new MemberName (member.MemberName.Left, prefix + member.ShortName, member.MemberName.ExplicitInterface, loc); + } + + public void UpdateName (InterfaceMemberBase member) + { + SetMemberName (SetupName (prefix, member, Location)); + } + + #region IMethodData Members + + public ToplevelBlock Block { + get { + return block; + } + + set { + block = value; + } + } + + public CallingConventions CallingConventions { + get { + return CallingConventions.Standard; + } + } + + public EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + return new EmitContext (this, ig, ReturnType, sourceMethod); + } + + public bool IsAccessor { + get { + return true; + } + } + + public MemberName MethodName { + get { + return MemberName; + } + } + + public TypeSpec[] ParameterTypes { + get { + return ParameterInfo.Types; + } + } + + MethodBase IMethodDefinition.Metadata { + get { + return method_data.MethodBuilder; + } + } + + public abstract ParametersCompiled ParameterInfo { get ; } + public abstract TypeSpec ReturnType { get; } + + #endregion + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.CLSCompliant || a.Type == pa.Obsolete || a.Type == pa.Conditional) { + Report.Error (1667, a.Location, + "Attribute `{0}' is not valid on property or event accessors. It is valid on `{1}' declarations only", + a.Type.GetSignatureForError (), a.GetValidTargets ()); + return; + } + + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (a.Target == AttributeTargets.Method) { + method_data.MethodBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + return; + } + + if (a.Target == AttributeTargets.ReturnValue) { + if (return_attributes == null) + return_attributes = new ReturnParameter (this, method_data.MethodBuilder, Location); + + return_attributes.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + ApplyToExtraTarget (a, ctor, cdata, pa); + } + + protected virtual void ApplyToExtraTarget (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + throw new NotSupportedException ("You forgot to define special attribute target handling"); + } + + // It is not supported for the accessors + public sealed override bool Define() + { + throw new NotSupportedException (); + } + + public virtual void Emit (TypeDefinition parent) + { + method_data.Emit (parent); + + if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated) + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (method_data.MethodBuilder); + if (((ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0)) + Module.PredefinedAttributes.DebuggerHidden.EmitAttribute (method_data.MethodBuilder); + + if (ReturnType.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + return_attributes = new ReturnParameter (this, method_data.MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder); + } else if (ReturnType.HasDynamicElement) { + return_attributes = new ReturnParameter (this, method_data.MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder, ReturnType, Location); + } + + if (OptAttributes != null) + OptAttributes.Emit (); + + if (declarative_security != null) { + foreach (var de in declarative_security) { +#if STATIC + method_data.MethodBuilder.__AddDeclarativeSecurity (de); +#else + method_data.MethodBuilder.AddDeclarativeSecurity (de.Key, de.Value); +#endif + } + } + + block = null; + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + if (overload is MethodCore) { + caching_flags |= Flags.MethodOverloadsExist; + return true; + } + + // This can only happen with indexers and it will + // be catched as indexer difference + if (overload is AbstractPropertyEventMethod) + return true; + + return false; + } + + public override string GetCallerMemberName () + { + return base.GetCallerMemberName ().Substring (prefix.Length); + } + + public override string GetSignatureForDocumentation () + { + // should not be called + throw new NotSupportedException (); + } + + public override bool IsClsComplianceRequired() + { + return false; + } + + public void PrepareEmit () + { + method_data.DefineMethodBuilder (Parent.PartialContainer, ParameterInfo); + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (method_data != null) + method_data.WriteDebugSymbol (file); + } + + public MethodSpec Spec { get; protected set; } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader { + get { throw new InvalidOperationException ("Unexpected attempt to get doc comment from " + this.GetType () + "."); } + } + } + + public class Operator : MethodOrOperator { + + const Modifiers AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.STATIC; + + public enum OpType : byte { + + // Unary operators + LogicalNot, + OnesComplement, + Increment, + Decrement, + True, + False, + + // Unary and Binary operators + Addition, + Subtraction, + + UnaryPlus, + UnaryNegation, + + // Binary operators + Multiply, + Division, + Modulus, + BitwiseAnd, + BitwiseOr, + ExclusiveOr, + LeftShift, + RightShift, + Equality, + Inequality, + GreaterThan, + LessThan, + GreaterThanOrEqual, + LessThanOrEqual, + + // Implicit and Explicit + Implicit, + Explicit, + + // Just because of enum + TOP + }; + + public readonly OpType OperatorType; + + static readonly string [] [] names; + + static Operator () + { + names = new string[(int)OpType.TOP][]; + names [(int) OpType.LogicalNot] = new string [] { "!", "op_LogicalNot" }; + names [(int) OpType.OnesComplement] = new string [] { "~", "op_OnesComplement" }; + names [(int) OpType.Increment] = new string [] { "++", "op_Increment" }; + names [(int) OpType.Decrement] = new string [] { "--", "op_Decrement" }; + names [(int) OpType.True] = new string [] { "true", "op_True" }; + names [(int) OpType.False] = new string [] { "false", "op_False" }; + names [(int) OpType.Addition] = new string [] { "+", "op_Addition" }; + names [(int) OpType.Subtraction] = new string [] { "-", "op_Subtraction" }; + names [(int) OpType.UnaryPlus] = new string [] { "+", "op_UnaryPlus" }; + names [(int) OpType.UnaryNegation] = new string [] { "-", "op_UnaryNegation" }; + names [(int) OpType.Multiply] = new string [] { "*", "op_Multiply" }; + names [(int) OpType.Division] = new string [] { "/", "op_Division" }; + names [(int) OpType.Modulus] = new string [] { "%", "op_Modulus" }; + names [(int) OpType.BitwiseAnd] = new string [] { "&", "op_BitwiseAnd" }; + names [(int) OpType.BitwiseOr] = new string [] { "|", "op_BitwiseOr" }; + names [(int) OpType.ExclusiveOr] = new string [] { "^", "op_ExclusiveOr" }; + names [(int) OpType.LeftShift] = new string [] { "<<", "op_LeftShift" }; + names [(int) OpType.RightShift] = new string [] { ">>", "op_RightShift" }; + names [(int) OpType.Equality] = new string [] { "==", "op_Equality" }; + names [(int) OpType.Inequality] = new string [] { "!=", "op_Inequality" }; + names [(int) OpType.GreaterThan] = new string [] { ">", "op_GreaterThan" }; + names [(int) OpType.LessThan] = new string [] { "<", "op_LessThan" }; + names [(int) OpType.GreaterThanOrEqual] = new string [] { ">=", "op_GreaterThanOrEqual" }; + names [(int) OpType.LessThanOrEqual] = new string [] { "<=", "op_LessThanOrEqual" }; + names [(int) OpType.Implicit] = new string [] { "implicit", "op_Implicit" }; + names [(int) OpType.Explicit] = new string [] { "explicit", "op_Explicit" }; + } + + public Operator (TypeDefinition parent, OpType type, FullNamedExpression ret_type, Modifiers mod_flags, ParametersCompiled parameters, + ToplevelBlock block, Attributes attrs, Location loc) + : base (parent, ret_type, mod_flags, AllowedModifiers, new MemberName (GetMetadataName (type), loc), attrs, parameters) + { + OperatorType = type; + Block = block; + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.Conditional) { + Error_ConditionalAttributeIsNotValid (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override bool Define () + { + const Modifiers RequiredModifiers = Modifiers.PUBLIC | Modifiers.STATIC; + if ((ModFlags & RequiredModifiers) != RequiredModifiers){ + Report.Error (558, Location, "User-defined operator `{0}' must be declared static and public", GetSignatureForError ()); + } + + if (!base.Define ()) + return false; + + if (block != null) { + if (block.IsIterator) { + // + // Current method is turned into automatically generated + // wrapper which creates an instance of iterator + // + Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags); + ModFlags |= Modifiers.DEBUGGER_HIDDEN; + } + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + } + + // imlicit and explicit operator of same types are not allowed + if (OperatorType == OpType.Explicit) + Parent.MemberCache.CheckExistingMembersOverloads (this, GetMetadataName (OpType.Implicit), parameters); + else if (OperatorType == OpType.Implicit) + Parent.MemberCache.CheckExistingMembersOverloads (this, GetMetadataName (OpType.Explicit), parameters); + + TypeSpec declaring_type = Parent.CurrentType; + TypeSpec return_type = MemberType; + TypeSpec first_arg_type = ParameterTypes [0]; + + TypeSpec first_arg_type_unwrap = first_arg_type; + if (first_arg_type.IsNullableType) + first_arg_type_unwrap = Nullable.NullableInfo.GetUnderlyingType (first_arg_type); + + TypeSpec return_type_unwrap = return_type; + if (return_type.IsNullableType) + return_type_unwrap = Nullable.NullableInfo.GetUnderlyingType (return_type); + + // + // Rules for conversion operators + // + if (OperatorType == OpType.Implicit || OperatorType == OpType.Explicit) { + if (first_arg_type_unwrap == return_type_unwrap && first_arg_type_unwrap == declaring_type) { + Report.Error (555, Location, + "User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type"); + return false; + } + + TypeSpec conv_type; + if (declaring_type == return_type || declaring_type == return_type_unwrap) { + conv_type = first_arg_type; + } else if (declaring_type == first_arg_type || declaring_type == first_arg_type_unwrap) { + conv_type = return_type; + } else { + Report.Error (556, Location, + "User-defined conversion must convert to or from the enclosing type"); + return false; + } + + if (conv_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Report.Error (1964, Location, + "User-defined conversion `{0}' cannot convert to or from the dynamic type", + GetSignatureForError ()); + + return false; + } + + if (conv_type.IsInterface) { + Report.Error (552, Location, "User-defined conversion `{0}' cannot convert to or from an interface type", + GetSignatureForError ()); + return false; + } + + if (conv_type.IsClass) { + if (TypeSpec.IsBaseClass (declaring_type, conv_type, true)) { + Report.Error (553, Location, "User-defined conversion `{0}' cannot convert to or from a base class", + GetSignatureForError ()); + return false; + } + + if (TypeSpec.IsBaseClass (conv_type, declaring_type, false)) { + Report.Error (554, Location, "User-defined conversion `{0}' cannot convert to or from a derived class", + GetSignatureForError ()); + return false; + } + } + } else if (OperatorType == OpType.LeftShift || OperatorType == OpType.RightShift) { + if (first_arg_type != declaring_type || parameters.Types[1].BuiltinType != BuiltinTypeSpec.Type.Int) { + Report.Error (564, Location, "Overloaded shift operator must have the type of the first operand be the containing type, and the type of the second operand must be int"); + return false; + } + } else if (parameters.Count == 1) { + // Checks for Unary operators + + if (OperatorType == OpType.Increment || OperatorType == OpType.Decrement) { + if (return_type != declaring_type && !TypeSpec.IsBaseClass (return_type, declaring_type, false)) { + Report.Error (448, Location, + "The return type for ++ or -- operator must be the containing type or derived from the containing type"); + return false; + } + if (first_arg_type != declaring_type) { + Report.Error ( + 559, Location, "The parameter type for ++ or -- operator must be the containing type"); + return false; + } + } + + if (first_arg_type_unwrap != declaring_type) { + Report.Error (562, Location, + "The parameter type of a unary operator must be the containing type"); + return false; + } + + if (OperatorType == OpType.True || OperatorType == OpType.False) { + if (return_type.BuiltinType != BuiltinTypeSpec.Type.Bool) { + Report.Error ( + 215, Location, + "The return type of operator True or False " + + "must be bool"); + return false; + } + } + + } else if (first_arg_type_unwrap != declaring_type) { + // Checks for Binary operators + + var second_arg_type = ParameterTypes[1]; + if (second_arg_type.IsNullableType) + second_arg_type = Nullable.NullableInfo.GetUnderlyingType (second_arg_type); + + if (second_arg_type != declaring_type) { + Report.Error (563, Location, + "One of the parameters of a binary operator must be the containing type"); + return false; + } + } + + return true; + } + + protected override bool ResolveMemberType () + { + if (!base.ResolveMemberType ()) + return false; + + flags |= MethodAttributes.SpecialName | MethodAttributes.HideBySig; + return true; + } + + protected override MemberSpec FindBaseMember (out MemberSpec bestCandidate, ref bool overrides) + { + // Operator cannot be override + bestCandidate = null; + return null; + } + + public static string GetName (OpType ot) + { + return names [(int) ot] [0]; + } + + public static string GetName (string metadata_name) + { + for (int i = 0; i < names.Length; ++i) { + if (names [i] [1] == metadata_name) + return names [i] [0]; + } + return null; + } + + public static string GetMetadataName (OpType ot) + { + return names [(int) ot] [1]; + } + + public static string GetMetadataName (string name) + { + for (int i = 0; i < names.Length; ++i) { + if (names [i] [0] == name) + return names [i] [1]; + } + return null; + } + + public static OpType? GetType (string metadata_name) + { + for (int i = 0; i < names.Length; ++i) { + if (names[i][1] == metadata_name) + return (OpType) i; + } + + return null; + } + + public OpType GetMatchingOperator () + { + switch (OperatorType) { + case OpType.Equality: + return OpType.Inequality; + case OpType.Inequality: + return OpType.Equality; + case OpType.True: + return OpType.False; + case OpType.False: + return OpType.True; + case OpType.GreaterThan: + return OpType.LessThan; + case OpType.LessThan: + return OpType.GreaterThan; + case OpType.GreaterThanOrEqual: + return OpType.LessThanOrEqual; + case OpType.LessThanOrEqual: + return OpType.GreaterThanOrEqual; + default: + return OpType.TOP; + } + } + + public override string GetSignatureForDocumentation () + { + string s = base.GetSignatureForDocumentation (); + if (OperatorType == OpType.Implicit || OperatorType == OpType.Explicit) { + s = s + "~" + ReturnType.GetSignatureForDocumentation (); + } + + return s; + } + + public override string GetSignatureForError () + { + StringBuilder sb = new StringBuilder (); + if (OperatorType == OpType.Implicit || OperatorType == OpType.Explicit) { + sb.AppendFormat ("{0}.{1} operator {2}", + Parent.GetSignatureForError (), GetName (OperatorType), + member_type == null ? type_expr.GetSignatureForError () : member_type.GetSignatureForError ()); + } + else { + sb.AppendFormat ("{0}.operator {1}", Parent.GetSignatureForError (), GetName (OperatorType)); + } + + sb.Append (parameters.GetSignatureForError ()); + return sb.ToString (); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/modifiers.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/modifiers.cs new file mode 100644 index 000000000..c8c6b73e6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/modifiers.cs @@ -0,0 +1,282 @@ +// +// modifiers.cs: Modifiers handling +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2010 Novell, Inc +// + +using System; + +#if STATIC +using IKVM.Reflection; +#else +using System.Reflection; +#endif + +namespace Mono.CSharp +{ + [Flags] + public enum Modifiers + { + PROTECTED = 0x0001, + PUBLIC = 0x0002, + PRIVATE = 0x0004, + INTERNAL = 0x0008, + NEW = 0x0010, + ABSTRACT = 0x0020, + SEALED = 0x0040, + STATIC = 0x0080, + READONLY = 0x0100, + VIRTUAL = 0x0200, + OVERRIDE = 0x0400, + EXTERN = 0x0800, + VOLATILE = 0x1000, + UNSAFE = 0x2000, + ASYNC = 0x4000, + TOP = 0x8000, + + // + // Compiler specific flags + // + PROPERTY_CUSTOM = 0x10000, + + PARTIAL = 0x20000, + DEFAULT_ACCESS_MODIFIER = 0x40000, + METHOD_EXTENSION = 0x80000, + COMPILER_GENERATED = 0x100000, + BACKING_FIELD = 0x200000, + DEBUGGER_HIDDEN = 0x400000, + DEBUGGER_STEP_THROUGH = 0x800000, + + AccessibilityMask = PUBLIC | PROTECTED | INTERNAL | PRIVATE, + AllowedExplicitImplFlags = UNSAFE | EXTERN, + } + + static class ModifiersExtensions + { + public static string AccessibilityName (Modifiers mod) + { + switch (mod & Modifiers.AccessibilityMask) { + case Modifiers.PUBLIC: + return "public"; + case Modifiers.PROTECTED: + return "protected"; + case Modifiers.PROTECTED | Modifiers.INTERNAL: + return "protected internal"; + case Modifiers.INTERNAL: + return "internal"; + case Modifiers.PRIVATE: + return "private"; + default: + throw new NotImplementedException (mod.ToString ()); + } + } + + static public string Name (Modifiers i) + { + string s = ""; + + switch (i) { + case Modifiers.NEW: + s = "new"; break; + case Modifiers.PUBLIC: + s = "public"; break; + case Modifiers.PROTECTED: + s = "protected"; break; + case Modifiers.INTERNAL: + s = "internal"; break; + case Modifiers.PRIVATE: + s = "private"; break; + case Modifiers.ABSTRACT: + s = "abstract"; break; + case Modifiers.SEALED: + s = "sealed"; break; + case Modifiers.STATIC: + s = "static"; break; + case Modifiers.READONLY: + s = "readonly"; break; + case Modifiers.VIRTUAL: + s = "virtual"; break; + case Modifiers.OVERRIDE: + s = "override"; break; + case Modifiers.EXTERN: + s = "extern"; break; + case Modifiers.VOLATILE: + s = "volatile"; break; + case Modifiers.UNSAFE: + s = "unsafe"; break; + case Modifiers.ASYNC: + s = "async"; break; + } + + return s; + } + + // + // Used by custom property accessors to check whether @modA is more restrictive than @modB + // + public static bool IsRestrictedModifier (Modifiers modA, Modifiers modB) + { + Modifiers flags = 0; + + if ((modB & Modifiers.PUBLIC) != 0) { + flags = Modifiers.PROTECTED | Modifiers.INTERNAL | Modifiers.PRIVATE; + } else if ((modB & Modifiers.PROTECTED) != 0) { + if ((modB & Modifiers.INTERNAL) != 0) + flags = Modifiers.PROTECTED | Modifiers.INTERNAL; + + flags |= Modifiers.PRIVATE; + } else if ((modB & Modifiers.INTERNAL) != 0) + flags = Modifiers.PRIVATE; + + return modB != modA && (modA & (~flags)) == 0; + } + + public static TypeAttributes TypeAttr (Modifiers mod_flags, bool is_toplevel) + { + TypeAttributes t = 0; + + if (is_toplevel){ + if ((mod_flags & Modifiers.PUBLIC) != 0) + t = TypeAttributes.Public; + else if ((mod_flags & Modifiers.PRIVATE) != 0) + t = TypeAttributes.NotPublic; + } else { + if ((mod_flags & Modifiers.PUBLIC) != 0) + t = TypeAttributes.NestedPublic; + else if ((mod_flags & Modifiers.PRIVATE) != 0) + t = TypeAttributes.NestedPrivate; + else if ((mod_flags & (Modifiers.PROTECTED | Modifiers.INTERNAL)) == (Modifiers.PROTECTED | Modifiers.INTERNAL)) + t = TypeAttributes.NestedFamORAssem; + else if ((mod_flags & Modifiers.PROTECTED) != 0) + t = TypeAttributes.NestedFamily; + else if ((mod_flags & Modifiers.INTERNAL) != 0) + t = TypeAttributes.NestedAssembly; + } + + if ((mod_flags & Modifiers.SEALED) != 0) + t |= TypeAttributes.Sealed; + if ((mod_flags & Modifiers.ABSTRACT) != 0) + t |= TypeAttributes.Abstract; + + return t; + } + + public static FieldAttributes FieldAttr (Modifiers mod_flags) + { + FieldAttributes fa = 0; + + if ((mod_flags & Modifiers.PUBLIC) != 0) + fa |= FieldAttributes.Public; + if ((mod_flags & Modifiers.PRIVATE) != 0) + fa |= FieldAttributes.Private; + if ((mod_flags & Modifiers.PROTECTED) != 0) { + if ((mod_flags & Modifiers.INTERNAL) != 0) + fa |= FieldAttributes.FamORAssem; + else + fa |= FieldAttributes.Family; + } else { + if ((mod_flags & Modifiers.INTERNAL) != 0) + fa |= FieldAttributes.Assembly; + } + + if ((mod_flags & Modifiers.STATIC) != 0) + fa |= FieldAttributes.Static; + if ((mod_flags & Modifiers.READONLY) != 0) + fa |= FieldAttributes.InitOnly; + + return fa; + } + + public static MethodAttributes MethodAttr (Modifiers mod_flags) + { + MethodAttributes ma = MethodAttributes.HideBySig; + + switch (mod_flags & Modifiers.AccessibilityMask) { + case Modifiers.PUBLIC: + ma |= MethodAttributes.Public; + break; + case Modifiers.PRIVATE: + ma |= MethodAttributes.Private; + break; + case Modifiers.PROTECTED | Modifiers.INTERNAL: + ma |= MethodAttributes.FamORAssem; + break; + case Modifiers.PROTECTED: + ma |= MethodAttributes.Family; + break; + case Modifiers.INTERNAL: + ma |= MethodAttributes.Assembly; + break; + default: + throw new NotImplementedException (mod_flags.ToString ()); + } + + if ((mod_flags & Modifiers.STATIC) != 0) + ma |= MethodAttributes.Static; + if ((mod_flags & Modifiers.ABSTRACT) != 0) { + ma |= MethodAttributes.Abstract | MethodAttributes.Virtual; + } + if ((mod_flags & Modifiers.SEALED) != 0) + ma |= MethodAttributes.Final; + + if ((mod_flags & Modifiers.VIRTUAL) != 0) + ma |= MethodAttributes.Virtual; + + if ((mod_flags & Modifiers.OVERRIDE) != 0) { + ma |= MethodAttributes.Virtual; + } else { + if ((ma & MethodAttributes.Virtual) != 0) + ma |= MethodAttributes.NewSlot; + } + + return ma; + } + + // + // Checks the object @mod modifiers to be in @allowed. + // Returns the new mask. Side effect: reports any + // incorrect attributes. + // + public static Modifiers Check (Modifiers allowed, Modifiers mod, Modifiers def_access, Location l, Report Report) + { + int invalid_flags = (~(int) allowed) & ((int) mod & ((int) Modifiers.TOP - 1)); + int i; + + if (invalid_flags == 0){ + // + // If no accessibility bits provided + // then provide the defaults. + // + if ((mod & Modifiers.AccessibilityMask) == 0) { + mod |= def_access; + if (def_access != 0) + mod |= Modifiers.DEFAULT_ACCESS_MODIFIER; + return mod; + } + + return mod; + } + + for (i = 1; i < (int) Modifiers.TOP; i <<= 1) { + if ((i & invalid_flags) == 0) + continue; + + Error_InvalidModifier ((Modifiers)i, l, Report); + } + + return allowed & mod; + } + + static void Error_InvalidModifier (Modifiers mod, Location l, Report Report) + { + Report.Error (106, l, "The modifier `{0}' is not valid for this item", + Name (mod)); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/module.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/module.cs new file mode 100644 index 000000000..68f1b8ece --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/module.cs @@ -0,0 +1,713 @@ +// +// module.cs: keeps a tree representation of the generated code +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Mono.CompilerServices.SymbolWriter; +using System.Linq; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // + // Module (top-level type) container + // + public sealed class ModuleContainer : TypeContainer + { +#if STATIC + // + // Compiler generated container for static data + // + sealed class StaticDataContainer : CompilerGeneratedContainer + { + readonly Dictionary size_types; + int fields; + + public StaticDataContainer (ModuleContainer module) + : base (module, new MemberName ("" + module.builder.ModuleVersionId.ToString ("B"), Location.Null), + Modifiers.STATIC | Modifiers.INTERNAL) + { + size_types = new Dictionary (); + } + + public override void CloseContainer () + { + base.CloseContainer (); + + foreach (var entry in size_types) { + entry.Value.CloseContainer (); + } + } + + public FieldSpec DefineInitializedData (byte[] data, Location loc) + { + Struct size_type; + if (!size_types.TryGetValue (data.Length, out size_type)) { + // + // Build common type for this data length. We cannot use + // DefineInitializedData because it creates public type, + // and its name is not unique among modules + // + size_type = new Struct (this, new MemberName ("$ArrayType=" + data.Length, loc), Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, null); + size_type.CreateContainer (); + size_type.DefineContainer (); + + size_types.Add (data.Length, size_type); + + // It has to work even if StructLayoutAttribute does not exist + size_type.TypeBuilder.__SetLayout (1, data.Length); + } + + var name = "$field-" + fields.ToString ("X"); + ++fields; + const Modifiers fmod = Modifiers.STATIC | Modifiers.INTERNAL; + var fbuilder = TypeBuilder.DefineField (name, size_type.CurrentType.GetMetaInfo (), ModifiersExtensions.FieldAttr (fmod) | FieldAttributes.HasFieldRVA); + fbuilder.__SetDataAndRVA (data); + + return new FieldSpec (CurrentType, null, size_type.CurrentType, fbuilder, fmod); + } + } + + StaticDataContainer static_data; + + // + // Makes const data field inside internal type container + // + public FieldSpec MakeStaticData (byte[] data, Location loc) + { + if (static_data == null) { + static_data = new StaticDataContainer (this); + static_data.CreateContainer (); + static_data.DefineContainer (); + + AddCompilerGeneratedClass (static_data); + } + + return static_data.DefineInitializedData (data, loc); + } +#endif + + public sealed class PatternMatchingHelper : CompilerGeneratedContainer + { + public PatternMatchingHelper (ModuleContainer module) + : base (module, new MemberName ("", Location.Null), + Modifiers.STATIC | Modifiers.INTERNAL | Modifiers.DEBUGGER_HIDDEN) + { + } + + public Method NumberMatcher { get; private set; } + + protected override bool DoDefineMembers () + { + if (!base.DoDefineMembers ()) + return false; + + NumberMatcher = GenerateNumberMatcher (); + return true; + } + + Method GenerateNumberMatcher () + { + var loc = Location; + var parameters = ParametersCompiled.CreateFullyResolved ( + new [] { + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "obj", 0, null, loc), + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "value", 0, null, loc), + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Bool, loc), "enumType", 0, null, loc), + }, + new [] { + Compiler.BuiltinTypes.Object, + Compiler.BuiltinTypes.Object, + Compiler.BuiltinTypes.Bool + }); + + var m = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Bool, loc), + Modifiers.PUBLIC | Modifiers.STATIC | Modifiers.DEBUGGER_HIDDEN, new MemberName ("NumberMatcher", loc), + parameters, null); + + parameters [0].Resolve (m, 0); + parameters [1].Resolve (m, 1); + parameters [2].Resolve (m, 2); + + ToplevelBlock top_block = new ToplevelBlock (Compiler, parameters, loc); + m.Block = top_block; + + // + // if (enumType) + // return Equals (obj, value); + // + var equals_args = new Arguments (2); + equals_args.Add (new Argument (top_block.GetParameterReference (0, loc))); + equals_args.Add (new Argument (top_block.GetParameterReference (1, loc))); + + var if_type = new If ( + top_block.GetParameterReference (2, loc), + new Return (new Invocation (new SimpleName ("Equals", loc), equals_args), loc), + loc); + + top_block.AddStatement (if_type); + + // + // if (obj is Enum || obj == null) + // return false; + // + + var if_enum = new If ( + new Binary (Binary.Operator.LogicalOr, + new Is (top_block.GetParameterReference (0, loc), new TypeExpression (Compiler.BuiltinTypes.Enum, loc), loc), + new Binary (Binary.Operator.Equality, top_block.GetParameterReference (0, loc), new NullLiteral (loc))), + new Return (new BoolLiteral (Compiler.BuiltinTypes, false, loc), loc), + loc); + + top_block.AddStatement (if_enum); + + + var system_convert = new MemberAccess (new QualifiedAliasMember ("global", "System", loc), "Convert", loc); + + // + // var converted = System.Convert.ChangeType (obj, System.Convert.GetTypeCode (value)); + // + var lv_converted = LocalVariable.CreateCompilerGenerated (Compiler.BuiltinTypes.Object, top_block, loc); + + var arguments_gettypecode = new Arguments (1); + arguments_gettypecode.Add (new Argument (top_block.GetParameterReference (1, loc))); + + var gettypecode = new Invocation (new MemberAccess (system_convert, "GetTypeCode", loc), arguments_gettypecode); + + var arguments_changetype = new Arguments (1); + arguments_changetype.Add (new Argument (top_block.GetParameterReference (0, loc))); + arguments_changetype.Add (new Argument (gettypecode)); + + var changetype = new Invocation (new MemberAccess (system_convert, "ChangeType", loc), arguments_changetype); + + top_block.AddStatement (new StatementExpression (new SimpleAssign (new LocalVariableReference (lv_converted, loc), changetype, loc))); + + + // + // return converted.Equals (value) + // + var equals_arguments = new Arguments (1); + equals_arguments.Add (new Argument (top_block.GetParameterReference (1, loc))); + var equals_invocation = new Invocation (new MemberAccess (new LocalVariableReference (lv_converted, loc), "Equals"), equals_arguments); + top_block.AddStatement (new Return (equals_invocation, loc)); + + m.Define (); + m.PrepareEmit (); + AddMember (m); + + return m; + } + } + + PatternMatchingHelper pmh; + + public PatternMatchingHelper CreatePatterMatchingHelper () + { + if (pmh == null) { + pmh = new PatternMatchingHelper (this); + + pmh.CreateContainer (); + pmh.DefineContainer (); + pmh.Define (); + AddCompilerGeneratedClass (pmh); + } + + return pmh; + } + + public CharSet? DefaultCharSet; + public TypeAttributes DefaultCharSetType = TypeAttributes.AnsiClass; + + readonly Dictionary> anonymous_types; + readonly Dictionary array_types; + readonly Dictionary pointer_types; + readonly Dictionary reference_types; + readonly Dictionary attrs_cache; + readonly Dictionary awaiters; + + AssemblyDefinition assembly; + readonly CompilerContext context; + readonly RootNamespace global_ns; + readonly Dictionary alias_ns; + + ModuleBuilder builder; + + bool has_extenstion_method; + + PredefinedAttributes predefined_attributes; + PredefinedTypes predefined_types; + PredefinedMembers predefined_members; + + public Binary.PredefinedOperator[] OperatorsBinaryEqualityLifted; + public Binary.PredefinedOperator[] OperatorsBinaryLifted; + + static readonly string[] attribute_targets = new string[] { "assembly", "module" }; + + public ModuleContainer (CompilerContext context) + : base (null, MemberName.Null, null, 0) + { + this.context = context; + + caching_flags &= ~(Flags.Obsolete_Undetected | Flags.Excluded_Undetected); + + containers = new List (); + anonymous_types = new Dictionary> (); + global_ns = new GlobalRootNamespace (); + alias_ns = new Dictionary (); + array_types = new Dictionary (); + pointer_types = new Dictionary (); + reference_types = new Dictionary (); + attrs_cache = new Dictionary (); + awaiters = new Dictionary (); + } + + #region Properties + + internal Dictionary ArrayTypesCache { + get { + return array_types; + } + } + + // + // Cache for parameter-less attributes + // + internal Dictionary AttributeConstructorCache { + get { + return attrs_cache; + } + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Assembly; + } + } + + public ModuleBuilder Builder { + get { + return builder; + } + } + + public override CompilerContext Compiler { + get { + return context; + } + } + + public int CounterAnonymousTypes { get; set; } + + public AssemblyDefinition DeclaringAssembly { + get { + return assembly; + } + } + + internal DocumentationBuilder DocumentationBuilder { + get; set; + } + + public override string DocCommentHeader { + get { + throw new NotSupportedException (); + } + } + + public Evaluator Evaluator { + get; set; + } + + public bool HasDefaultCharSet { + get { + return DefaultCharSet.HasValue; + } + } + + public bool HasExtensionMethod { + get { + return has_extenstion_method; + } + set { + has_extenstion_method = value; + } + } + + public bool HasTypesFullyDefined { + get; set; + } + + // + // Returns module global:: namespace + // + public RootNamespace GlobalRootNamespace { + get { + return global_ns; + } + } + + public override ModuleContainer Module { + get { + return this; + } + } + + internal Dictionary PointerTypesCache { + get { + return pointer_types; + } + } + + internal PredefinedAttributes PredefinedAttributes { + get { + return predefined_attributes; + } + } + + internal PredefinedMembers PredefinedMembers { + get { + return predefined_members; + } + } + + internal PredefinedTypes PredefinedTypes { + get { + return predefined_types; + } + } + + internal Dictionary ReferenceTypesCache { + get { + return reference_types; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public void AddAnonymousType (AnonymousTypeClass type) + { + List existing; + if (!anonymous_types.TryGetValue (type.Parameters.Count, out existing)) + if (existing == null) { + existing = new List (); + anonymous_types.Add (type.Parameters.Count, existing); + } + + existing.Add (type); + } + + public void AddAttribute (Attribute attr, IMemberContext context) + { + attr.AttachTo (this, context); + + if (attributes == null) { + attributes = new Attributes (attr); + return; + } + + attributes.AddAttribute (attr); + } + + public override void AddTypeContainer (TypeContainer tc) + { + AddTypeContainerMember (tc); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Assembly) { + assembly.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + if (a.Type == pa.DefaultCharset) { + switch (a.GetCharSetValue ()) { + case CharSet.Ansi: + case CharSet.None: + break; + case CharSet.Auto: + DefaultCharSet = CharSet.Auto; + DefaultCharSetType = TypeAttributes.AutoClass; + break; + case CharSet.Unicode: + DefaultCharSet = CharSet.Unicode; + DefaultCharSetType = TypeAttributes.UnicodeClass; + break; + default: + Report.Error (1724, a.Location, "Value specified for the argument to `{0}' is not valid", + a.GetSignatureForError ()); + break; + } + } else if (a.Type == pa.CLSCompliant) { + Attribute cls = DeclaringAssembly.CLSCompliantAttribute; + if (cls == null) { + Report.Warning (3012, 1, a.Location, + "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking"); + } else if (DeclaringAssembly.IsCLSCompliant != a.GetBoolean ()) { + Report.SymbolRelatedToPreviousError (cls.Location, cls.GetSignatureForError ()); + Report.Warning (3017, 1, a.Location, + "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly"); + return; + } + } + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public override void CloseContainer () + { + if (anonymous_types != null) { + foreach (var atypes in anonymous_types) + foreach (var at in atypes.Value) + at.CloseContainer (); + } + + base.CloseContainer (); + } + + public TypeBuilder CreateBuilder (string name, TypeAttributes attr, int typeSize) + { + return builder.DefineType (name, attr, null, typeSize); + } + + // + // Creates alias global namespace + // + public RootNamespace CreateRootNamespace (string alias) + { + if (alias == global_ns.Alias) { + RootNamespace.Error_GlobalNamespaceRedefined (Report, Location.Null); + return global_ns; + } + + RootNamespace rn; + if (!alias_ns.TryGetValue (alias, out rn)) { + rn = new RootNamespace (alias); + alias_ns.Add (alias, rn); + } + + return rn; + } + + public void Create (AssemblyDefinition assembly, ModuleBuilder moduleBuilder) + { + this.assembly = assembly; + builder = moduleBuilder; + } + + public override bool Define () + { + DefineContainer (); + + ExpandBaseInterfaces (); + + base.Define (); + + HasTypesFullyDefined = true; + + return true; + } + + public override bool DefineContainer () + { + DefineNamespace (); + + return base.DefineContainer (); + } + + public void EnableRedefinition () + { + is_defined = false; + } + + public override void EmitContainer () + { + if (OptAttributes != null) + OptAttributes.Emit (); + + if (Compiler.Settings.Unsafe && !assembly.IsSatelliteAssembly) { + var pa = PredefinedAttributes.UnverifiableCode; + if (pa.IsDefined) + pa.EmitAttribute (builder); + } + + foreach (var tc in containers) { + tc.PrepareEmit (); + } + + base.EmitContainer (); + + if (Compiler.Report.Errors == 0 && !Compiler.Settings.WriteMetadataOnly) + VerifyMembers (); + + if (anonymous_types != null) { + foreach (var atypes in anonymous_types) + foreach (var at in atypes.Value) + at.EmitContainer (); + } + } + + internal override void GenerateDocComment (DocumentationBuilder builder) + { + foreach (var tc in containers) + tc.GenerateDocComment (builder); + } + + public AnonymousTypeClass GetAnonymousType (IList parameters) + { + List candidates; + if (!anonymous_types.TryGetValue (parameters.Count, out candidates)) + return null; + + int i; + foreach (AnonymousTypeClass at in candidates) { + for (i = 0; i < parameters.Count; ++i) { + if (!parameters [i].Equals (at.Parameters [i])) + break; + } + + if (i == parameters.Count) + return at; + } + + return null; + } + + // + // Return container with awaiter definition. It never returns null + // but all container member can be null for easier error reporting + // + public AwaiterDefinition GetAwaiter (TypeSpec type) + { + AwaiterDefinition awaiter; + if (awaiters.TryGetValue (type, out awaiter)) + return awaiter; + + awaiter = new AwaiterDefinition (); + + // + // Predefined: bool IsCompleted { get; } + // + awaiter.IsCompleted = MemberCache.FindMember (type, MemberFilter.Property ("IsCompleted", Compiler.BuiltinTypes.Bool), + BindingRestriction.InstanceOnly) as PropertySpec; + + // + // Predefined: GetResult () + // + // The method return type is also result type of await expression + // + awaiter.GetResult = MemberCache.FindMember (type, MemberFilter.Method ("GetResult", 0, + ParametersCompiled.EmptyReadOnlyParameters, null), + BindingRestriction.InstanceOnly) as MethodSpec; + + // + // Predefined: INotifyCompletion.OnCompleted (System.Action) + // + var nc = PredefinedTypes.INotifyCompletion; + awaiter.INotifyCompletion = !nc.Define () || type.ImplementsInterface (nc.TypeSpec, false); + + awaiters.Add (type, awaiter); + return awaiter; + } + + public override void GetCompletionStartingWith (string prefix, List results) + { + var names = Evaluator.GetVarNames (); + results.AddRange (names.Where (l => l.StartsWith (prefix))); + } + + public RootNamespace GetRootNamespace (string name) + { + RootNamespace rn; + alias_ns.TryGetValue (name, out rn); + return rn; + } + + public override string GetSignatureForError () + { + return ""; + } + + public Binary.PredefinedOperator[] GetPredefinedEnumAritmeticOperators (TypeSpec enumType, bool nullable) + { + TypeSpec underlying; + Binary.Operator mask = 0; + + if (nullable) { + underlying = Nullable.NullableInfo.GetEnumUnderlyingType (this, enumType); + mask = Binary.Operator.NullableMask; + } else { + underlying = EnumSpec.GetUnderlyingType (enumType); + } + + var operators = new[] { + new Binary.PredefinedOperator (enumType, underlying, + mask | Binary.Operator.AdditionMask | Binary.Operator.SubtractionMask | Binary.Operator.DecomposedMask, enumType), + new Binary.PredefinedOperator (underlying, enumType, + mask | Binary.Operator.AdditionMask | Binary.Operator.SubtractionMask | Binary.Operator.DecomposedMask, enumType), + new Binary.PredefinedOperator (enumType, mask | Binary.Operator.SubtractionMask, underlying) + }; + + return operators; + } + + public void InitializePredefinedTypes () + { + predefined_attributes = new PredefinedAttributes (this); + predefined_types = new PredefinedTypes (this); + predefined_members = new PredefinedMembers (this); + + OperatorsBinaryEqualityLifted = Binary.CreateEqualityLiftedOperatorsTable (this); + OperatorsBinaryLifted = Binary.CreateStandardLiftedOperatorsTable (this); + } + + public override bool IsClsComplianceRequired () + { + return DeclaringAssembly.IsCLSCompliant; + } + + public Attribute ResolveAssemblyAttribute (PredefinedAttribute a_type) + { + Attribute a = OptAttributes.Search ("assembly", a_type); + if (a != null) { + a.Resolve (); + } + return a; + } + + public void SetDeclaringAssembly (AssemblyDefinition assembly) + { + // TODO: This setter is quite ugly but I have not found a way around it yet + this.assembly = assembly; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs new file mode 100644 index 000000000..f4c5aba5b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs @@ -0,0 +1,1584 @@ +// +// namespace.cs: Tracks namespaces +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Copyright 2001 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc +// +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.CompilerServices.SymbolWriter; + +namespace Mono.CSharp { + + public class RootNamespace : Namespace { + + readonly string alias_name; + readonly Dictionary all_namespaces; + + public RootNamespace (string alias_name) + : base () + { + this.alias_name = alias_name; + RegisterNamespace (this); + + all_namespaces = new Dictionary (); + all_namespaces.Add ("", this); + } + + public string Alias { + get { + return alias_name; + } + } + + public static void Error_GlobalNamespaceRedefined (Report report, Location loc) + { + report.Error (1681, loc, "The global extern alias cannot be redefined"); + } + + // + // For better error reporting where we try to guess missing using directive + // + public List FindTypeNamespaces (IMemberContext ctx, string name, int arity) + { + List res = null; + + foreach (var ns in all_namespaces) { + var type = ns.Value.LookupType (ctx, name, arity, LookupMode.Normal, Location.Null); + if (type != null) { + if (res == null) + res = new List (); + + res.Add (ns.Key); + } + } + + return res; + } + + // + // For better error reporting where compiler tries to guess missing using directive + // + public List FindExtensionMethodNamespaces (IMemberContext ctx, string name, int arity) + { + List res = null; + + foreach (var ns in all_namespaces) { + var methods = ns.Value.LookupExtensionMethod (ctx, name, arity); + if (methods != null) { + if (res == null) + res = new List (); + + res.Add (ns.Key); + } + } + + return res; + } + + public void RegisterNamespace (Namespace child) + { + if (child != this) + all_namespaces.Add (child.Name, child); + } + + public override string GetSignatureForError () + { + return alias_name + "::"; + } + } + + public sealed class GlobalRootNamespace : RootNamespace + { + public GlobalRootNamespace () + : base ("global") + { + } + } + + // + // Namespace cache for imported and compiled namespaces + // + public class Namespace + { + readonly Namespace parent; + string fullname; + protected Dictionary namespaces; + protected Dictionary> types; + List extension_method_types; + Dictionary cached_types; + bool cls_checked; + + /// + /// Constructor Takes the current namespace and the + /// name. This is bootstrapped with parent == null + /// and name = "" + /// + public Namespace (Namespace parent, string name) + : this () + { + if (name == null) + throw new ArgumentNullException ("name"); + + this.parent = parent; + + string pname = parent != null ? parent.fullname : null; + + if (pname == null) + fullname = name; + else + fullname = pname + "." + name; + + while (parent.parent != null) + parent = parent.parent; + + var root = parent as RootNamespace; + if (root == null) + throw new InternalErrorException ("Root namespaces must be created using RootNamespace"); + + root.RegisterNamespace (this); + } + + protected Namespace () + { + namespaces = new Dictionary (); + cached_types = new Dictionary (); + } + + #region Properties + + /// + /// The qualified name of the current namespace + /// + public string Name { + get { return fullname; } + } + + /// + /// The parent of this namespace, used by the parser to "Pop" + /// the current namespace declaration + /// + public Namespace Parent { + get { return parent; } + } + + #endregion + + public Namespace AddNamespace (MemberName name) + { + var ns_parent = name.Left == null ? this : AddNamespace (name.Left); + return ns_parent.TryAddNamespace (name.Basename); + } + + Namespace TryAddNamespace (string name) + { + Namespace ns; + + if (!namespaces.TryGetValue (name, out ns)) { + ns = new Namespace (this, name); + namespaces.Add (name, ns); + } + + return ns; + } + + public bool TryGetNamespace (string name, out Namespace ns) + { + return namespaces.TryGetValue (name, out ns); + } + + // TODO: Replace with CreateNamespace where MemberName is created for the method call + public Namespace GetNamespace (string name, bool create) + { + int pos = name.IndexOf ('.'); + + Namespace ns; + string first; + if (pos >= 0) + first = name.Substring (0, pos); + else + first = name; + + if (!namespaces.TryGetValue (first, out ns)) { + if (!create) + return null; + + ns = new Namespace (this, first); + namespaces.Add (first, ns); + } + + if (pos >= 0) + ns = ns.GetNamespace (name.Substring (pos + 1), create); + + return ns; + } + + public IList GetAllTypes (string name) + { + IList found; + if (types == null || !types.TryGetValue (name, out found)) + return null; + + return found; + } + + public virtual string GetSignatureForError () + { + return fullname; + } + + public TypeSpec LookupType (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc) + { + if (types == null) + return null; + + TypeSpec best = null; + if (arity == 0 && cached_types.TryGetValue (name, out best)) { + if (best != null || mode != LookupMode.IgnoreAccessibility) + return best; + } + + IList found; + if (!types.TryGetValue (name, out found)) + return null; + + foreach (var ts in found) { + if (ts.Arity == arity || mode == LookupMode.NameOf) { + if (best == null) { + if ((ts.Modifiers & Modifiers.INTERNAL) != 0 && !ts.MemberDefinition.IsInternalAsPublic (ctx.Module.DeclaringAssembly) && mode != LookupMode.IgnoreAccessibility) + continue; + + best = ts; + continue; + } + + if (best.MemberDefinition.IsImported && ts.MemberDefinition.IsImported) { + if (ts.Kind == MemberKind.MissingType) + continue; + + if (best.Kind == MemberKind.MissingType) { + best = ts; + continue; + } + + if (mode == LookupMode.Normal) { + ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (best); + ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ts); + ctx.Module.Compiler.Report.Error (433, loc, "The imported type `{0}' is defined multiple times", ts.GetSignatureForError ()); + } + + break; + } + + if (best.MemberDefinition.IsImported) + best = ts; + + if ((best.Modifiers & Modifiers.INTERNAL) != 0 && !best.MemberDefinition.IsInternalAsPublic (ctx.Module.DeclaringAssembly)) + continue; + + if (mode != LookupMode.Normal) + continue; + + if (ts.MemberDefinition.IsImported) { + ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (best); + ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ts); + } + + ctx.Module.Compiler.Report.Warning (436, 2, loc, + "The type `{0}' conflicts with the imported type of same name'. Ignoring the imported type definition", + best.GetSignatureForError ()); + } + + // + // Lookup for the best candidate with the closest arity match + // + if (arity < 0) { + if (best == null) { + best = ts; + } else if (System.Math.Abs (ts.Arity + arity) < System.Math.Abs (best.Arity + arity)) { + best = ts; + } + } + } + + // TODO MemberCache: Cache more + if (arity == 0 && mode == LookupMode.Normal) + cached_types.Add (name, best); + + if (best != null) { + var dep = best.GetMissingDependencies (); + if (dep != null) + ImportedTypeDefinition.Error_MissingDependency (ctx, dep, loc); + } + + return best; + } + + public FullNamedExpression LookupTypeOrNamespace (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc) + { + var texpr = LookupType (ctx, name, arity, mode, loc); + + Namespace ns; + if (arity == 0 && namespaces.TryGetValue (name, out ns)) { + if (texpr == null) + return new NamespaceExpression (ns, loc); + + if (mode != LookupMode.Probing) { + //ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (texpr.Type); + // ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ns.loc, ""); + ctx.Module.Compiler.Report.Warning (437, 2, loc, + "The type `{0}' conflicts with the imported namespace `{1}'. Using the definition found in the source file", + texpr.GetSignatureForError (), ns.GetSignatureForError ()); + } + + if (texpr.MemberDefinition.IsImported) + return new NamespaceExpression (ns, loc); + } + + if (texpr == null) + return null; + + return new TypeExpression (texpr, loc); + } + + // + // Completes types with the given `prefix' + // + public IEnumerable CompletionGetTypesStartingWith (string prefix) + { + if (types == null) + return Enumerable.Empty (); + + var res = from item in types + where item.Key.StartsWith (prefix) && item.Value.Any (l => (l.Modifiers & Modifiers.PUBLIC) != 0) + select item.Key; + + if (namespaces != null) + res = res.Concat (from item in namespaces where item.Key.StartsWith (prefix) select item.Key); + + return res; + } + + // + // Looks for extension method in this namespace + // + public List LookupExtensionMethod (IMemberContext invocationContext, string name, int arity) + { + if (extension_method_types == null) + return null; + + List found = null; + for (int i = 0; i < extension_method_types.Count; ++i) { + var ts = extension_method_types[i]; + + // + // When the list was built we didn't know what members the type + // contains + // + if ((ts.Modifiers & Modifiers.METHOD_EXTENSION) == 0) { + if (extension_method_types.Count == 1) { + extension_method_types = null; + return found; + } + + extension_method_types.RemoveAt (i--); + continue; + } + + var res = ts.MemberCache.FindExtensionMethods (invocationContext, name, arity); + if (res == null) + continue; + + if (found == null) { + found = res; + } else { + found.AddRange (res); + } + } + + return found; + } + + public void AddType (ModuleContainer module, TypeSpec ts) + { + if (types == null) { + types = new Dictionary> (64); + } + + if (ts.IsClass && ts.Arity == 0) { + var extension_method_allowed = ts.MemberDefinition.IsImported ? (ts.Modifiers & Modifiers.METHOD_EXTENSION) != 0 : (ts.IsStatic || ts.MemberDefinition.IsPartial); + if (extension_method_allowed) { + if (extension_method_types == null) + extension_method_types = new List (); + + extension_method_types.Add (ts); + } + } + + var name = ts.Name; + IList existing; + if (types.TryGetValue (name, out existing)) { + TypeSpec better_type; + TypeSpec found; + if (existing.Count == 1) { + found = existing[0]; + if (ts.Arity == found.Arity) { + better_type = IsImportedTypeOverride (module, ts, found); + if (better_type == found) + return; + + if (better_type != null) { + existing [0] = better_type; + return; + } + } + + existing = new List (); + existing.Add (found); + types[name] = existing; + } else { + for (int i = 0; i < existing.Count; ++i) { + found = existing[i]; + if (ts.Arity != found.Arity) + continue; + + better_type = IsImportedTypeOverride (module, ts, found); + if (better_type == found) + return; + + if (better_type != null) { + existing.RemoveAt (i); + --i; + continue; + } + } + } + + existing.Add (ts); + } else { + types.Add (name, new TypeSpec[] { ts }); + } + } + + // + // We import any types but in the situation there are same types + // but one has better visibility (either public or internal with friend) + // the less visible type is removed from the namespace cache + // + public static TypeSpec IsImportedTypeOverride (ModuleContainer module, TypeSpec ts, TypeSpec found) + { + var ts_accessible = (ts.Modifiers & Modifiers.PUBLIC) != 0 || ts.MemberDefinition.IsInternalAsPublic (module.DeclaringAssembly); + var found_accessible = (found.Modifiers & Modifiers.PUBLIC) != 0 || found.MemberDefinition.IsInternalAsPublic (module.DeclaringAssembly); + + if (ts_accessible && !found_accessible) + return ts; + + // found is better always better for accessible or inaccessible ts + if (!ts_accessible) + return found; + + return null; + } + + public void RemoveContainer (TypeContainer tc) + { + types.Remove (tc.Basename); + cached_types.Remove (tc.Basename); + } + + public void SetBuiltinType (BuiltinTypeSpec pts) + { + var found = types[pts.Name]; + cached_types.Remove (pts.Name); + if (found.Count == 1) { + types[pts.Name][0] = pts; + } else { + throw new NotImplementedException (); + } + } + + public void VerifyClsCompliance () + { + if (types == null || cls_checked) + return; + + cls_checked = true; + + // TODO: This is quite ugly way to check for CLS compliance at namespace level + + var locase_types = new Dictionary> (StringComparer.OrdinalIgnoreCase); + foreach (var tgroup in types.Values) { + foreach (var tm in tgroup) { + if ((tm.Modifiers & Modifiers.PUBLIC) == 0 || !tm.IsCLSCompliant ()) + continue; + + List found; + if (!locase_types.TryGetValue (tm.Name, out found)) { + found = new List (); + locase_types.Add (tm.Name, found); + } + + found.Add (tm); + } + } + + foreach (var locase in locase_types.Values) { + if (locase.Count < 2) + continue; + + bool all_same = true; + foreach (var notcompliant in locase) { + all_same = notcompliant.Name == locase[0].Name; + if (!all_same) + break; + } + + if (all_same) + continue; + + TypeContainer compiled = null; + foreach (var notcompliant in locase) { + if (!notcompliant.MemberDefinition.IsImported) { + if (compiled != null) + compiled.Compiler.Report.SymbolRelatedToPreviousError (compiled); + + compiled = notcompliant.MemberDefinition as TypeContainer; + } else { + compiled.Compiler.Report.SymbolRelatedToPreviousError (notcompliant); + } + } + + compiled.Compiler.Report.Warning (3005, 1, compiled.Location, + "Identifier `{0}' differing only in case is not CLS-compliant", compiled.GetSignatureForError ()); + } + } + } + + public class CompilationSourceFile : NamespaceContainer + { + readonly SourceFile file; + CompileUnitEntry comp_unit; + Dictionary include_files; + Dictionary conditionals; + + public CompilationSourceFile (ModuleContainer parent, SourceFile sourceFile) + : this (parent) + { + this.file = sourceFile; + } + + public CompilationSourceFile (ModuleContainer parent) + : base (parent) + { + } + + public CompileUnitEntry SymbolUnitEntry { + get { + return comp_unit; + } + } + + public IDictionary Conditionals { + get { + return conditionals ?? new Dictionary (); + } + } + + public string FileName { + get { + return file.Name; + } + } + + public SourceFile SourceFile { + get { + return file; + } + } + + public void AddIncludeFile (SourceFile file) + { + if (file == this.file) + return; + + if (include_files == null) + include_files = new Dictionary (); + + if (!include_files.ContainsKey (file.FullPathName)) + include_files.Add (file.FullPathName, file); + } + + public void AddDefine (string value) + { + if (conditionals == null) + conditionals = new Dictionary (2); + + conditionals[value] = true; + } + + public void AddUndefine (string value) + { + if (conditionals == null) + conditionals = new Dictionary (2); + + conditionals[value] = false; + } + + public override void PrepareEmit () + { + var sw = Module.DeclaringAssembly.SymbolWriter; + if (sw != null) { + CreateUnitSymbolInfo (sw); + } + + base.PrepareEmit (); + } + + // + // Creates symbol file index in debug symbol file + // + void CreateUnitSymbolInfo (MonoSymbolFile symwriter) + { + var si = file.CreateSymbolInfo (symwriter); + comp_unit = new CompileUnitEntry (symwriter, si); + + if (include_files != null) { + foreach (SourceFile include in include_files.Values) { + si = include.CreateSymbolInfo (symwriter); + comp_unit.AddFile (si); + } + } + } + + public bool IsConditionalDefined (string value) + { + if (conditionals != null) { + bool res; + if (conditionals.TryGetValue (value, out res)) + return res; + + // When conditional was undefined + if (conditionals.ContainsKey (value)) + return false; + } + + return Compiler.Settings.IsConditionalSymbolDefined (value); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } + + + // + // Namespace block as created by the parser + // + public class NamespaceContainer : TypeContainer, IMemberContext + { + static readonly Namespace[] empty_namespaces = new Namespace[0]; + + readonly Namespace ns; + + public new readonly NamespaceContainer Parent; + + List clauses; + + // Used by parsed to check for parser errors + public bool DeclarationFound; + + Namespace[] namespace_using_table; + TypeSpec[] types_using_table; + Dictionary aliases; + public readonly MemberName RealMemberName; + + public NamespaceContainer (MemberName name, NamespaceContainer parent) + : base (parent, name, null, MemberKind.Namespace) + { + this.RealMemberName = name; + this.Parent = parent; + this.ns = parent.NS.AddNamespace (name); + + containers = new List (); + } + + protected NamespaceContainer (ModuleContainer parent) + : base (parent, null, null, MemberKind.Namespace) + { + ns = parent.GlobalRootNamespace; + containers = new List (2); + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + throw new NotSupportedException (); + } + } + + public override string DocCommentHeader { + get { + throw new NotSupportedException (); + } + } + + public Namespace NS { + get { + return ns; + } + } + + public List Usings { + get { + return clauses; + } + } + + public override string[] ValidAttributeTargets { + get { + throw new NotSupportedException (); + } + } + + #endregion + + public void AddUsing (UsingNamespace un) + { + if (DeclarationFound){ + Compiler.Report.Error (1529, un.Location, "A using clause must precede all other namespace elements except extern alias declarations"); + } + + if (clauses == null) + clauses = new List (); + + clauses.Add (un); + } + + public void AddUsing (UsingAliasNamespace un) + { + if (DeclarationFound){ + Compiler.Report.Error (1529, un.Location, "A using clause must precede all other namespace elements except extern alias declarations"); + } + + AddAlias (un); + } + + void AddAlias (UsingAliasNamespace un) + { + if (clauses == null) { + clauses = new List (); + } else { + foreach (var entry in clauses) { + var a = entry as UsingAliasNamespace; + if (a != null && a.Alias.Value == un.Alias.Value) { + Compiler.Report.SymbolRelatedToPreviousError (a.Location, ""); + Compiler.Report.Error (1537, un.Location, + "The using alias `{0}' appeared previously in this namespace", un.Alias.Value); + } + } + } + + clauses.Add (un); + } + + public override void AddPartial (TypeDefinition next_part) + { + var existing = ns.LookupType (this, next_part.MemberName.Name, next_part.MemberName.Arity, LookupMode.Probing, Location.Null); + var td = existing != null ? existing.MemberDefinition as TypeDefinition : null; + AddPartial (next_part, td); + } + + public override void AddTypeContainer (TypeContainer tc) + { + string name = tc.Basename; + + var mn = tc.MemberName; + while (mn.Left != null) { + mn = mn.Left; + name = mn.Name; + } + + var names_container = Parent == null ? Module : (TypeContainer) this; + + MemberCore mc; + if (names_container.DefinedNames.TryGetValue (name, out mc)) { + if (tc is NamespaceContainer && mc is NamespaceContainer) { + AddTypeContainerMember (tc); + return; + } + + Report.SymbolRelatedToPreviousError (mc); + if ((mc.ModFlags & Modifiers.PARTIAL) != 0 && (tc is ClassOrStruct || tc is Interface)) { + Error_MissingPartialModifier (tc); + } else { + Report.Error (101, tc.Location, "The namespace `{0}' already contains a definition for `{1}'", + GetSignatureForError (), mn.GetSignatureForError ()); + } + } else { + names_container.DefinedNames.Add (name, tc); + + var tdef = tc.PartialContainer; + if (tdef != null) { + // + // Same name conflict in different namespace containers + // + var conflict = ns.GetAllTypes (name); + if (conflict != null) { + foreach (var e in conflict) { + if (e.Arity == mn.Arity) { + mc = (MemberCore) e.MemberDefinition; + break; + } + } + } + + if (mc != null) { + Report.SymbolRelatedToPreviousError (mc); + Report.Error (101, tc.Location, "The namespace `{0}' already contains a definition for `{1}'", + GetSignatureForError (), mn.GetSignatureForError ()); + } else { + ns.AddType (Module, tdef.Definition); + } + } + } + + base.AddTypeContainer (tc); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + throw new NotSupportedException (); + } + + public override void EmitContainer () + { + VerifyClsCompliance (); + + base.EmitContainer (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (IMemberContext invocationContext, TypeSpec extensionType, string name, int arity, int position) + { + // + // Here we try to resume the search for extension method at the point + // where the last bunch of candidates was found. It's more tricky than + // it seems as we have to check both namespace containers and namespace + // in correct order. + // + // Consider: + // + // namespace A { + // using N1; + // namespace B.C.D { + // 0) { + mn = mn.Left; + container_ns = container_ns.Parent; + } + + while (mn != null) { + ++position; + + var methods = container_ns.LookupExtensionMethod (invocationContext, name, arity); + if (methods != null) { + return new ExtensionMethodCandidates (invocationContext, methods, container, position); + } + + mn = mn.Left; + container_ns = container_ns.Parent; + } + + position = 0; + container = container.Parent; + } while (container != null); + + return null; + } + + ExtensionMethodCandidates LookupExtensionMethodCandidates (IMemberContext invocationContext, string name, int arity, ref int position) + { + List candidates = null; + + if (position == 0) { + ++position; + + candidates = ns.LookupExtensionMethod (invocationContext, name, arity); + if (candidates != null) { + return new ExtensionMethodCandidates (invocationContext, candidates, this, position); + } + } + + if (position == 1) { + ++position; + + foreach (Namespace n in namespace_using_table) { + var a = n.LookupExtensionMethod (invocationContext, name, arity); + if (a == null) + continue; + + if (candidates == null) + candidates = a; + else + candidates.AddRange (a); + } + + if (candidates != null) + return new ExtensionMethodCandidates (invocationContext, candidates, this, position); + } + + // LAMESPEC: TODO no spec about priority over normal extension methods yet + if (types_using_table != null) { + foreach (var t in types_using_table) { + + var res = t.MemberCache.FindExtensionMethods (invocationContext, name, arity); + if (res == null) + continue; + + if (candidates == null) + candidates = res; + else + candidates.AddRange (res); + } + + if (candidates != null) + return new ExtensionMethodCandidates (invocationContext, candidates, this, position); + } + + return null; + } + + public override FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + // + // Only simple names (no dots) will be looked up with this function + // + FullNamedExpression resolved; + for (NamespaceContainer container = this; container != null; container = container.Parent) { + resolved = container.Lookup (name, arity, mode, loc); + if (resolved != null || container.MemberName == null) + return resolved; + + var container_ns = container.ns.Parent; + var mn = container.MemberName.Left; + while (mn != null) { + resolved = container_ns.LookupTypeOrNamespace (this, name, arity, mode, loc); + if (resolved != null) + return resolved; + + mn = mn.Left; + container_ns = container_ns.Parent; + } + } + + return null; + } + + public override void GetCompletionStartingWith (string prefix, List results) + { + if (Usings == null) + return; + + foreach (var un in Usings) { + if (un.Alias != null) + continue; + + var name = un.NamespaceExpression.Name; + if (name.StartsWith (prefix)) + results.Add (name); + } + + + IEnumerable all = Enumerable.Empty (); + + foreach (Namespace using_ns in namespace_using_table) { + if (prefix.StartsWith (using_ns.Name)) { + int ld = prefix.LastIndexOf ('.'); + if (ld != -1) { + string rest = prefix.Substring (ld + 1); + + all = all.Concat (using_ns.CompletionGetTypesStartingWith (rest)); + } + } + all = all.Concat (using_ns.CompletionGetTypesStartingWith (prefix)); + } + + results.AddRange (all); + + base.GetCompletionStartingWith (prefix, results); + } + + + // + // Looks-up a alias named @name in this and surrounding namespace declarations + // + public FullNamedExpression LookupExternAlias (string name) + { + if (aliases == null) + return null; + + UsingAliasNamespace uan; + if (aliases.TryGetValue (name, out uan) && uan is UsingExternAlias) + return uan.ResolvedExpression; + + return null; + } + + // + // Looks-up a alias named @name in this and surrounding namespace declarations + // + public override FullNamedExpression LookupNamespaceAlias (string name) + { + for (NamespaceContainer n = this; n != null; n = n.Parent) { + if (n.aliases == null) + continue; + + UsingAliasNamespace uan; + if (n.aliases.TryGetValue (name, out uan)) + return uan.ResolvedExpression; + } + + return null; + } + + FullNamedExpression Lookup (string name, int arity, LookupMode mode, Location loc) + { + // + // Check whether it's in the namespace. + // + FullNamedExpression fne = ns.LookupTypeOrNamespace (this, name, arity, mode, loc); + + // + // Check aliases. + // + if (aliases != null && arity == 0) { + UsingAliasNamespace uan; + if (aliases.TryGetValue (name, out uan)) { + if (fne != null && mode != LookupMode.Probing) { + // TODO: Namespace has broken location + //Report.SymbolRelatedToPreviousError (fne.Location, null); + Compiler.Report.SymbolRelatedToPreviousError (uan.Location, null); + Compiler.Report.Error (576, loc, + "Namespace `{0}' contains a definition with same name as alias `{1}'", + GetSignatureForError (), name); + } + + return uan.ResolvedExpression; + } + } + + if (fne != null) + return fne; + + // + // Lookup can be called before the namespace is defined from different namespace using alias clause + // + if (namespace_using_table == null) { + DoDefineNamespace (); + } + + // + // Check using entries. + // + FullNamedExpression match = null; + foreach (Namespace using_ns in namespace_using_table) { + // + // A using directive imports only types contained in the namespace, it + // does not import any nested namespaces + // + var t = using_ns.LookupType (this, name, arity, mode, loc); + if (t == null) + continue; + + fne = new TypeExpression (t, loc); + if (match == null) { + match = fne; + continue; + } + + // Prefer types over namespaces + var texpr_fne = fne as TypeExpr; + var texpr_match = match as TypeExpr; + if (texpr_fne != null && texpr_match == null) { + match = fne; + continue; + } else if (texpr_fne == null) { + continue; + } + + // It can be top level accessibility only + var better = Namespace.IsImportedTypeOverride (Module, texpr_match.Type, texpr_fne.Type); + if (better == null) { + if (mode == LookupMode.Normal) { + Compiler.Report.SymbolRelatedToPreviousError (texpr_match.Type); + Compiler.Report.SymbolRelatedToPreviousError (texpr_fne.Type); + Compiler.Report.Error (104, loc, "`{0}' is an ambiguous reference between `{1}' and `{2}'", + name, texpr_match.GetSignatureForError (), texpr_fne.GetSignatureForError ()); + } + + return match; + } + + if (better == texpr_fne.Type) + match = texpr_fne; + } + + return match; + } + + public static MethodGroupExpr LookupStaticUsings (IMemberContext mc, string name, int arity, Location loc) + { + for (var m = mc.CurrentMemberDefinition; m != null; m = m.Parent) { + + var nc = m as NamespaceContainer; + if (nc == null) + continue; + + List candidates = null; + if (nc.types_using_table != null) { + foreach (var using_type in nc.types_using_table) { + var members = MemberCache.FindMembers (using_type, name, true); + if (members != null) { + foreach (var member in members) { + if ((member.Modifiers & Modifiers.METHOD_EXTENSION) != 0) + continue; + + if (arity > 0 && member.Arity != arity) + continue; + + if (candidates == null) + candidates = new List (); + + candidates.Add (member); + } + } + } + } + + if (candidates != null) + return new MethodGroupExpr (candidates, null, loc); + } + + return null; + } + + protected override void DefineNamespace () + { + if (namespace_using_table == null) + DoDefineNamespace (); + + base.DefineNamespace (); + } + + void DoDefineNamespace () + { + namespace_using_table = empty_namespaces; + + if (clauses != null) { + List namespaces = null; + List types = null; + + bool post_process_using_aliases = false; + + for (int i = 0; i < clauses.Count; ++i) { + var entry = clauses[i]; + + if (entry.Alias != null) { + if (aliases == null) + aliases = new Dictionary (); + + // + // Aliases are not available when resolving using section + // except extern aliases + // + if (entry is UsingExternAlias) { + entry.Define (this); + if (entry.ResolvedExpression != null) + aliases.Add (entry.Alias.Value, (UsingExternAlias) entry); + + clauses.RemoveAt (i--); + } else { + post_process_using_aliases = true; + } + + continue; + } + + entry.Define (this); + + // + // It's needed for repl only, when using clause cannot be resolved don't hold it in + // global list which is resolved for each evaluation + // + if (entry.ResolvedExpression == null) { + clauses.RemoveAt (i--); + continue; + } + + var using_ns = entry.ResolvedExpression as NamespaceExpression; + if (using_ns == null) { + + var type = ((TypeExpr)entry.ResolvedExpression).Type; + + if (types == null) + types = new List (); + + if (types.Contains (type)) { + Warning_DuplicateEntry (entry); + } else { + types.Add (type); + } + } else { + if (namespaces == null) + namespaces = new List (); + + if (namespaces.Contains (using_ns.Namespace)) { + // Ensure we don't report the warning multiple times in repl + clauses.RemoveAt (i--); + + Warning_DuplicateEntry (entry); + } else { + namespaces.Add (using_ns.Namespace); + } + } + } + + namespace_using_table = namespaces == null ? new Namespace [0] : namespaces.ToArray (); + if (types != null) + types_using_table = types.ToArray (); + + if (post_process_using_aliases) { + for (int i = 0; i < clauses.Count; ++i) { + var entry = clauses[i]; + if (entry.Alias != null) { + entry.Define (this); + if (entry.ResolvedExpression != null) { + aliases.Add (entry.Alias.Value, (UsingAliasNamespace) entry); + } + + clauses.RemoveAt (i--); + } + } + } + } + } + + public void EnableRedefinition () + { + is_defined = false; + namespace_using_table = null; + } + + internal override void GenerateDocComment (DocumentationBuilder builder) + { + if (containers != null) { + foreach (var tc in containers) + tc.GenerateDocComment (builder); + } + } + + public override string GetSignatureForError () + { + return MemberName == null ? "global::" : base.GetSignatureForError (); + } + + public override void RemoveContainer (TypeContainer cont) + { + base.RemoveContainer (cont); + NS.RemoveContainer (cont); + } + + protected override bool VerifyClsCompliance () + { + if (Module.IsClsComplianceRequired ()) { + if (MemberName != null && MemberName.Name[0] == '_') { + Warning_IdentifierNotCompliant (); + } + + ns.VerifyClsCompliance (); + return true; + } + + return false; + } + + void Warning_DuplicateEntry (UsingNamespace entry) + { + Compiler.Report.Warning (105, 3, entry.Location, + "The using directive for `{0}' appeared previously in this namespace", + entry.ResolvedExpression.GetSignatureForError ()); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } + + public class UsingNamespace + { + readonly ATypeNameExpression expr; + readonly Location loc; + protected FullNamedExpression resolved; + + public UsingNamespace (ATypeNameExpression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + #region Properties + + public virtual SimpleMemberName Alias { + get { + return null; + } + } + + public Location Location { + get { + return loc; + } + } + + public ATypeNameExpression NamespaceExpression { + get { + return expr; + } + } + + public FullNamedExpression ResolvedExpression { + get { + return resolved; + } + } + + #endregion + + public string GetSignatureForError () + { + return expr.GetSignatureForError (); + } + + public virtual void Define (NamespaceContainer ctx) + { + resolved = expr.ResolveAsTypeOrNamespace (ctx, false); + var ns = resolved as NamespaceExpression; + if (ns != null) + return; + + if (resolved != null) { + var compiler = ctx.Module.Compiler; + var type = resolved.Type; + if (compiler.Settings.Version >= LanguageVersion.V_6) { + if (!type.IsClass || !type.IsStatic) { + compiler.Report.SymbolRelatedToPreviousError (type); + compiler.Report.Error (7007, Location, + "`{0}' is not a static class. A using namespace directive can only be applied to static classes or namespace", + GetSignatureForError ()); + } + + return; + } + + compiler.Report.SymbolRelatedToPreviousError (type); + compiler.Report.Error (138, Location, + "`{0}' is a type not a namespace. A using namespace directive can only be applied to namespaces", + GetSignatureForError ()); + } + } + + public virtual void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override string ToString() + { + return resolved.ToString(); + } + } + + public class UsingExternAlias : UsingAliasNamespace + { + public UsingExternAlias (SimpleMemberName alias, Location loc) + : base (alias, null, loc) + { + } + + public override void Define (NamespaceContainer ctx) + { + var ns = ctx.Module.GetRootNamespace (Alias.Value); + if (ns == null) { + ctx.Module.Compiler.Report.Error (430, Location, + "The extern alias `{0}' was not specified in -reference option", + Alias.Value); + return; + } + + resolved = new NamespaceExpression (ns, Location); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } + + public class UsingAliasNamespace : UsingNamespace + { + readonly SimpleMemberName alias; + + public struct AliasContext : IMemberContext + { + readonly NamespaceContainer ns; + + public AliasContext (NamespaceContainer ns) + { + this.ns = ns; + } + + public TypeSpec CurrentType { + get { + return null; + } + } + + public TypeParameters CurrentTypeParameters { + get { + return null; + } + } + + public MemberCore CurrentMemberDefinition { + get { + return null; + } + } + + public bool IsObsolete { + get { + return false; + } + } + + public bool IsUnsafe { + get { + throw new NotImplementedException (); + } + } + + public bool IsStatic { + get { + throw new NotImplementedException (); + } + } + + public ModuleContainer Module { + get { + return ns.Module; + } + } + + public string GetSignatureForError () + { + throw new NotImplementedException (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + return null; + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + var fne = ns.NS.LookupTypeOrNamespace (ns, name, arity, mode, loc); + if (fne != null) + return fne; + + // + // Only extern aliases are allowed in this context + // + fne = ns.LookupExternAlias (name); + if (fne != null || ns.MemberName == null) + return fne; + + var container_ns = ns.NS.Parent; + var mn = ns.MemberName.Left; + while (mn != null) { + fne = container_ns.LookupTypeOrNamespace (this, name, arity, mode, loc); + if (fne != null) + return fne; + + mn = mn.Left; + container_ns = container_ns.Parent; + } + + if (ns.Parent != null) + return ns.Parent.LookupNamespaceOrType (name, arity, mode, loc); + + return null; + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + return ns.LookupNamespaceAlias (name); + } + } + + public UsingAliasNamespace (SimpleMemberName alias, ATypeNameExpression expr, Location loc) + : base (expr, loc) + { + this.alias = alias; + } + + public override SimpleMemberName Alias { + get { + return alias; + } + } + + public override void Define (NamespaceContainer ctx) + { + // + // The namespace-or-type-name of a using-alias-directive is resolved as if + // the immediately containing compilation unit or namespace body had no + // using-directives. A using-alias-directive may however be affected + // by extern-alias-directives in the immediately containing compilation + // unit or namespace body + // + // We achieve that by introducing alias-context which redirect any local + // namespace or type resolve calls to parent namespace + // + resolved = NamespaceExpression.ResolveAsTypeOrNamespace (new AliasContext (ctx), false); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/nullable.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/nullable.cs new file mode 100644 index 000000000..e857e3654 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/nullable.cs @@ -0,0 +1,1380 @@ +// +// nullable.cs: Nullable types support +// +// Authors: Martin Baulig (martin@ximian.com) +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using SLE = System.Linq.Expressions; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp.Nullable +{ + public class NullableType : TypeExpr + { + readonly TypeSpec underlying; + + public NullableType (TypeSpec type, Location loc) + { + this.underlying = type; + this.loc = loc; + } + + public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments = false) + { + eclass = ExprClass.Type; + + var otype = ec.Module.PredefinedTypes.Nullable.Resolve (); + if (otype == null) + return null; + + TypeArguments args = new TypeArguments (new TypeExpression (underlying, loc)); + GenericTypeExpr ctype = new GenericTypeExpr (otype, args, loc); + + type = ctype.ResolveAsType (ec); + return type; + } + } + + static class NullableInfo + { + public static MethodSpec GetConstructor (TypeSpec nullableType) + { + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly); + } + + public static MethodSpec GetHasValue (TypeSpec nullableType) + { + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Method ("get_HasValue", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None); + } + + public static MethodSpec GetGetValueOrDefault (TypeSpec nullableType) + { + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Method ("GetValueOrDefault", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None); + } + + // + // Don't use unless really required for correctness, see Unwrap::Emit + // + public static MethodSpec GetValue (TypeSpec nullableType) + { + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None); + } + + public static TypeSpec GetUnderlyingType (TypeSpec nullableType) + { + return ((InflatedTypeSpec) nullableType).TypeArguments[0]; + } + + public static TypeSpec GetEnumUnderlyingType (ModuleContainer module, TypeSpec nullableEnum) + { + return MakeType (module, EnumSpec.GetUnderlyingType (GetUnderlyingType (nullableEnum))); + } + + public static TypeSpec MakeType (ModuleContainer module, TypeSpec underlyingType) + { + return module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (module, + new[] { underlyingType }); + + } + } + + public class Unwrap : Expression, IMemoryLocation + { + Expression expr; + + LocalTemporary temp; + Expression temp_field; + readonly bool useDefaultValue; + + public Unwrap (Expression expr, bool useDefaultValue = true) + { + this.expr = expr; + this.loc = expr.Location; + this.useDefaultValue = useDefaultValue; + + type = NullableInfo.GetUnderlyingType (expr.Type); + eclass = expr.eclass; + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + // TODO: REMOVE + public static Expression Create (Expression expr) + { + // + // Avoid unwraping and wraping of same type + // + Wrap wrap = expr as Wrap; + if (wrap != null) + return wrap.Child; + + return Create (expr, false); + } + + public static Expression CreateUnwrapped (Expression expr) + { + // + // Avoid unwraping and wraping of same type + // + Wrap wrap = expr as Wrap; + if (wrap != null) + return wrap.Child; + + return Create (expr, true); + } + + public static Unwrap Create (Expression expr, bool useDefaultValue) + { + return new Unwrap (expr, useDefaultValue); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + expr = expr.DoResolveLValue (ec, right_side); + return this; + } + + public override void Emit (EmitContext ec) + { + Store (ec); + + var call = new CallEmitter (); + call.InstanceExpression = this; + + // + // Using GetGetValueOrDefault is prefered because JIT can possibly + // inline it whereas Value property contains a throw which is very + // unlikely to be inlined + // + if (useDefaultValue) + call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null); + else + call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null); + } + + public void EmitCheck (EmitContext ec) + { + Store (ec); + + var call = new CallEmitter (); + call.InstanceExpression = this; + + call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null); + } + + public override void EmitSideEffect (EmitContext ec) + { + expr.EmitSideEffect (ec); + } + + public override Expression EmitToField (EmitContext ec) + { + if (temp_field == null) + temp_field = this.expr.EmitToField (ec); + + return this; + } + + public override bool Equals (object obj) + { + Unwrap uw = obj as Unwrap; + return uw != null && expr.Equals (uw.expr); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + public Expression Original { + get { + return expr; + } + } + + public override int GetHashCode () + { + return expr.GetHashCode (); + } + + public override bool IsNull { + get { + return expr.IsNull; + } + } + + public void Store (EmitContext ec) + { + if (temp != null || temp_field != null) + return; + + if (expr is VariableReference) + return; + + expr.Emit (ec); + LocalVariable.Store (ec); + } + + public void Load (EmitContext ec) + { + if (temp_field != null) + temp_field.Emit (ec); + else if (expr is VariableReference) + expr.Emit (ec); + else + LocalVariable.Emit (ec); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return expr.MakeExpression (ctx); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + IMemoryLocation ml; + + if (temp_field != null) { + ml = temp_field as IMemoryLocation; + if (ml == null) { + var lt = new LocalTemporary (temp_field.Type); + temp_field.Emit (ec); + lt.Store (ec); + ml = lt; + } + } else { + ml = expr as VariableReference; + } + + if (ml != null) + ml.AddressOf (ec, mode); + else + LocalVariable.AddressOf (ec, mode); + } + + // + // Keeps result of non-variable expression + // + LocalTemporary LocalVariable { + get { + if (temp == null && temp_field == null) + temp = new LocalTemporary (expr.Type); + return temp; + } + } + } + + // + // Calls get_Value method on nullable expression + // + public class UnwrapCall : CompositeExpression + { + public UnwrapCall (Expression expr) + : base (expr) + { + } + + protected override Expression DoResolve (ResolveContext rc) + { + base.DoResolve (rc); + + if (type != null) + type = NullableInfo.GetUnderlyingType (type); + + return this; + } + + public override void Emit (EmitContext ec) + { + var call = new CallEmitter (); + call.InstanceExpression = Child; + call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null); + } + } + + public class Wrap : TypeCast + { + private Wrap (Expression expr, TypeSpec type) + : base (expr, type) + { + eclass = ExprClass.Value; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + TypeCast child_cast = child as TypeCast; + if (child_cast != null) { + child.Type = type; + return child_cast.CreateExpressionTree (ec); + } + + var user_cast = child as UserCast; + if (user_cast != null) { + child.Type = type; + return user_cast.CreateExpressionTree (ec); + } + + return base.CreateExpressionTree (ec); + } + + public static Expression Create (Expression expr, TypeSpec type) + { + // + // Avoid unwraping and wraping of the same type + // + Unwrap unwrap = expr as Unwrap; + if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type)) + return unwrap.Original; + + return new Wrap (expr, type); + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + } + } + + // + // Represents null literal lifted to nullable type + // + public class LiftedNull : NullConstant, IMemoryLocation + { + private LiftedNull (TypeSpec nullable_type, Location loc) + : base (nullable_type, loc) + { + eclass = ExprClass.Value; + } + + public static Constant Create (TypeSpec nullable, Location loc) + { + return new LiftedNull (nullable, loc); + } + + public static Constant CreateFromExpression (ResolveContext rc, Expression e) + { + if (!rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + rc.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'", + e.Type.GetSignatureForError ()); + } + + return ReducedExpression.Create (Create (e.Type, e.Location), e); + } + + public override void Emit (EmitContext ec) + { + // TODO: generate less temporary variables + LocalTemporary value_target = new LocalTemporary (type); + + value_target.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Initobj, type); + value_target.Emit (ec); + value_target.Release (ec); + } + + public void AddressOf (EmitContext ec, AddressOp Mode) + { + LocalTemporary value_target = new LocalTemporary (type); + + value_target.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Initobj, type); + value_target.AddressOf (ec, Mode); + } + } + + // + // Generic lifting expression, supports all S/S? -> T/T? cases + // + public class LiftedConversion : Expression, IMemoryLocation + { + Expression expr, null_value; + Unwrap unwrap; + + public LiftedConversion (Expression expr, Unwrap unwrap, TypeSpec type) + { + this.expr = expr; + this.unwrap = unwrap; + this.loc = expr.Location; + this.type = type; + } + + public LiftedConversion (Expression expr, Expression unwrap, TypeSpec type) + : this (expr, unwrap as Unwrap, type) + { + } + + public override bool IsNull { + get { + return expr.IsNull; + } + } + + public override bool ContainsEmitWithAwait () + { + return unwrap.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + // + // It's null when lifting non-nullable type + // + if (unwrap == null) { + // S -> T? is wrap only + if (type.IsNullableType) + return Wrap.Create (expr, type); + + // S -> T can be simplified + return expr; + } + + // Wrap target for T? + if (type.IsNullableType) { + if (!expr.Type.IsNullableType) { + expr = Wrap.Create (expr, type); + if (expr == null) + return null; + } + + null_value = LiftedNull.Create (type, loc); + } else if (TypeSpec.IsValueType (type)) { + null_value = LiftedNull.Create (type, loc); + } else { + null_value = new NullConstant (type, loc); + } + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + Label is_null_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, is_null_label); + + expr.Emit (ec); + + ec.Emit (OpCodes.Br, end_label); + ec.MarkLabel (is_null_label); + + null_value.Emit (ec); + + ec.MarkLabel (end_label); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); + } + } + + public class LiftedUnaryOperator : Unary, IMemoryLocation + { + Unwrap unwrap; + Expression user_operator; + + public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc) + : base (op, expr, loc) + { + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (user_operator != null) + return user_operator.CreateExpressionTree (ec); + + if (Oper == Operator.UnaryPlus) + return Expr.CreateExpressionTree (ec); + + return base.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + unwrap = Unwrap.Create (Expr, false); + if (unwrap == null) + return null; + + Expression res = base.ResolveOperator (ec, unwrap); + if (res == null) { + Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type); + return null; + } + + if (res != this) { + if (user_operator == null) + return res; + } else { + res = Expr = LiftExpression (ec, Expr); + } + + if (res == null) + return null; + + eclass = ExprClass.Value; + type = res.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + Label is_null_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, is_null_label); + + if (user_operator != null) { + user_operator.Emit (ec); + } else { + EmitOperator (ec, NullableInfo.GetUnderlyingType (type)); + } + + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + + ec.MarkLabel (end_label); + } + + static Expression LiftExpression (ResolveContext ec, Expression expr) + { + var lifted_type = new NullableType (expr.Type, expr.Location); + if (lifted_type.ResolveAsType (ec) == null) + return null; + + expr.Type = lifted_type.Type; + return expr; + } + + protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined) + { + expr = base.ResolveEnumOperator (ec, expr, predefined); + if (expr == null) + return null; + + Expr = LiftExpression (ec, Expr); + return LiftExpression (ec, expr); + } + + protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr) + { + expr = base.ResolveUserOperator (ec, expr); + if (expr == null) + return null; + + // + // When a user operator is of non-nullable type + // + if (Expr is Unwrap) { + user_operator = LiftExpression (ec, expr); + return user_operator; + } + + return expr; + } + } + + // + // Lifted version of binary operators + // + class LiftedBinaryOperator : Expression + { + public LiftedBinaryOperator (Binary b) + { + this.Binary = b; + this.loc = b.Location; + } + + public Binary Binary { get; private set; } + + public Expression Left { get; set; } + + public Expression Right { get; set; } + + public Unwrap UnwrapLeft { get; set; } + + public Unwrap UnwrapRight { get; set; } + + public MethodSpec UserOperator { get; set; } + + bool IsBitwiseBoolean { + get { + return (Binary.Oper == Binary.Operator.BitwiseAnd || Binary.Oper == Binary.Operator.BitwiseOr) && + ((UnwrapLeft != null && UnwrapLeft.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) || + (UnwrapRight != null && UnwrapRight.Type.BuiltinType == BuiltinTypeSpec.Type.Bool)); + } + } + + public override bool ContainsEmitWithAwait () + { + return Left.ContainsEmitWithAwait () || Right.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext rc) + { + if (UserOperator != null) { + Arguments args = new Arguments (2); + args.Add (new Argument (Binary.Left)); + args.Add (new Argument (Binary.Right)); + + var method = new UserOperatorCall (UserOperator, args, Binary.CreateExpressionTree, loc); + return method.CreateExpressionTree (rc); + } + + return Binary.CreateExpressionTree (rc); + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (rc.IsRuntimeBinder) { + if (UnwrapLeft == null && !Left.Type.IsNullableType) + Left = LiftOperand (rc, Left); + + if (UnwrapRight == null && !Right.Type.IsNullableType) + Right = LiftOperand (rc, Right); + } else { + if (UnwrapLeft == null && Left != null && Left.Type.IsNullableType) { + Left = Unwrap.CreateUnwrapped (Left); + UnwrapLeft = Left as Unwrap; + } + + if (UnwrapRight == null && Right != null && Right.Type.IsNullableType) { + Right = Unwrap.CreateUnwrapped (Right); + UnwrapRight = Right as Unwrap; + } + } + + type = Binary.Type; + eclass = Binary.eclass; + + return this; + } + + Expression LiftOperand (ResolveContext rc, Expression expr) + { + TypeSpec type; + if (expr.IsNull) { + type = Left.IsNull ? Right.Type : Left.Type; + } else { + type = expr.Type; + } + + if (!type.IsNullableType) + type = NullableInfo.MakeType (rc.Module, type); + + return Wrap.Create (expr, type); + } + + public override void Emit (EmitContext ec) + { + if (IsBitwiseBoolean && UserOperator == null) { + EmitBitwiseBoolean (ec); + return; + } + + if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) { + EmitEquality (ec); + return; + } + + Label is_null_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) { + Left = Left.EmitToField (ec); + Right = Right.EmitToField (ec); + } + + if (UnwrapLeft != null) { + UnwrapLeft.EmitCheck (ec); + } + + // + // Don't emit HasValue check when left and right expressions are same + // + if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) { + UnwrapRight.EmitCheck (ec); + if (UnwrapLeft != null) { + ec.Emit (OpCodes.And); + } + } + + ec.Emit (OpCodes.Brfalse, is_null_label); + + if (UserOperator != null) { + var args = new Arguments (2); + args.Add (new Argument (Left)); + args.Add (new Argument (Right)); + + var call = new CallEmitter (); + call.EmitPredefined (ec, UserOperator, args); + } else { + Binary.EmitOperator (ec, Left, Right); + } + + // + // Wrap the result when the operator return type is nullable type + // + if (type.IsNullableType) + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + + ec.Emit (OpCodes.Br_S, end_label); + ec.MarkLabel (is_null_label); + + if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) { + ec.EmitInt (0); + } else { + LiftedNull.Create (type, loc).Emit (ec); + } + + ec.MarkLabel (end_label); + } + + void EmitBitwiseBoolean (EmitContext ec) + { + Label load_left = ec.DefineLabel (); + Label load_right = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + Label is_null_label = ec.DefineLabel (); + + bool or = Binary.Oper == Binary.Operator.BitwiseOr; + + // + // Both operands are bool? types + // + if (UnwrapLeft != null && UnwrapRight != null) { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) { + Left = Left.EmitToField (ec); + Right = Right.EmitToField (ec); + } else { + UnwrapLeft.Store (ec); + UnwrapRight.Store (ec); + } + + Left.Emit (ec); + ec.Emit (OpCodes.Brtrue_S, load_right); + + Right.Emit (ec); + ec.Emit (OpCodes.Brtrue_S, load_left); + + UnwrapLeft.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse_S, load_right); + + // load left + ec.MarkLabel (load_left); + if (or) + UnwrapRight.Load (ec); + else + UnwrapLeft.Load (ec); + + ec.Emit (OpCodes.Br_S, end_label); + + // load right + ec.MarkLabel (load_right); + if (or) + UnwrapLeft.Load (ec); + else + UnwrapRight.Load (ec); + + ec.MarkLabel (end_label); + return; + } + + // + // Faster version when one operand is bool + // + if (UnwrapLeft == null) { + // + // (bool, bool?) + // + // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle + // in binary expression reduction + // + var c = Left as BoolConstant; + if (c != null) { + // Keep evaluation order + UnwrapRight.Store (ec); + + ec.EmitInt (or ? 1 : 0); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + } else if (Left.IsNull) { + UnwrapRight.Emit (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label); + + UnwrapRight.Load (ec); + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + } else { + Left.Emit (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right); + + ec.EmitInt (or ? 1 : 0); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (load_right); + UnwrapRight.Original.Emit (ec); + } + } else { + // + // (bool?, bool) + // + // Keep left-right evaluation order + UnwrapLeft.Store (ec); + + // + // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle + // in binary expression reduction + // + var c = Right as BoolConstant; + if (c != null) { + ec.EmitInt (or ? 1 : 0); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + } else if (Right.IsNull) { + UnwrapLeft.Emit (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label); + + UnwrapLeft.Load (ec); + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + } else { + Right.Emit (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right); + + ec.EmitInt (or ? 1 : 0); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (load_right); + + UnwrapLeft.Load (ec); + } + } + + ec.MarkLabel (end_label); + } + + // + // Emits optimized equality or inequality operator when possible + // + void EmitEquality (EmitContext ec) + { + // + // Either left or right is null + // + if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable + // + // left.HasValue == false + // + UnwrapLeft.EmitCheck (ec); + if (Binary.Oper == Binary.Operator.Equality) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + } + return; + } + + if (UnwrapRight != null && Binary.Left.IsNull) { + // + // right.HasValue == false + // + UnwrapRight.EmitCheck (ec); + if (Binary.Oper == Binary.Operator.Equality) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + } + return; + } + + Label dissimilar_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + if (UserOperator != null) { + var left = Left; + + if (UnwrapLeft != null) { + UnwrapLeft.EmitCheck (ec); + } else { + // Keep evaluation order same + if (!(Left is VariableReference)) { + Left.Emit (ec); + var lt = new LocalTemporary (Left.Type); + lt.Store (ec); + left = lt; + } + } + + if (UnwrapRight != null) { + UnwrapRight.EmitCheck (ec); + + if (UnwrapLeft != null) { + ec.Emit (OpCodes.Bne_Un, dissimilar_label); + + Label compare_label = ec.DefineLabel (); + UnwrapLeft.EmitCheck (ec); + ec.Emit (OpCodes.Brtrue, compare_label); + + if (Binary.Oper == Binary.Operator.Equality) + ec.EmitInt (1); + else + ec.EmitInt (0); + + ec.Emit (OpCodes.Br, end_label); + + ec.MarkLabel (compare_label); + } else { + ec.Emit (OpCodes.Brfalse, dissimilar_label); + } + } else { + ec.Emit (OpCodes.Brfalse, dissimilar_label); + } + + var args = new Arguments (2); + args.Add (new Argument (left)); + args.Add (new Argument (Right)); + + var call = new CallEmitter (); + call.EmitPredefined (ec, UserOperator, args); + } else { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) { + Left = Left.EmitToField (ec); + Right = Right.EmitToField (ec); + } + + // + // Emit underlying value comparison first. + // + // For this code: int? a = 1; bool b = a == 1; + // + // We emit something similar to this. Expressions with side effects have local + // variable created by Unwrap expression + // + // left.GetValueOrDefault () + // right + // bne.un.s dissimilar_label + // left.HasValue + // br.s end_label + // dissimilar_label: + // ldc.i4.0 + // end_label: + // + + Left.Emit (ec); + Right.Emit (ec); + + ec.Emit (OpCodes.Bne_Un_S, dissimilar_label); + + // + // Check both left and right expressions for Unwrap call in which + // case we need to run get_HasValue() check because the type is + // nullable and could have null value + // + if (UnwrapLeft != null) + UnwrapLeft.EmitCheck (ec); + + if (UnwrapRight != null) + UnwrapRight.EmitCheck (ec); + + if (UnwrapLeft != null && UnwrapRight != null) { + if (Binary.Oper == Binary.Operator.Inequality) + ec.Emit (OpCodes.Xor); + else + ec.Emit (OpCodes.Ceq); + } else { + if (Binary.Oper == Binary.Operator.Inequality) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + } + } + } + + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (dissimilar_label); + if (Binary.Oper == Binary.Operator.Inequality) + ec.EmitInt (1); + else + ec.EmitInt (0); + + ec.MarkLabel (end_label); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + Binary.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return Binary.MakeExpression (ctx, Left, Right); + } + } + + public class NullCoalescingOperator : Expression + { + Expression left, right; + Unwrap unwrap; + + public NullCoalescingOperator (Expression left, Expression right) + { + this.left = left; + this.right = right; + this.loc = left.Location; + } + + public Expression LeftExpression { + get { + return left; + } + } + + public Expression RightExpression { + get { + return right; + } + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (left is NullLiteral) + ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side"); + + UserCast uc = left as UserCast; + Expression conversion = null; + if (uc != null) { + left = uc.Source; + + Arguments c_args = new Arguments (2); + c_args.Add (new Argument (uc.CreateExpressionTree (ec))); + c_args.Add (new Argument (left.CreateExpressionTree (ec))); + conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args); + } + + Arguments args = new Arguments (3); + args.Add (new Argument (left.CreateExpressionTree (ec))); + args.Add (new Argument (right.CreateExpressionTree (ec))); + if (conversion != null) + args.Add (new Argument (conversion)); + + return CreateExpressionFactoryCall (ec, "Coalesce", args); + } + + Expression ConvertExpression (ResolveContext ec) + { + // TODO: ImplicitConversionExists should take care of this + if (left.eclass == ExprClass.MethodGroup) + return null; + + TypeSpec ltype = left.Type; + + // + // If left is a nullable type and an implicit conversion exists from right to underlying type of left, + // the result is underlying type of left + // + if (ltype.IsNullableType) { + unwrap = Unwrap.Create (left, false); + if (unwrap == null) + return null; + + // + // Reduce (left ?? null) to left + // + if (right.IsNull) + return ReducedExpression.Create (left, this); + + Expression conv; + if (right.Type.IsNullableType) { + conv = right.Type == ltype ? right : Convert.ImplicitNulableConversion (ec, right, ltype); + if (conv != null) { + right = conv; + type = ltype; + return this; + } + } else { + conv = Convert.ImplicitConversion (ec, right, unwrap.Type, loc); + if (conv != null) { + left = unwrap; + ltype = left.Type; + + // + // If right is a dynamic expression, the result type is dynamic + // + if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + type = right.Type; + + // Need to box underlying value type + left = Convert.ImplicitBoxingConversion (left, ltype, type); + return this; + } + + right = conv; + type = ltype; + return this; + } + } + } else if (TypeSpec.IsReferenceType (ltype)) { + if (Convert.ImplicitConversionExists (ec, right, ltype)) { + // + // If right is a dynamic expression, the result type is dynamic + // + if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + type = right.Type; + return this; + } + + // + // Reduce ("foo" ?? expr) to expression + // + Constant lc = left as Constant; + if (lc != null && !lc.IsDefaultValue) + return ReducedExpression.Create (lc, this, false); + + // + // Reduce (left ?? null) to left OR (null-constant ?? right) to right + // + if (right.IsNull || lc != null) { + // + // Special case null ?? null + // + if (right.IsNull && ltype == right.Type) + return null; + + return ReducedExpression.Create (lc != null ? right : left, this, false); + } + + right = Convert.ImplicitConversion (ec, right, ltype, loc); + type = ltype; + return this; + } + } else { + return null; + } + + TypeSpec rtype = right.Type; + if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup) + return null; + + // + // Reduce (null ?? right) to right + // + if (left.IsNull) + return ReducedExpression.Create (right, this, false).Resolve (ec); + + left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc); + + if (TypeSpec.IsValueType (left.Type) && !left.Type.IsNullableType) { + Warning_UnreachableExpression (ec, right.Location); + return ReducedExpression.Create (left, this, false).Resolve (ec); + } + + type = rtype; + return this; + } + + public override bool ContainsEmitWithAwait () + { + if (unwrap != null) + return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait (); + + return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + left = left.Resolve (ec); + right = right.Resolve (ec); + + if (left == null || right == null) + return null; + + eclass = ExprClass.Value; + + Expression e = ConvertExpression (ec); + if (e == null) { + Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc); + return null; + } + + return e; + } + + public override void Emit (EmitContext ec) + { + Label end_label = ec.DefineLabel (); + + if (unwrap != null) { + Label is_null_label = ec.DefineLabel (); + + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, is_null_label); + + // + // When both expressions are nullable the unwrap + // is needed only for null check not for value uwrap + // + if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type)) + unwrap.Load (ec); + else + left.Emit (ec); + + ec.Emit (OpCodes.Br, end_label); + + ec.MarkLabel (is_null_label); + right.Emit (ec); + + ec.MarkLabel (end_label); + return; + } + + left.Emit (ec); + ec.Emit (OpCodes.Dup); + + // Only to make verifier happy + if (left.Type.IsGenericParameter) + ec.Emit (OpCodes.Box, left.Type); + + ec.Emit (OpCodes.Brtrue, end_label); + + ec.Emit (OpCodes.Pop); + right.Emit (ec); + + ec.MarkLabel (end_label); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + left.FlowAnalysis (fc); + var left_da = fc.BranchDefiniteAssignment (); + right.FlowAnalysis (fc); + fc.DefiniteAssignment = left_da; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + NullCoalescingOperator target = (NullCoalescingOperator) t; + + target.left = left.Clone (clonectx); + target.right = right.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + class LiftedUnaryMutator : UnaryMutator + { + public LiftedUnaryMutator (Mode mode, Expression expr, Location loc) + : base (mode, expr, loc) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + var orig_expr = expr; + + expr = Unwrap.Create (expr); + + var res = base.DoResolveOperation (ec); + + expr = orig_expr; + type = expr.Type; + + return res; + } + + protected override void EmitOperation (EmitContext ec) + { + Label is_null_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + LocalTemporary lt = new LocalTemporary (type); + + // Value is on the stack + lt.Store (ec); + + var call = new CallEmitter (); + call.InstanceExpression = lt; + call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null); + + ec.Emit (OpCodes.Brfalse, is_null_label); + + call = new CallEmitter (); + call.InstanceExpression = lt; + call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null); + + lt.Release (ec); + + base.EmitOperation (ec); + + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + + ec.MarkLabel (end_label); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/outline.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/outline.cs new file mode 100644 index 000000000..1104b1c11 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/outline.cs @@ -0,0 +1,1038 @@ +// +// outline -- support for rendering in monop +// Some code stolen from updater.cs in monodoc. +// +// Authors: +// Ben Maurer (bmaurer@users.sourceforge.net) +// +// (C) 2004 Ben Maurer +// + +// +// 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.Reflection; +using System.Collections; +using System.CodeDom.Compiler; +using System.IO; +using System.Text; + +namespace Mono.CSharp { +public class Outline { + + bool declared_only; + bool show_private; + bool filter_obsolete; + + IndentedTextWriter o; + Type t; + + public Outline (Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete) + { + this.t = t; + this.o = new IndentedTextWriter (output, "\t"); + this.declared_only = declared_only; + this.show_private = show_private; + this.filter_obsolete = filter_obsolete; + } + + public void OutlineType () + { + bool first; + + OutlineAttributes (); + o.Write (GetTypeVisibility (t)); + + if (t.IsClass && !t.IsSubclassOf (typeof (System.MulticastDelegate))) { + if (t.IsSealed) + o.Write (t.IsAbstract ? " static" : " sealed"); + else if (t.IsAbstract) + o.Write (" abstract"); + } + + o.Write (" "); + o.Write (GetTypeKind (t)); + o.Write (" "); + + Type [] interfaces = (Type []) Comparer.Sort (TypeGetInterfaces (t, declared_only)); + Type parent = t.BaseType; + + if (t.IsSubclassOf (typeof (System.MulticastDelegate))) { + MethodInfo method; + + method = t.GetMethod ("Invoke"); + + o.Write (FormatType (method.ReturnType)); + o.Write (" "); + o.Write (GetTypeName (t)); + o.Write (" ("); + OutlineParams (method.GetParameters ()); + o.Write (")"); + +#if NET_2_0 + WriteGenericConstraints (t.GetGenericArguments ()); +#endif + + o.WriteLine (";"); + return; + } + + o.Write (GetTypeName (t)); + if (((parent != null && parent != typeof (object) && parent != typeof (ValueType)) || interfaces.Length != 0) && ! t.IsEnum) { + first = true; + o.Write (" : "); + + if (parent != null && parent != typeof (object) && parent != typeof (ValueType)) { + o.Write (FormatType (parent)); + first = false; + } + + foreach (Type intf in interfaces) { + if (!first) o.Write (", "); + first = false; + + o.Write (FormatType (intf)); + } + } + + if (t.IsEnum) { + Type underlyingType = System.Enum.GetUnderlyingType (t); + if (underlyingType != typeof (int)) + o.Write (" : {0}", FormatType (underlyingType)); + } +#if NET_2_0 + WriteGenericConstraints (t.GetGenericArguments ()); +#endif + o.WriteLine (" {"); + o.Indent++; + + if (t.IsEnum) { + bool is_first = true; + foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Static)) { + + if (! is_first) + o.WriteLine (","); + is_first = false; + o.Write (fi.Name); + } + o.WriteLine (); + o.Indent--; o.WriteLine ("}"); + return; + } + + first = true; + + foreach (ConstructorInfo ci in t.GetConstructors (DefaultFlags)) { + if (! ShowMember (ci)) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (ci); + OutlineConstructor (ci); + + o.WriteLine (); + } + + + first = true; + + foreach (MethodInfo m in Comparer.Sort (t.GetMethods (DefaultFlags))) { + + if (! ShowMember (m)) + continue; + + if ((m.Attributes & MethodAttributes.SpecialName) != 0) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (m); + OutlineMethod (m); + + o.WriteLine (); + } + + first = true; + + foreach (MethodInfo m in t.GetMethods (DefaultFlags)) { + + if (! ShowMember (m)) + continue; + + if ((m.Attributes & MethodAttributes.SpecialName) == 0) + continue; + if (!(m.Name.StartsWith ("op_"))) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (m); + OutlineOperator (m); + + o.WriteLine (); + } + + first = true; + + foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (DefaultFlags))) { + + if (! ((pi.CanRead && ShowMember (pi.GetGetMethod (true))) || + (pi.CanWrite && ShowMember (pi.GetSetMethod (true))))) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (pi); + OutlineProperty (pi); + + o.WriteLine (); + } + + first = true; + + foreach (FieldInfo fi in t.GetFields (DefaultFlags)) { + + if (! ShowMember (fi)) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (fi); + OutlineField (fi); + + o.WriteLine (); + } + + first = true; + + foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) { + + if (! ShowMember (ei.GetAddMethod (true))) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (ei); + OutlineEvent (ei); + + o.WriteLine (); + } + + first = true; + + foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) { + + if (! ShowMember (ntype)) + continue; + + if (first) + o.WriteLine (); + first = false; + + new Outline (ntype, o, declared_only, show_private, filter_obsolete).OutlineType (); + } + + o.Indent--; o.WriteLine ("}"); + } + + BindingFlags DefaultFlags { + get { + BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + + if (declared_only) + f |= BindingFlags.DeclaredOnly; + + return f; + } + } + + // FIXME: add other interesting attributes? + void OutlineAttributes () + { + if (t.IsSerializable) + o.WriteLine ("[Serializable]"); + + if (t.IsDefined (typeof (System.FlagsAttribute), true)) + o.WriteLine ("[Flags]"); + + if (t.IsDefined (typeof (System.ObsoleteAttribute), true)) + o.WriteLine ("[Obsolete]"); + } + + void OutlineMemberAttribute (MemberInfo mi) + { + if (!mi.IsDefined (typeof (System.ObsoleteAttribute), false)) + return; + var oa = mi.GetCustomAttributes (typeof (System.ObsoleteAttribute), false) [0] as ObsoleteAttribute; + var msg = oa.Message; + o.WriteLine ("[Obsolete{0}]", msg == null || msg == "" ? "" : string.Format ("(\"{0}\")", msg)); + } + + void OutlineEvent (EventInfo ei) + { + MethodBase accessor = ei.GetAddMethod (true); + + o.Write (GetMethodVisibility (accessor)); + o.Write ("event "); + o.Write (FormatType (ei.EventHandlerType)); + o.Write (" "); + o.Write (ei.Name); + o.Write (";"); + } + + void OutlineConstructor (ConstructorInfo ci) + { + o.Write (GetMethodVisibility (ci)); + o.Write (RemoveGenericArity (t.Name)); + o.Write (" ("); + OutlineParams (ci.GetParameters ()); + o.Write (");"); + } + + + void OutlineProperty (PropertyInfo pi) + { + ParameterInfo [] idxp = pi.GetIndexParameters (); + MethodBase g = pi.GetGetMethod (true); + MethodBase s = pi.GetSetMethod (true); + MethodBase accessor = g != null ? g : s; + + if (pi.CanRead && pi.CanWrite) { + + + // Get the more accessible accessor + if ((g.Attributes & MethodAttributes.MemberAccessMask) != + (s.Attributes & MethodAttributes.MemberAccessMask)) { + + if (g.IsPublic) accessor = g; + else if (s.IsPublic) accessor = s; + else if (g.IsFamilyOrAssembly) accessor = g; + else if (s.IsFamilyOrAssembly) accessor = s; + else if (g.IsAssembly || g.IsFamily) accessor = g; + else if (s.IsAssembly || s.IsFamily) accessor = s; + } + } + + o.Write (GetMethodVisibility (accessor)); + o.Write (GetMethodModifiers (accessor)); + o.Write (FormatType (pi.PropertyType)); + o.Write (" "); + + if (idxp.Length == 0) + o.Write (pi.Name); + else { + o.Write ("this ["); + OutlineParams (idxp); + o.Write ("]"); + } + + o.WriteLine (" {"); + o.Indent ++; + + if (g != null && ShowMember (g)) { + if ((g.Attributes & MethodAttributes.MemberAccessMask) != + (accessor.Attributes & MethodAttributes.MemberAccessMask)) + o.Write (GetMethodVisibility (g)); + o.WriteLine ("get;"); + } + + if (s != null && ShowMember (s)) { + if ((s.Attributes & MethodAttributes.MemberAccessMask) != + (accessor.Attributes & MethodAttributes.MemberAccessMask)) + o.Write (GetMethodVisibility (s)); + o.WriteLine ("set;"); + } + + o.Indent --; + o.Write ("}"); + } + + void OutlineMethod (MethodInfo mi) + { + if (MethodIsExplicitIfaceImpl (mi)) { + o.Write (FormatType (mi.ReturnType)); + o.Write (" "); + // MSFT has no way to get the method that we are overriding + // from the interface. this would allow us to pretty print + // the type name (and be more correct if there compiler + // were to do some strange naming thing). + } else { + o.Write (GetMethodVisibility (mi)); + o.Write (GetMethodModifiers (mi)); + o.Write (FormatType (mi.ReturnType)); + o.Write (" "); + } + + o.Write (mi.Name); +#if NET_2_0 + o.Write (FormatGenericParams (mi.GetGenericArguments ())); +#endif + o.Write (" ("); + OutlineParams (mi.GetParameters ()); + o.Write (")"); +#if NET_2_0 + WriteGenericConstraints (mi.GetGenericArguments ()); +#endif + o.Write (";"); + } + + void OutlineOperator (MethodInfo mi) + { + o.Write (GetMethodVisibility (mi)); + o.Write (GetMethodModifiers (mi)); + if (mi.Name == "op_Explicit" || mi.Name == "op_Implicit") { + o.Write (mi.Name.Substring (3).ToLower ()); + o.Write (" operator "); + o.Write (FormatType (mi.ReturnType)); + } else { + o.Write (FormatType (mi.ReturnType)); + o.Write (" operator "); + o.Write (OperatorFromName (mi.Name)); + } + o.Write (" ("); + OutlineParams (mi.GetParameters ()); + o.Write (");"); + } + + void OutlineParams (ParameterInfo [] pi) + { + int i = 0; + foreach (ParameterInfo p in pi) { + if (p.ParameterType.IsByRef) { + o.Write (p.IsOut ? "out " : "ref "); + o.Write (FormatType (p.ParameterType.GetElementType ())); + } else if (p.IsDefined (typeof (ParamArrayAttribute), false)) { + o.Write ("params "); + o.Write (FormatType (p.ParameterType)); + } else { + o.Write (FormatType (p.ParameterType)); + } + + o.Write (" "); + o.Write (p.Name); + if (i + 1 < pi.Length) + o.Write (", "); + i++; + } + } + + void OutlineField (FieldInfo fi) + { + if (fi.IsPublic) o.Write ("public "); + if (fi.IsFamily) o.Write ("protected "); + if (fi.IsPrivate) o.Write ("private "); + if (fi.IsAssembly) o.Write ("internal "); + if (fi.IsLiteral) o.Write ("const "); + else if (fi.IsStatic) o.Write ("static "); + if (fi.IsInitOnly) o.Write ("readonly "); + + o.Write (FormatType (fi.FieldType)); + o.Write (" "); + o.Write (fi.Name); + if (fi.IsLiteral) { + object v = fi.GetValue (this); + + // TODO: Escape values here + o.Write (" = "); + if (v is char) + o.Write ("'{0}'", v); + else if (v is string) + o.Write ("\"{0}\"", v); + else + o.Write (fi.GetValue (this)); + } + o.Write (";"); + } + + static string GetMethodVisibility (MethodBase m) + { + // itnerfaces have no modifiers here + if (m.DeclaringType.IsInterface) + return ""; + + if (m.IsPublic) return "public "; + if (m.IsFamily) return "protected "; + if (m.IsPrivate) return "private "; + if (m.IsAssembly) return "internal "; + + return null; + } + + static string GetMethodModifiers (MethodBase method) + { + if (method.IsStatic) + return "static "; + + if (method.IsFinal) { + // This will happen if you have + // class X : IA { + // public void A () {} + // static void Main () {} + // } + // interface IA { + // void A (); + // } + // + // A needs to be virtual (the CLR requires + // methods implementing an iface be virtual), + // but can not be inherited. It also can not + // be inherited. In C# this is represented + // with no special modifiers + + if (method.IsVirtual) + return null; + return "sealed "; + } + + // all interface methods are "virtual" but we don't say that in c# + if (method.IsVirtual && !method.DeclaringType.IsInterface) { + if (method.IsAbstract) + return "abstract "; + + return ((method.Attributes & MethodAttributes.NewSlot) != 0) ? + "virtual " : + "override "; + } + + return null; + } + + static string GetTypeKind (Type t) + { + if (t.IsEnum) + return "enum"; + if (t.IsClass) { + if (t.IsSubclassOf (typeof (System.MulticastDelegate))) + return "delegate"; + else + return "class"; + } + if (t.IsInterface) + return "interface"; + if (t.IsValueType) + return "struct"; + return "class"; + } + + static string GetTypeVisibility (Type t) + { + switch (t.Attributes & TypeAttributes.VisibilityMask){ + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + return "public"; + + case TypeAttributes.NestedFamily: + case TypeAttributes.NestedFamANDAssem: + case TypeAttributes.NestedFamORAssem: + return "protected"; + + default: + return "internal"; + } + } + +#if NET_2_0 + string FormatGenericParams (Type [] args) + { + StringBuilder sb = new StringBuilder (); + if (args.Length == 0) + return ""; + + sb.Append ("<"); + for (int i = 0; i < args.Length; i++) { + if (i > 0) + sb.Append (","); + sb.Append (FormatType (args [i])); + } + sb.Append (">"); + return sb.ToString (); + } +#endif + + // TODO: fine tune this so that our output is less verbose. We need to figure + // out a way to do this while not making things confusing. + string FormatType (Type t) + { + if (t == null) + return ""; + + string type = GetFullName (t); + if (type == null) + return t.ToString (); + + if (!type.StartsWith ("System.")) { + if (t.Namespace == this.t.Namespace) + return t.Name; + return type; + } + + if (t.HasElementType) { + Type et = t.GetElementType (); + if (t.IsArray) + return FormatType (et) + " []"; + if (t.IsPointer) + return FormatType (et) + " *"; + if (t.IsByRef) + return "ref " + FormatType (et); + } + + switch (type) { + case "System.Byte": return "byte"; + case "System.SByte": return "sbyte"; + case "System.Int16": return "short"; + case "System.Int32": return "int"; + case "System.Int64": return "long"; + + case "System.UInt16": return "ushort"; + case "System.UInt32": return "uint"; + case "System.UInt64": return "ulong"; + + case "System.Single": return "float"; + case "System.Double": return "double"; + case "System.Decimal": return "decimal"; + case "System.Boolean": return "bool"; + case "System.Char": return "char"; + case "System.String": return "string"; + + case "System.Object": return "object"; + case "System.Void": return "void"; + } + + if (type.LastIndexOf(".") == 6) + return type.Substring(7); + + // + // If the namespace of the type is the namespace of what + // we are printing (or is a member of one if its children + // don't print it. This basically means that in C# we would + // automatically get the namespace imported by virtue of the + // namespace {} block. + // + if (this.t.Namespace.StartsWith (t.Namespace + ".") || t.Namespace == this.t.Namespace) + return type.Substring (t.Namespace.Length + 1); + + return type; + } + + public static string RemoveGenericArity (string name) + { + int start = 0; + StringBuilder sb = new StringBuilder (); + while (start < name.Length) { + int pos = name.IndexOf ('`', start); + if (pos < 0) { + sb.Append (name.Substring (start)); + break; + } + sb.Append (name.Substring (start, pos-start)); + + pos++; + + while ((pos < name.Length) && Char.IsNumber (name [pos])) + pos++; + + start = pos; + } + + return sb.ToString (); + } + + string GetTypeName (Type t) + { + StringBuilder sb = new StringBuilder (); + GetTypeName (sb, t); + return sb.ToString (); + } + + void GetTypeName (StringBuilder sb, Type t) + { + sb.Append (RemoveGenericArity (t.Name)); +#if NET_2_0 + sb.Append (FormatGenericParams (t.GetGenericArguments ())); +#endif + } + + string GetFullName (Type t) + { + StringBuilder sb = new StringBuilder (); + GetFullName_recursed (sb, t, false); + return sb.ToString (); + } + + void GetFullName_recursed (StringBuilder sb, Type t, bool recursed) + { +#if NET_2_0 + if (t.IsGenericParameter) { + sb.Append (t.Name); + return; + } +#endif + + if (t.DeclaringType != null) { + GetFullName_recursed (sb, t.DeclaringType, true); + sb.Append ("."); + } + + if (!recursed) { + string ns = t.Namespace; + if ((ns != null) && (ns != "")) { + sb.Append (ns); + sb.Append ("."); + } + } + + GetTypeName (sb, t); + } + +#if NET_2_0 + void WriteGenericConstraints (Type [] args) + { + + foreach (Type t in args) { + bool first = true; + Type[] ifaces = TypeGetInterfaces (t, true); + + GenericParameterAttributes attrs = t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask; + GenericParameterAttributes [] interesting = { + GenericParameterAttributes.ReferenceTypeConstraint, + GenericParameterAttributes.NotNullableValueTypeConstraint, + GenericParameterAttributes.DefaultConstructorConstraint + }; + + if (t.BaseType != typeof (object) || ifaces.Length != 0 || attrs != 0) { + o.Write (" where "); + o.Write (FormatType (t)); + o.Write (" : "); + } + + if (t.BaseType != typeof (object)) { + o.Write (FormatType (t.BaseType)); + first = false; + } + + foreach (Type iface in ifaces) { + if (!first) + o.Write (", "); + first = false; + + o.Write (FormatType (iface)); + } + + foreach (GenericParameterAttributes a in interesting) { + if ((attrs & a) == 0) + continue; + + if (!first) + o.Write (", "); + first = false; + + switch (a) { + case GenericParameterAttributes.ReferenceTypeConstraint: + o.Write ("class"); + break; + case GenericParameterAttributes.NotNullableValueTypeConstraint: + o.Write ("struct"); + break; + case GenericParameterAttributes.DefaultConstructorConstraint: + o.Write ("new ()"); + break; + } + } + } + } +#endif + + string OperatorFromName (string name) + { + switch (name) { + case "op_UnaryPlus": return "+"; + case "op_UnaryNegation": return "-"; + case "op_LogicalNot": return "!"; + case "op_OnesComplement": return "~"; + case "op_Increment": return "++"; + case "op_Decrement": return "--"; + case "op_True": return "true"; + case "op_False": return "false"; + case "op_Addition": return "+"; + case "op_Subtraction": return "-"; + case "op_Multiply": return "*"; + case "op_Division": return "/"; + case "op_Modulus": return "%"; + case "op_BitwiseAnd": return "&"; + case "op_BitwiseOr": return "|"; + case "op_ExclusiveOr": return "^"; + case "op_LeftShift": return "<<"; + case "op_RightShift": return ">>"; + case "op_Equality": return "=="; + case "op_Inequality": return "!="; + case "op_GreaterThan": return ">"; + case "op_LessThan": return "<"; + case "op_GreaterThanOrEqual": return ">="; + case "op_LessThanOrEqual": return "<="; + default: return name; + } + } + + bool MethodIsExplicitIfaceImpl (MethodBase mb) + { + if (!(mb.IsFinal && mb.IsVirtual && mb.IsPrivate)) + return false; + + // UGH msft has no way to get the info about what method is + // getting overriden. Another reason to use cecil :-) + // + //MethodInfo mi = mb as MethodInfo; + //if (mi == null) + // return false; + // + //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType); + //return mi.GetBaseDefinition ().DeclaringType.IsInterface; + + // So, we guess that virtual final private methods only come + // from ifaces :-) + return true; + } + + bool ShowMember (MemberInfo mi) + { + if (mi.MemberType == MemberTypes.Constructor && ((MethodBase) mi).IsStatic) + return false; + + if (show_private) + return true; + + if (filter_obsolete && mi.IsDefined (typeof (ObsoleteAttribute), false)) + return false; + + switch (mi.MemberType) { + case MemberTypes.Constructor: + case MemberTypes.Method: + MethodBase mb = mi as MethodBase; + + if (mb.IsFamily || mb.IsPublic || mb.IsFamilyOrAssembly) + return true; + + if (MethodIsExplicitIfaceImpl (mb)) + return true; + + return false; + + + case MemberTypes.Field: + FieldInfo fi = mi as FieldInfo; + + if (fi.IsFamily || fi.IsPublic || fi.IsFamilyOrAssembly) + return true; + + return false; + + + case MemberTypes.NestedType: + case MemberTypes.TypeInfo: + Type t = mi as Type; + + switch (t.Attributes & TypeAttributes.VisibilityMask){ + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + case TypeAttributes.NestedFamily: + case TypeAttributes.NestedFamORAssem: + return true; + } + + return false; + } + + // What am I !!! + return true; + } + + static Type [] TypeGetInterfaces (Type t, bool declonly) + { + if (t.IsGenericParameter) + return new Type [0]; + + Type [] ifaces = t.GetInterfaces (); + if (! declonly) + return ifaces; + + // Handle Object. Also, optimize for no interfaces + if (t.BaseType == null || ifaces.Length == 0) + return ifaces; + + ArrayList ar = new ArrayList (); + + foreach (Type i in ifaces) + if (! i.IsAssignableFrom (t.BaseType)) + ar.Add (i); + + return (Type []) ar.ToArray (typeof (Type)); + } +} + +public class Comparer : IComparer { + delegate int ComparerFunc (object a, object b); + + ComparerFunc cmp; + + Comparer (ComparerFunc f) + { + this.cmp = f; + } + + public int Compare (object a, object b) + { + return cmp (a, b); + } + + static int CompareType (object a, object b) + { + Type type1 = (Type) a; + Type type2 = (Type) b; + + if (type1.IsSubclassOf (typeof (System.MulticastDelegate)) != type2.IsSubclassOf (typeof (System.MulticastDelegate))) + return (type1.IsSubclassOf (typeof (System.MulticastDelegate)))? -1:1; + return string.Compare (type1.Name, type2.Name); + + } + +// static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType)); + +// static Type [] Sort (Type [] types) +// { +// Array.Sort (types, TypeComparer); +// return types; +// } + + static int CompareMemberInfo (object a, object b) + { + return string.Compare (((MemberInfo) a).Name, ((MemberInfo) b).Name); + } + + static Comparer MemberInfoComparer = new Comparer (new ComparerFunc (CompareMemberInfo)); + + public static MemberInfo [] Sort (MemberInfo [] inf) + { + Array.Sort (inf, MemberInfoComparer); + return inf; + } + + static int CompareMethodBase (object a, object b) + { + MethodBase aa = (MethodBase) a, bb = (MethodBase) b; + + if (aa.IsStatic == bb.IsStatic) { + int c = CompareMemberInfo (a, b); + if (c != 0) + return c; + ParameterInfo [] ap, bp; + + // + // Sort overloads by the names of their types + // put methods with fewer params first. + // + + ap = aa.GetParameters (); + bp = bb.GetParameters (); + int n = System.Math.Min (ap.Length, bp.Length); + + for (int i = 0; i < n; i ++) + if ((c = CompareType (ap [i].ParameterType, bp [i].ParameterType)) != 0) + return c; + + return ap.Length.CompareTo (bp.Length); + } + if (aa.IsStatic) + return -1; + + return 1; + } + + static Comparer MethodBaseComparer = new Comparer (new ComparerFunc (CompareMethodBase)); + + public static MethodBase [] Sort (MethodBase [] inf) + { + Array.Sort (inf, MethodBaseComparer); + return inf; + } + + static int ComparePropertyInfo (object a, object b) + { + PropertyInfo aa = (PropertyInfo) a, bb = (PropertyInfo) b; + + bool astatic = (aa.CanRead ? aa.GetGetMethod (true) : aa.GetSetMethod (true)).IsStatic; + bool bstatic = (bb.CanRead ? bb.GetGetMethod (true) : bb.GetSetMethod (true)).IsStatic; + + if (astatic == bstatic) + return CompareMemberInfo (a, b); + + if (astatic) + return -1; + + return 1; + } + + static Comparer PropertyInfoComparer = new Comparer (new ComparerFunc (ComparePropertyInfo)); + + public static PropertyInfo [] Sort (PropertyInfo [] inf) + { + Array.Sort (inf, PropertyInfoComparer); + return inf; + } + + static int CompareEventInfo (object a, object b) + { + EventInfo aa = (EventInfo) a, bb = (EventInfo) b; + + bool astatic = aa.GetAddMethod (true).IsStatic; + bool bstatic = bb.GetAddMethod (true).IsStatic; + + if (astatic == bstatic) + return CompareMemberInfo (a, b); + + if (astatic) + return -1; + + return 1; + } + + static Comparer EventInfoComparer = new Comparer (new ComparerFunc (CompareEventInfo)); + + public static EventInfo [] Sort (EventInfo [] inf) + { + Array.Sort (inf, EventInfoComparer); + return inf; + } +} +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/parameter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/parameter.cs new file mode 100644 index 000000000..1a30ae384 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/parameter.cs @@ -0,0 +1,1472 @@ +// +// parameter.cs: Parameter definition. +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc +// +// +using System; +using System.Text; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// Abstract Base class for parameters of a method. + /// + public abstract class ParameterBase : Attributable + { + protected ParameterBuilder builder; + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { +#if false + if (a.Type == pa.MarshalAs) { + UnmanagedMarshal marshal = a.GetMarshal (this); + if (marshal != null) { + builder.SetMarshal (marshal); + } + return; + } +#endif + if (a.HasSecurityAttribute) { + a.Error_InvalidSecurityParent (); + return; + } + + if (a.Type == pa.Dynamic) { + a.Error_MisusedDynamicAttribute (); + return; + } + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public ParameterBuilder Builder { + get { + return builder; + } + } + + public override bool IsClsComplianceRequired() + { + return false; + } + } + + /// + /// Class for applying custom attributes on the return type + /// + public class ReturnParameter : ParameterBase + { + MemberCore method; + + // TODO: merge method and mb + public ReturnParameter (MemberCore method, MethodBuilder mb, Location location) + { + this.method = method; + try { + builder = mb.DefineParameter (0, ParameterAttributes.None, ""); + } + catch (ArgumentOutOfRangeException) { + method.Compiler.Report.RuntimeMissingSupport (location, "custom attributes on the return type"); + } + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.CLSCompliant) { + method.Compiler.Report.Warning (3023, 1, a.Location, + "CLSCompliant attribute has no meaning when applied to return types. Try putting it on the method instead"); + } + + // This occurs after Warning -28 + if (builder == null) + return; + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.ReturnValue; + } + } + + /// + /// Is never called + /// + public override string[] ValidAttributeTargets { + get { + return null; + } + } + } + + public class ImplicitLambdaParameter : Parameter + { + public ImplicitLambdaParameter (string name, Location loc) + : base (null, name, Modifier.NONE, null, loc) + { + } + + public override TypeSpec Resolve (IMemberContext ec, int index) + { + if (parameter_type == null) + throw new InternalErrorException ("A type of implicit lambda parameter `{0}' is not set", + Name); + + base.idx = index; + return parameter_type; + } + + public void SetParameterType (TypeSpec type) + { + parameter_type = type; + } + } + + public class ParamsParameter : Parameter { + public ParamsParameter (FullNamedExpression type, string name, Attributes attrs, Location loc): + base (type, name, Parameter.Modifier.PARAMS, attrs, loc) + { + } + + public override TypeSpec Resolve (IMemberContext ec, int index) + { + if (base.Resolve (ec, index) == null) + return null; + + var ac = parameter_type as ArrayContainer; + if (ac == null || ac.Rank != 1) { + ec.Module.Compiler.Report.Error (225, Location, "The params parameter must be a single dimensional array"); + return null; + } + + return parameter_type; + } + + public override void ApplyAttributes (MethodBuilder mb, ConstructorBuilder cb, int index, PredefinedAttributes pa) + { + base.ApplyAttributes (mb, cb, index, pa); + pa.ParamArray.EmitAttribute (builder); + } + } + + public class ArglistParameter : Parameter { + // Doesn't have proper type because it's never chosen for better conversion + public ArglistParameter (Location loc) : + base (null, String.Empty, Parameter.Modifier.NONE, null, loc) + { + parameter_type = InternalType.Arglist; + } + + public override void ApplyAttributes (MethodBuilder mb, ConstructorBuilder cb, int index, PredefinedAttributes pa) + { + // Nothing to do + } + + public override bool CheckAccessibility (InterfaceMemberBase member) + { + return true; + } + + public override TypeSpec Resolve (IMemberContext ec, int index) + { + return parameter_type; + } + } + + public interface IParameterData + { + Expression DefaultValue { get; } + bool HasExtensionMethodModifier { get; } + bool HasDefaultValue { get; } + Parameter.Modifier ModFlags { get; } + string Name { get; } + } + + // + // Parameter information created by parser + // + public class Parameter : ParameterBase, IParameterData, ILocalVariable // TODO: INamedBlockVariable + { + [Flags] + public enum Modifier : byte { + NONE = 0, + PARAMS = 1 << 0, + REF = 1 << 1, + OUT = 1 << 2, + This = 1 << 3, + CallerMemberName = 1 << 4, + CallerLineNumber = 1 << 5, + CallerFilePath = 1 << 6, + + RefOutMask = REF | OUT, + ModifierMask = PARAMS | REF | OUT | This, + CallerMask = CallerMemberName | CallerLineNumber | CallerFilePath + } + + static readonly string[] attribute_targets = new [] { "param" }; + + FullNamedExpression texpr; + Modifier modFlags; + string name; + Expression default_expr; + protected TypeSpec parameter_type; + readonly Location loc; + protected int idx; + public bool HasAddressTaken; + + TemporaryVariableReference expr_tree_variable; + + HoistedParameter hoisted_variant; + + public Parameter (FullNamedExpression type, string name, Modifier mod, Attributes attrs, Location loc) + { + this.name = name; + modFlags = mod; + this.loc = loc; + texpr = type; + + // Only assign, attributes will be attached during resolve + base.attributes = attrs; + } + + #region Properties + + public Expression DefaultExpression { + get { + return default_expr; + } + } + + public DefaultParameterValueExpression DefaultValue { + get { + return default_expr as DefaultParameterValueExpression; + } + set { + default_expr = value; + } + } + + Expression IParameterData.DefaultValue { + get { + var expr = default_expr as DefaultParameterValueExpression; + return expr == null ? default_expr : expr.Child; + } + } + + bool HasOptionalExpression { + get { + return default_expr is DefaultParameterValueExpression; + } + } + + public Location Location { + get { + return loc; + } + } + + public Modifier ParameterModifier { + get { + return modFlags; + } + } + + public TypeSpec Type { + get { + return parameter_type; + } + set { + parameter_type = value; + } + } + + public FullNamedExpression TypeExpression { + get { + return texpr; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + #endregion + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.In && ModFlags == Modifier.OUT) { + a.Report.Error (36, a.Location, "An out parameter cannot have the `In' attribute"); + return; + } + + if (a.Type == pa.ParamArray) { + a.Report.Error (674, a.Location, "Do not use `System.ParamArrayAttribute'. Use the `params' keyword instead"); + return; + } + + if (a.Type == pa.Out && (ModFlags & Modifier.REF) != 0 && + !OptAttributes.Contains (pa.In)) { + a.Report.Error (662, a.Location, + "Cannot specify only `Out' attribute on a ref parameter. Use both `In' and `Out' attributes or neither"); + return; + } + + if (a.Type == pa.CLSCompliant) { + a.Report.Warning (3022, 1, a.Location, "CLSCompliant attribute has no meaning when applied to parameters. Try putting it on the method instead"); + } else if (a.Type == pa.DefaultParameterValue || a.Type == pa.OptionalParameter) { + if (HasOptionalExpression) { + a.Report.Error (1745, a.Location, + "Cannot specify `{0}' attribute on optional parameter `{1}'", + a.Type.GetSignatureForError ().Replace ("Attribute", ""), Name); + } + + if (a.Type == pa.DefaultParameterValue) + return; + } else if (a.Type == pa.CallerMemberNameAttribute) { + if ((modFlags & Modifier.CallerMemberName) == 0) { + a.Report.Error (4022, a.Location, + "The CallerMemberName attribute can only be applied to parameters with default value"); + } + } else if (a.Type == pa.CallerLineNumberAttribute) { + if ((modFlags & Modifier.CallerLineNumber) == 0) { + a.Report.Error (4020, a.Location, + "The CallerLineNumber attribute can only be applied to parameters with default value"); + } + } else if (a.Type == pa.CallerFilePathAttribute) { + if ((modFlags & Modifier.CallerFilePath) == 0) { + a.Report.Error (4021, a.Location, + "The CallerFilePath attribute can only be applied to parameters with default value"); + } + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public virtual bool CheckAccessibility (InterfaceMemberBase member) + { + if (parameter_type == null) + return true; + + return member.IsAccessibleAs (parameter_type); + } + + bool IsValidCallerContext (MemberCore memberContext) + { + var m = memberContext as Method; + if (m != null) + return !m.IsPartialImplementation; + + return true; + } + + // + // Resolve is used in method definitions + // + public virtual TypeSpec Resolve (IMemberContext rc, int index) + { + if (parameter_type != null) + return parameter_type; + + if (attributes != null) + attributes.AttachTo (this, rc); + + parameter_type = texpr.ResolveAsType (rc); + if (parameter_type == null) + return null; + + this.idx = index; + + if ((modFlags & Parameter.Modifier.RefOutMask) != 0 && parameter_type.IsSpecialRuntimeType) { + rc.Module.Compiler.Report.Error (1601, Location, "Method or delegate parameter cannot be of type `{0}'", + GetSignatureForError ()); + return null; + } + + VarianceDecl.CheckTypeVariance (parameter_type, + (modFlags & Parameter.Modifier.RefOutMask) != 0 ? Variance.None : Variance.Contravariant, + rc); + + if (parameter_type.IsStatic) { + rc.Module.Compiler.Report.Error (721, Location, "`{0}': static types cannot be used as parameters", + texpr.GetSignatureForError ()); + return parameter_type; + } + + if ((modFlags & Modifier.This) != 0 && (parameter_type.IsPointer || parameter_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)) { + rc.Module.Compiler.Report.Error (1103, Location, "The extension method cannot be of type `{0}'", + parameter_type.GetSignatureForError ()); + } + + return parameter_type; + } + + void ResolveCallerAttributes (ResolveContext rc) + { + var pa = rc.Module.PredefinedAttributes; + TypeSpec caller_type; + Attribute callerMemberName = null, callerFilePath = null; + + foreach (var attr in attributes.Attrs) { + var atype = attr.ResolveTypeForComparison (); + if (atype == null) + continue; + + if (atype == pa.CallerMemberNameAttribute) { + caller_type = rc.BuiltinTypes.String; + if (caller_type != parameter_type && !Convert.ImplicitReferenceConversionExists (caller_type, parameter_type)) { + rc.Report.Error (4019, attr.Location, + "The CallerMemberName attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'", + caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ()); + } + + if (!IsValidCallerContext (rc.CurrentMemberDefinition)) { + rc.Report.Warning (4026, 1, attr.Location, + "The CallerMemberName applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments", + name); + } + + modFlags |= Modifier.CallerMemberName; + callerMemberName = attr; + continue; + } + + if (atype == pa.CallerLineNumberAttribute) { + caller_type = rc.BuiltinTypes.Int; + if (caller_type != parameter_type && !Convert.ImplicitStandardConversionExists (new IntConstant (caller_type, int.MaxValue, Location.Null), parameter_type)) { + rc.Report.Error (4017, attr.Location, + "The CallerLineNumberAttribute attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'", + caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ()); + } + + if (!IsValidCallerContext (rc.CurrentMemberDefinition)) { + rc.Report.Warning (4024, 1, attr.Location, + "The CallerLineNumberAttribute applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments", + name); + } + + modFlags |= Modifier.CallerLineNumber; + continue; + } + + if (atype == pa.CallerFilePathAttribute) { + caller_type = rc.BuiltinTypes.String; + if (caller_type != parameter_type && !Convert.ImplicitReferenceConversionExists (caller_type, parameter_type)) { + rc.Report.Error (4018, attr.Location, + "The CallerFilePath attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'", + caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ()); + } + + if (!IsValidCallerContext (rc.CurrentMemberDefinition)) { + rc.Report.Warning (4025, 1, attr.Location, + "The CallerFilePath applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments", + name); + } + + modFlags |= Modifier.CallerFilePath; + callerFilePath = attr; + continue; + } + } + + if ((modFlags & Modifier.CallerLineNumber) != 0) { + if (callerMemberName != null) { + rc.Report.Warning (7081, 1, callerMemberName.Location, + "The CallerMemberNameAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerLineNumberAttribute", + Name); + } + + if (callerFilePath != null) { + rc.Report.Warning (7082, 1, callerFilePath.Location, + "The CallerFilePathAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerLineNumberAttribute", + name); + } + } + + if ((modFlags & Modifier.CallerMemberName) != 0) { + if (callerFilePath != null) { + rc.Report.Warning (7080, 1, callerFilePath.Location, + "The CallerMemberNameAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerFilePathAttribute", + name); + } + + } + } + + public void ResolveDefaultValue (ResolveContext rc) + { + // + // Default value was specified using an expression + // + if (default_expr != null) { + ((DefaultParameterValueExpression)default_expr).Resolve (rc, this); + if (attributes != null) + ResolveCallerAttributes (rc); + + return; + } + + if (attributes == null) + return; + + var pa = rc.Module.PredefinedAttributes; + var def_attr = attributes.Search (pa.DefaultParameterValue); + if (def_attr != null) { + if (def_attr.Resolve () == null) + return; + + var default_expr_attr = def_attr.GetParameterDefaultValue (); + if (default_expr_attr == null) + return; + + var dpa_rc = def_attr.CreateResolveContext (); + default_expr = default_expr_attr.Resolve (dpa_rc); + + if (default_expr is BoxedCast) + default_expr = ((BoxedCast) default_expr).Child; + + Constant c = default_expr as Constant; + if (c == null) { + if (parameter_type.BuiltinType == BuiltinTypeSpec.Type.Object) { + rc.Report.Error (1910, default_expr.Location, + "Argument of type `{0}' is not applicable for the DefaultParameterValue attribute", + default_expr.Type.GetSignatureForError ()); + } else { + rc.Report.Error (1909, default_expr.Location, + "The DefaultParameterValue attribute is not applicable on parameters of type `{0}'", + default_expr.Type.GetSignatureForError ()); + } + + default_expr = null; + return; + } + + if (TypeSpecComparer.IsEqual (default_expr.Type, parameter_type) || + (default_expr is NullConstant && TypeSpec.IsReferenceType (parameter_type) && !parameter_type.IsGenericParameter) || + parameter_type.BuiltinType == BuiltinTypeSpec.Type.Object) { + return; + } + + // + // LAMESPEC: Some really weird csc behaviour which we have to mimic + // User operators returning same type as parameter type are considered + // valid for this attribute only + // + // struct S { public static implicit operator S (int i) {} } + // + // void M ([DefaultParameterValue (3)]S s) + // + var expr = Convert.ImplicitUserConversion (dpa_rc, default_expr, parameter_type, loc); + if (expr != null && TypeSpecComparer.IsEqual (expr.Type, parameter_type)) { + return; + } + + rc.Report.Error (1908, default_expr.Location, "The type of the default value should match the type of the parameter"); + return; + } + + var opt_attr = attributes.Search (pa.OptionalParameter); + if (opt_attr != null) { + default_expr = EmptyExpression.MissingValue; + } + } + + public bool HasDefaultValue { + get { return default_expr != null; } + } + + public bool HasExtensionMethodModifier { + get { return (modFlags & Modifier.This) != 0; } + } + + // + // Hoisted parameter variant + // + public HoistedParameter HoistedVariant { + get { + return hoisted_variant; + } + set { + hoisted_variant = value; + } + } + + public Modifier ModFlags { + get { return modFlags & ~Modifier.This; } + } + + public string Name { + get { return name; } + set { name = value; } + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Parameter; + } + } + + public void Error_DuplicateName (Report r) + { + r.Error (100, Location, "The parameter name `{0}' is a duplicate", Name); + } + + public virtual string GetSignatureForError () + { + string type_name; + if (parameter_type != null) + type_name = parameter_type.GetSignatureForError (); + else + type_name = texpr.GetSignatureForError (); + + string mod = GetModifierSignature (modFlags); + if (mod.Length > 0) + return String.Concat (mod, " ", type_name); + + return type_name; + } + + public static string GetModifierSignature (Modifier mod) + { + switch (mod) { + case Modifier.OUT: + return "out"; + case Modifier.PARAMS: + return "params"; + case Modifier.REF: + return "ref"; + case Modifier.This: + return "this"; + default: + return ""; + } + } + + public void IsClsCompliant (IMemberContext ctx) + { + if (parameter_type.IsCLSCompliant ()) + return; + + ctx.Module.Compiler.Report.Warning (3001, 1, Location, + "Argument type `{0}' is not CLS-compliant", parameter_type.GetSignatureForError ()); + } + + public virtual void ApplyAttributes (MethodBuilder mb, ConstructorBuilder cb, int index, PredefinedAttributes pa) + { + if (builder != null) + throw new InternalErrorException ("builder already exists"); + + var pattrs = ParametersCompiled.GetParameterAttribute (modFlags); + if (HasOptionalExpression) + pattrs |= ParameterAttributes.Optional; + + if (mb == null) + builder = cb.DefineParameter (index, pattrs, Name); + else + builder = mb.DefineParameter (index, pattrs, Name); + + if (OptAttributes != null) + OptAttributes.Emit (); + + if (HasDefaultValue && default_expr.Type != null) { + // + // Emit constant values for true constants only, the other + // constant-like expressions will rely on default value expression + // + var def_value = DefaultValue; + Constant c = def_value != null ? def_value.Child as Constant : default_expr as Constant; + if (c != null) { + if (c.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + pa.DecimalConstant.EmitAttribute (builder, (decimal) c.GetValue (), c.Location); + } else { + builder.SetConstant (c.GetValue ()); + } + } else if (default_expr.Type.IsStruct || default_expr.Type.IsGenericParameter) { + // + // Handles special case where default expression is used with value-type or type parameter + // + // void Foo (S s = default (S)) {} + // + builder.SetConstant (null); + } + } + + if (parameter_type != null) { + if (parameter_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + pa.Dynamic.EmitAttribute (builder); + } else if (parameter_type.HasDynamicElement) { + pa.Dynamic.EmitAttribute (builder, parameter_type, Location); + } + } + } + + public Parameter Clone () + { + Parameter p = (Parameter) MemberwiseClone (); + if (attributes != null) + p.attributes = attributes.Clone (); + + return p; + } + + public ExpressionStatement CreateExpressionTreeVariable (BlockContext ec) + { + if ((modFlags & Modifier.RefOutMask) != 0) + ec.Report.Error (1951, Location, "An expression tree parameter cannot use `ref' or `out' modifier"); + + expr_tree_variable = TemporaryVariableReference.Create (ResolveParameterExpressionType (ec, Location).Type, ec.CurrentBlock.ParametersBlock, Location); + expr_tree_variable = (TemporaryVariableReference) expr_tree_variable.Resolve (ec); + + Arguments arguments = new Arguments (2); + arguments.Add (new Argument (new TypeOf (parameter_type, Location))); + arguments.Add (new Argument (new StringConstant (ec.BuiltinTypes, Name, Location))); + return new SimpleAssign (ExpressionTreeVariableReference (), + Expression.CreateExpressionFactoryCall (ec, "Parameter", null, arguments, Location)); + } + + public void Emit (EmitContext ec) + { + ec.EmitArgumentLoad (idx); + } + + public void EmitAssign (EmitContext ec) + { + ec.EmitArgumentStore (idx); + } + + public void EmitAddressOf (EmitContext ec) + { + if ((ModFlags & Modifier.RefOutMask) != 0) { + ec.EmitArgumentLoad (idx); + } else { + ec.EmitArgumentAddress (idx); + } + } + + public TemporaryVariableReference ExpressionTreeVariableReference () + { + return expr_tree_variable; + } + + // + // System.Linq.Expressions.ParameterExpression type + // + public static TypeExpr ResolveParameterExpressionType (IMemberContext ec, Location location) + { + TypeSpec p_type = ec.Module.PredefinedTypes.ParameterExpression.Resolve (); + return new TypeExpression (p_type, location); + } + + public void SetIndex (int index) + { + idx = index; + } + + public void Warning_UselessOptionalParameter (Report Report) + { + Report.Warning (1066, 1, Location, + "The default value specified for optional parameter `{0}' will never be used", + Name); + } + } + + // + // Imported or resolved parameter information + // + public class ParameterData : IParameterData + { + readonly string name; + readonly Parameter.Modifier modifiers; + readonly Expression default_value; + + public ParameterData (string name, Parameter.Modifier modifiers) + { + this.name = name; + this.modifiers = modifiers; + } + + public ParameterData (string name, Parameter.Modifier modifiers, Expression defaultValue) + : this (name, modifiers) + { + this.default_value = defaultValue; + } + + #region IParameterData Members + + public Expression DefaultValue { + get { return default_value; } + } + + public bool HasExtensionMethodModifier { + get { return (modifiers & Parameter.Modifier.This) != 0; } + } + + public bool HasDefaultValue { + get { return default_value != null; } + } + + public Parameter.Modifier ModFlags { + get { return modifiers; } + } + + public string Name { + get { return name; } + } + + #endregion + } + + public abstract class AParametersCollection + { + protected bool has_arglist; + protected bool has_params; + + // Null object pattern + protected IParameterData [] parameters; + protected TypeSpec [] types; + + public CallingConventions CallingConvention { + get { + return has_arglist ? + CallingConventions.VarArgs : + CallingConventions.Standard; + } + } + + public int Count { + get { return parameters.Length; } + } + + public TypeSpec ExtensionMethodType { + get { + if (Count == 0) + return null; + + return FixedParameters [0].HasExtensionMethodModifier ? + types [0] : null; + } + } + + public IParameterData [] FixedParameters { + get { + return parameters; + } + } + + public static ParameterAttributes GetParameterAttribute (Parameter.Modifier modFlags) + { + return (modFlags & Parameter.Modifier.OUT) != 0 ? + ParameterAttributes.Out : ParameterAttributes.None; + } + + // Very expensive operation + public MetaType[] GetMetaInfo () + { + MetaType[] types; + if (has_arglist) { + if (Count == 1) + return MetaType.EmptyTypes; + + types = new MetaType[Count - 1]; + } else { + if (Count == 0) + return MetaType.EmptyTypes; + + types = new MetaType[Count]; + } + + for (int i = 0; i < types.Length; ++i) { + types[i] = Types[i].GetMetaInfo (); + + if ((FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == 0) + continue; + + // TODO MemberCache: Should go to MetaInfo getter + types [i] = types [i].MakeByRefType (); + } + + return types; + } + + // + // Returns the parameter information based on the name + // + public int GetParameterIndexByName (string name) + { + for (int idx = 0; idx < Count; ++idx) { + if (parameters [idx].Name == name) + return idx; + } + + return -1; + } + + public string GetSignatureForDocumentation () + { + if (IsEmpty) + return string.Empty; + + StringBuilder sb = new StringBuilder ("("); + for (int i = 0; i < Count; ++i) { + if (i != 0) + sb.Append (","); + + sb.Append (types [i].GetSignatureForDocumentation ()); + + if ((parameters[i].ModFlags & Parameter.Modifier.RefOutMask) != 0) + sb.Append ("@"); + } + sb.Append (")"); + + return sb.ToString (); + } + + public string GetSignatureForError () + { + return GetSignatureForError ("(", ")", Count); + } + + public string GetSignatureForError (string start, string end, int count) + { + StringBuilder sb = new StringBuilder (start); + for (int i = 0; i < count; ++i) { + if (i != 0) + sb.Append (", "); + sb.Append (ParameterDesc (i)); + } + sb.Append (end); + return sb.ToString (); + } + + public bool HasArglist { + get { return has_arglist; } + } + + public bool HasExtensionMethodType { + get { + if (Count == 0) + return false; + + return FixedParameters [0].HasExtensionMethodModifier; + } + } + + public bool HasParams { + get { return has_params; } + } + + public bool IsEmpty { + get { return parameters.Length == 0; } + } + + public AParametersCollection Inflate (TypeParameterInflator inflator) + { + TypeSpec[] inflated_types = null; + bool default_value = false; + + for (int i = 0; i < Count; ++i) { + var inflated_param = inflator.Inflate (types[i]); + if (inflated_types == null) { + if (inflated_param == types[i]) + continue; + + default_value |= FixedParameters[i].HasDefaultValue; + inflated_types = new TypeSpec[types.Length]; + Array.Copy (types, inflated_types, types.Length); + } else { + if (inflated_param == types[i]) + continue; + + default_value |= FixedParameters[i].HasDefaultValue; + } + + inflated_types[i] = inflated_param; + } + + if (inflated_types == null) + return this; + + var clone = (AParametersCollection) MemberwiseClone (); + clone.types = inflated_types; + + // + // Default expression is original expression from the parameter + // declaration context which can be of nested enum in generic class type. + // In such case we end up with expression type of G.E and e.g. parameter + // type of G.E and conversion would fail without inflate in this + // context. + // + if (default_value) { + clone.parameters = new IParameterData[Count]; + for (int i = 0; i < Count; ++i) { + var fp = FixedParameters[i]; + clone.FixedParameters[i] = fp; + + if (!fp.HasDefaultValue) + continue; + + var expr = fp.DefaultValue; + + if (inflated_types[i] == expr.Type) + continue; + + var c = expr as Constant; + if (c != null) { + // + // It may fail we are inflating before type validation is done + // + c = Constant.ExtractConstantFromValue (inflated_types[i], c.GetValue (), expr.Location); + if (c == null) + expr = new DefaultValueExpression (new TypeExpression (inflated_types[i], expr.Location), expr.Location); + else + expr = c; + } else if (expr is DefaultValueExpression) + expr = new DefaultValueExpression (new TypeExpression (inflated_types[i], expr.Location), expr.Location); + + clone.FixedParameters[i] = new ParameterData (fp.Name, fp.ModFlags, expr); + } + } + + return clone; + } + + public string ParameterDesc (int pos) + { + if (types == null || types [pos] == null) + return ((Parameter)FixedParameters [pos]).GetSignatureForError (); + + string type = types [pos].GetSignatureForError (); + if (FixedParameters [pos].HasExtensionMethodModifier) + return "this " + type; + + var mod = FixedParameters[pos].ModFlags & Parameter.Modifier.ModifierMask; + if (mod == 0) + return type; + + return Parameter.GetModifierSignature (mod) + " " + type; + } + + public TypeSpec[] Types { + get { return types; } + set { types = value; } + } + } + + // + // A collection of imported or resolved parameters + // + public class ParametersImported : AParametersCollection + { + public ParametersImported (IParameterData [] parameters, TypeSpec [] types, bool hasArglist, bool hasParams) + { + this.parameters = parameters; + this.types = types; + this.has_arglist = hasArglist; + this.has_params = hasParams; + } + + public ParametersImported (IParameterData[] param, TypeSpec[] types, bool hasParams) + { + this.parameters = param; + this.types = types; + this.has_params = hasParams; + } + } + + /// + /// Represents the methods parameters + /// + public class ParametersCompiled : AParametersCollection + { + public static readonly ParametersCompiled EmptyReadOnlyParameters = new ParametersCompiled (); + + // Used by C# 2.0 delegates + public static readonly ParametersCompiled Undefined = new ParametersCompiled (); + + private ParametersCompiled () + { + parameters = new Parameter [0]; + types = TypeSpec.EmptyTypes; + } + + private ParametersCompiled (IParameterData[] parameters, TypeSpec[] types) + { + this.parameters = parameters; + this.types = types; + } + + public ParametersCompiled (params Parameter[] parameters) + { + if (parameters == null || parameters.Length == 0) + throw new ArgumentException ("Use EmptyReadOnlyParameters"); + + this.parameters = parameters; + int count = parameters.Length; + + for (int i = 0; i < count; i++){ + has_params |= (parameters [i].ModFlags & Parameter.Modifier.PARAMS) != 0; + } + } + + public ParametersCompiled (Parameter [] parameters, bool has_arglist) : + this (parameters) + { + this.has_arglist = has_arglist; + } + + public static ParametersCompiled CreateFullyResolved (Parameter p, TypeSpec type) + { + return new ParametersCompiled (new Parameter [] { p }, new TypeSpec [] { type }); + } + + public static ParametersCompiled CreateFullyResolved (Parameter[] parameters, TypeSpec[] types) + { + return new ParametersCompiled (parameters, types); + } + + public static ParametersCompiled Prefix (ParametersCompiled parameters, Parameter p, TypeSpec type) + { + var ptypes = new TypeSpec [parameters.Count + 1]; + ptypes [0] = type; + Array.Copy (parameters.Types, 0, ptypes, 1, parameters.Count); + + var param = new Parameter [ptypes.Length]; + param [0] = p; + for (int i = 0; i < parameters.Count; ++i) { + var pi = parameters [i]; + param [i + 1] = pi; + pi.SetIndex (i + 1); + } + + return ParametersCompiled.CreateFullyResolved (param, ptypes); + } + + // + // TODO: This does not fit here, it should go to different version of AParametersCollection + // as the underlying type is not Parameter and some methods will fail to cast + // + public static AParametersCollection CreateFullyResolved (params TypeSpec[] types) + { + var pd = new ParameterData [types.Length]; + for (int i = 0; i < pd.Length; ++i) + pd[i] = new ParameterData (null, Parameter.Modifier.NONE, null); + + return new ParametersCompiled (pd, types); + } + + public static ParametersCompiled CreateImplicitParameter (FullNamedExpression texpr, Location loc) + { + return new ParametersCompiled ( + new[] { new Parameter (texpr, "value", Parameter.Modifier.NONE, null, loc) }, + null); + } + + public void CheckConstraints (IMemberContext mc) + { + foreach (Parameter p in parameters) { + // + // It's null for compiler generated types or special types like __arglist + // + if (p.TypeExpression != null) + ConstraintChecker.Check (mc, p.Type, p.TypeExpression.Location); + } + } + + // + // Returns non-zero value for equal CLS parameter signatures + // + public static int IsSameClsSignature (AParametersCollection a, AParametersCollection b) + { + int res = 0; + + for (int i = 0; i < a.Count; ++i) { + var a_type = a.Types[i]; + var b_type = b.Types[i]; + if (TypeSpecComparer.Override.IsEqual (a_type, b_type)) { + if ((a.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (b.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) + res |= 1; + + continue; + } + + var ac_a = a_type as ArrayContainer; + if (ac_a == null) + return 0; + + var ac_b = b_type as ArrayContainer; + if (ac_b == null) + return 0; + + if (ac_a.Element is ArrayContainer || ac_b.Element is ArrayContainer) { + res |= 2; + continue; + } + + if (ac_a.Rank != ac_b.Rank && TypeSpecComparer.Override.IsEqual (ac_a.Element, ac_b.Element)) { + res |= 1; + continue; + } + + return 0; + } + + return res; + } + + public static ParametersCompiled MergeGenerated (CompilerContext ctx, ParametersCompiled userParams, bool checkConflicts, Parameter compilerParams, TypeSpec compilerTypes) + { + return MergeGenerated (ctx, userParams, checkConflicts, + new Parameter [] { compilerParams }, + new TypeSpec [] { compilerTypes }); + } + + // + // Use this method when you merge compiler generated parameters with user parameters + // + public static ParametersCompiled MergeGenerated (CompilerContext ctx, ParametersCompiled userParams, bool checkConflicts, Parameter[] compilerParams, TypeSpec[] compilerTypes) + { + Parameter[] all_params = new Parameter [userParams.Count + compilerParams.Length]; + userParams.FixedParameters.CopyTo(all_params, 0); + + TypeSpec [] all_types; + if (userParams.types != null) { + all_types = new TypeSpec [all_params.Length]; + userParams.Types.CopyTo (all_types, 0); + } else { + all_types = null; + } + + int last_filled = userParams.Count; + int index = 0; + foreach (Parameter p in compilerParams) { + for (int i = 0; i < last_filled; ++i) { + while (p.Name == all_params [i].Name) { + if (checkConflicts && i < userParams.Count) { + ctx.Report.Error (316, userParams[i].Location, + "The parameter name `{0}' conflicts with a compiler generated name", p.Name); + } + p.Name = '_' + p.Name; + } + } + all_params [last_filled] = p; + if (all_types != null) + all_types [last_filled] = compilerTypes [index++]; + ++last_filled; + } + + ParametersCompiled parameters = new ParametersCompiled (all_params, all_types); + parameters.has_params = userParams.has_params; + return parameters; + } + + // + // Parameters checks for members which don't have a block + // + public void CheckParameters (MemberCore member) + { + for (int i = 0; i < parameters.Length; ++i) { + var name = parameters[i].Name; + for (int ii = i + 1; ii < parameters.Length; ++ii) { + if (parameters[ii].Name == name) + this[ii].Error_DuplicateName (member.Compiler.Report); + } + } + } + + public bool Resolve (IMemberContext ec) + { + if (types != null) + return true; + + types = new TypeSpec [Count]; + + bool ok = true; + Parameter p; + for (int i = 0; i < FixedParameters.Length; ++i) { + p = this [i]; + TypeSpec t = p.Resolve (ec, i); + if (t == null) { + ok = false; + continue; + } + + types [i] = t; + } + + return ok; + } + + public void ResolveDefaultValues (MemberCore m) + { + ResolveContext rc = null; + for (int i = 0; i < parameters.Length; ++i) { + Parameter p = (Parameter) parameters [i]; + + // + // Try not to enter default values resolution if there are is not any default value possible + // + if (p.HasDefaultValue || p.OptAttributes != null) { + if (rc == null) + rc = new ResolveContext (m); + + p.ResolveDefaultValue (rc); + } + } + } + + // Define each type attribute (in/out/ref) and + // the argument names. + public void ApplyAttributes (IMemberContext mc, MethodBase builder) + { + if (Count == 0) + return; + + MethodBuilder mb = builder as MethodBuilder; + ConstructorBuilder cb = builder as ConstructorBuilder; + var pa = mc.Module.PredefinedAttributes; + + for (int i = 0; i < Count; i++) { + this [i].ApplyAttributes (mb, cb, i + 1, pa); + } + } + + public void VerifyClsCompliance (IMemberContext ctx) + { + foreach (Parameter p in FixedParameters) + p.IsClsCompliant (ctx); + } + + public Parameter this [int pos] { + get { return (Parameter) parameters [pos]; } + } + + public Expression CreateExpressionTree (BlockContext ec, Location loc) + { + var initializers = new ArrayInitializer (Count, loc); + foreach (Parameter p in FixedParameters) { + // + // Each parameter expression is stored to local variable + // to save some memory when referenced later. + // + StatementExpression se = new StatementExpression (p.CreateExpressionTreeVariable (ec), Location.Null); + if (se.Resolve (ec)) { + ec.CurrentBlock.AddScopeStatement (new TemporaryVariableReference.Declarator (p.ExpressionTreeVariableReference ())); + ec.CurrentBlock.AddScopeStatement (se); + } + + initializers.Add (p.ExpressionTreeVariableReference ()); + } + + return new ArrayCreation ( + Parameter.ResolveParameterExpressionType (ec, loc), + initializers, loc); + } + + public ParametersCompiled Clone () + { + ParametersCompiled p = (ParametersCompiled) MemberwiseClone (); + + p.parameters = new IParameterData [parameters.Length]; + for (int i = 0; i < Count; ++i) + p.parameters [i] = this [i].Clone (); + + return p; + } + } + + // + // Default parameter value expression. We need this wrapper to handle + // default parameter values of folded constants (e.g. indexer parameters). + // The expression is resolved only once but applied to two methods which + // both share reference to this expression and we ensure that resolving + // this expression always returns same instance + // + public class DefaultParameterValueExpression : CompositeExpression + { + public DefaultParameterValueExpression (Expression expr) + : base (expr) + { + } + + public void Resolve (ResolveContext rc, Parameter p) + { + var expr = Resolve (rc); + if (expr == null) { + this.expr = ErrorExpression.Instance; + return; + } + + expr = Child; + + if (!(expr is Constant || expr is DefaultValueExpression || (expr is New && ((New) expr).IsDefaultStruct))) { + if (!(expr is ErrorExpression)) { + rc.Report.Error (1736, Location, + "The expression being assigned to optional parameter `{0}' must be a constant or default value", + p.Name); + } + + return; + } + + var parameter_type = p.Type; + if (type == parameter_type) + return; + + var res = Convert.ImplicitConversionStandard (rc, expr, parameter_type, Location); + if (res != null) { + if (parameter_type.IsNullableType && res is Nullable.Wrap) { + Nullable.Wrap wrap = (Nullable.Wrap) res; + res = wrap.Child; + if (!(res is Constant)) { + rc.Report.Error (1770, Location, + "The expression being assigned to nullable optional parameter `{0}' must be default value", + p.Name); + return; + } + } + + if (!expr.IsNull && TypeSpec.IsReferenceType (parameter_type) && parameter_type.BuiltinType != BuiltinTypeSpec.Type.String) { + rc.Report.Error (1763, Location, + "Optional parameter `{0}' of type `{1}' can only be initialized with `null'", + p.Name, parameter_type.GetSignatureForError ()); + + return; + } + + this.expr = res; + return; + } + + rc.Report.Error (1750, Location, + "Optional parameter expression of type `{0}' cannot be converted to parameter type `{1}'", + type.GetSignatureForError (), parameter_type.GetSignatureForError ()); + + this.expr = ErrorExpression.Instance; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/pending.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/pending.cs new file mode 100644 index 000000000..0f863a7bc --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/pending.cs @@ -0,0 +1,734 @@ +// +// pending.cs: Pending method implementation +// +// Authors: +// Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Linq; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + struct TypeAndMethods { + public TypeSpec type; + public IList methods; + + // + // Whether it is optional, this is used to allow the explicit/implicit + // implementation when a base class already implements an interface. + // + // For example: + // + // class X : IA { } class Y : X, IA { IA.Explicit (); } + // + public bool optional; + + // + // This flag on the method says `We found a match, but + // because it was private, we could not use the match + // + public MethodData [] found; + + // If a method is defined here, then we always need to + // create a proxy for it. This is used when implementing + // an interface's indexer with a different IndexerName. + public MethodSpec [] need_proxy; + } + + struct ProxyMethodContext : IMemberContext + { + readonly TypeContainer container; + + public ProxyMethodContext (TypeContainer container) + { + this.container = container; + } + + public TypeSpec CurrentType { + get { + throw new NotImplementedException (); + } + } + + public TypeParameters CurrentTypeParameters { + get { + throw new NotImplementedException (); + } + } + + public MemberCore CurrentMemberDefinition { + get { + throw new NotImplementedException (); + } + } + + public bool IsObsolete { + get { + return false; + } + } + + public bool IsUnsafe { + get { + throw new NotImplementedException (); + } + } + + public bool IsStatic { + get { + return false; + } + } + + public ModuleContainer Module { + get { + return container.Module; + } + } + + public string GetSignatureForError () + { + throw new NotImplementedException (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + throw new NotImplementedException (); + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + throw new NotImplementedException (); + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + throw new NotImplementedException (); + } + } + + public class PendingImplementation + { + /// + /// The container for this PendingImplementation + /// + readonly TypeDefinition container; + + /// + /// This is the array of TypeAndMethods that describes the pending implementations + /// (both interfaces and abstract methods in base class) + /// + TypeAndMethods [] pending_implementations; + + PendingImplementation (TypeDefinition container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total) + { + var type_builder = container.Definition; + + this.container = container; + pending_implementations = new TypeAndMethods [total]; + + int i = 0; + if (abstract_methods != null) { + int count = abstract_methods.Length; + pending_implementations [i].methods = new MethodSpec [count]; + pending_implementations [i].need_proxy = new MethodSpec [count]; + + pending_implementations [i].methods = abstract_methods; + pending_implementations [i].found = new MethodData [count]; + pending_implementations [i].type = type_builder; + ++i; + } + + foreach (MissingInterfacesInfo missing in missing_ifaces) { + var iface = missing.Type; + var mi = MemberCache.GetInterfaceMethods (iface); + + int count = mi.Count; + pending_implementations [i].type = iface; + pending_implementations [i].optional = missing.Optional; + pending_implementations [i].methods = mi; + pending_implementations [i].found = new MethodData [count]; + pending_implementations [i].need_proxy = new MethodSpec [count]; + i++; + } + } + + Report Report { + get { + return container.Module.Compiler.Report; + } + } + + struct MissingInterfacesInfo { + public TypeSpec Type; + public bool Optional; + + public MissingInterfacesInfo (TypeSpec t) + { + Type = t; + Optional = false; + } + } + + static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0]; + + static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container) + { + // + // Interfaces will return all interfaces that the container + // implements including any inherited interfaces + // + var impl = container.Definition.Interfaces; + + if (impl == null || impl.Count == 0) + return EmptyMissingInterfacesInfo; + + var ret = new MissingInterfacesInfo[impl.Count]; + + for (int i = 0; i < ret.Length; i++) + ret [i] = new MissingInterfacesInfo (impl [i]); + + // we really should not get here because Object doesnt implement any + // interfaces. But it could implement something internal, so we have + // to handle that case. + if (container.BaseType == null) + return ret; + + var base_impls = container.BaseType.Interfaces; + if (base_impls != null) { + foreach (TypeSpec t in base_impls) { + for (int i = 0; i < ret.Length; i++) { + if (t == ret[i].Type) { + ret[i].Optional = true; + break; + } + } + } + } + + return ret; + } + + // + // Factory method: if there are pending implementation methods, we return a PendingImplementation + // object, otherwise we return null. + // + // Register method implementations are either abstract methods + // flagged as such on the base class or interface methods + // + static public PendingImplementation GetPendingImplementations (TypeDefinition container) + { + TypeSpec b = container.BaseType; + + var missing_interfaces = GetMissingInterfaces (container); + + // + // If we are implementing an abstract class, and we are not + // ourselves abstract, and there are abstract methods (C# allows + // abstract classes that have no abstract methods), then allocate + // one slot. + // + // We also pre-compute the methods. + // + bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0); + MethodSpec[] abstract_methods = null; + + if (implementing_abstract){ + var am = MemberCache.GetNotImplementedAbstractMethods (b); + + if (am == null) { + implementing_abstract = false; + } else { + abstract_methods = new MethodSpec[am.Count]; + am.CopyTo (abstract_methods, 0); + } + } + + int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0); + if (total == 0) + return null; + + var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total); + + // + // check for inherited conflicting methods + // + foreach (var p in pending.pending_implementations) { + // + // It can happen for generic interfaces only + // + if (!p.type.IsGeneric) + continue; + + // + // CLR does not distinguishes between ref and out + // + for (int i = 0; i < p.methods.Count; ++i) { + MethodSpec compared_method = p.methods[i]; + if (compared_method.Parameters.IsEmpty) + continue; + + for (int ii = i + 1; ii < p.methods.Count; ++ii) { + MethodSpec tested_method = p.methods[ii]; + if (compared_method.Name != tested_method.Name) + continue; + + if (p.type != tested_method.DeclaringType) + continue; + + if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types)) + continue; + + bool exact_match = true; + bool ref_only_difference = false; + var cp = compared_method.Parameters.FixedParameters; + var tp = tested_method.Parameters.FixedParameters; + + for (int pi = 0; pi < cp.Length; ++pi) { + // + // First check exact modifiers match + // + if ((cp[pi].ModFlags & Parameter.Modifier.RefOutMask) == (tp[pi].ModFlags & Parameter.Modifier.RefOutMask)) + continue; + + if (((cp[pi].ModFlags | tp[pi].ModFlags) & Parameter.Modifier.RefOutMask) == Parameter.Modifier.RefOutMask) { + ref_only_difference = true; + continue; + } + + exact_match = false; + break; + } + + if (!exact_match || !ref_only_difference) + continue; + + pending.Report.SymbolRelatedToPreviousError (compared_method); + pending.Report.SymbolRelatedToPreviousError (tested_method); + pending.Report.Error (767, container.Location, + "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only", + p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ()); + + break; + } + } + } + + return pending; + } + + public enum Operation { + // + // If you change this, review the whole InterfaceMethod routine as there + // are a couple of assumptions on these three states + // + Lookup, ClearOne, ClearAll + } + + /// + /// Whether the specified method is an interface method implementation + /// + public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional) + { + return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional); + } + + public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional) + { + InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional); + } + + /// + /// If a method in Type `t' (or null to look in all interfaces + /// and the base abstract class) with name `Name', return type `ret_type' and + /// arguments `args' implements an interface, this method will + /// return the MethodInfo that this method implements. + /// + /// If `name' is null, we operate solely on the method's signature. This is for + /// instance used when implementing indexers. + /// + /// The `Operation op' controls whether to lookup, clear the pending bit, or clear + /// all the methods with the given signature. + /// + /// The `MethodInfo need_proxy' is used when we're implementing an interface's + /// indexer in a class. If the new indexer's IndexerName does not match the one + /// that was used in the interface, then we always need to create a proxy for it. + /// + /// + public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional) + { + ambiguousCandidate = null; + + if (pending_implementations == null) + return null; + + TypeSpec ret_type = method.method.ReturnType; + ParametersCompiled args = method.method.ParameterInfo; + bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod; + MethodSpec m; + + foreach (TypeAndMethods tm in pending_implementations){ + if (!(iType == null || tm.type == iType)) + continue; + + int method_count = tm.methods.Count; + for (int i = 0; i < method_count; i++){ + m = tm.methods [i]; + + if (m == null) + continue; + + if (is_indexer) { + if (!m.IsAccessor || m.Parameters.IsEmpty) + continue; + } else { + if (name.Name != m.Name) + continue; + + if (m.Arity != name.Arity) + continue; + } + + if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args)) + continue; + + if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) { + tm.found[i] = method; + continue; + } + + // + // `need_proxy' is not null when we're implementing an + // interface indexer and this is Clear(One/All) operation. + // + // If `name' is null, then we do a match solely based on the + // signature and not on the name (this is done in the Lookup + // for an interface indexer). + // + if (op != Operation.Lookup) { + if (m.IsAccessor != method.method.IsAccessor) + continue; + + // If `t != null', then this is an explicitly interface + // implementation and we can always clear the method. + // `need_proxy' is not null if we're implementing an + // interface indexer. In this case, we need to create + // a proxy if the implementation's IndexerName doesn't + // match the IndexerName in the interface. + if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison + tm.need_proxy[i] = method.method.Spec; + } else { + tm.methods[i] = null; + } + } else { + tm.found [i] = method; + optional = tm.optional; + } + + if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) { + ambiguousCandidate = m; + continue; + } + + // + // Lookups and ClearOne return + // + if (op != Operation.ClearAll) + return m; + } + + // If a specific type was requested, we can stop now. + if (tm.type == iType) + break; + } + + m = ambiguousCandidate; + ambiguousCandidate = null; + return m; + } + + /// + /// C# allows this kind of scenarios: + /// interface I { void M (); } + /// class X { public void M (); } + /// class Y : X, I { } + /// + /// For that case, we create an explicit implementation function + /// I.M in Y. + /// + void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method) + { + // TODO: Handle nested iface names + string proxy_name; + var ns = iface.MemberDefinition.Namespace; + if (string.IsNullOrEmpty (ns)) + proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name; + else + proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name; + + var param = iface_method.Parameters; + + MethodBuilder proxy = container.TypeBuilder.DefineMethod ( + proxy_name, + MethodAttributes.Private | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.CheckAccessOnOverride | + MethodAttributes.Virtual | MethodAttributes.Final, + CallingConventions.Standard | CallingConventions.HasThis, + base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ()); + + if (iface_method.IsGeneric) { + var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray (); + proxy.DefineGenericParameters (gnames); + } + + for (int i = 0; i < param.Count; i++) { + string name = param.FixedParameters [i].Name; + ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags); + proxy.DefineParameter (i + 1, attr, name); + } + + int top = param.Count; + var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null, null); + ec.EmitThis (); + // TODO: GetAllParametersArguments + for (int i = 0; i < top; i++) + ec.EmitArgumentLoad (i); + + ec.Emit (OpCodes.Call, base_method); + ec.Emit (OpCodes.Ret); + + container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ()); + } + + /// + /// This function tells whether one of our base classes implements + /// the given method (which turns out, it is valid to have an interface + /// implementation in a base + /// + bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method) + { + base_method = null; + var base_type = container.BaseType; + + // + // Setup filter with no return type to give better error message + // about mismatch at return type when the check bellow rejects them + // + var parameters = mi.Parameters; + MethodSpec close_match = null; + + while (true) { + var candidates = MemberCache.FindMembers (base_type, mi.Name, false); + if (candidates == null) { + base_method = close_match; + return false; + } + + MethodSpec similar_candidate = null; + foreach (var candidate in candidates) { + if (candidate.Kind != MemberKind.Method) + continue; + + if (candidate.Arity != mi.Arity) + continue; + + var candidate_param = ((MethodSpec) candidate).Parameters; + if (!TypeSpecComparer.Override.IsEqual (parameters.Types, candidate_param.Types)) + continue; + + bool modifiers_match = true; + for (int i = 0; i < parameters.Count; ++i) { + // + // First check exact ref/out match + // + if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) + continue; + + modifiers_match = false; + + // + // Different in ref/out only + // + if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { + if (similar_candidate == null) { + if (!candidate.IsPublic) + break; + + if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType)) + break; + + // It's used for ref/out ambiguity overload check + similar_candidate = (MethodSpec) candidate; + } + + continue; + } + + similar_candidate = null; + break; + } + + if (!modifiers_match) + continue; + + // + // From this point the candidate is used for detailed error reporting + // because it's very close match to what we are looking for + // + var m = (MethodSpec) candidate; + + if (!m.IsPublic) { + if (close_match == null) + close_match = m; + + continue; + } + + if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, m.ReturnType)) { + if (close_match == null) + close_match = m; + + continue; + } + + base_method = m; + + if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, m, mi)) { + return true; + } + } + + if (base_method != null) { + if (similar_candidate != null) { + Report.SymbolRelatedToPreviousError (similar_candidate); + Report.SymbolRelatedToPreviousError (mi); + Report.SymbolRelatedToPreviousError (container); + Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location, + "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'", + mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ()); + } + + break; + } + + base_type = candidates[0].DeclaringType.BaseType; + if (base_type == null) { + base_method = close_match; + return false; + } + } + + if (!base_method.IsVirtual) { +#if STATIC + var base_builder = base_method.GetMetaInfo () as MethodBuilder; + if (base_builder != null) { + // + // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can + // be done for all methods from compiled assembly + // + base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot); + return true; + } +#endif + DefineProxy (iface_type, base_method, mi); + } + + return true; + } + + /// + /// Verifies that any pending abstract methods or interface methods + /// were implemented. + /// + public bool VerifyPendingMethods () + { + int top = pending_implementations.Length; + bool errors = false; + int i; + + for (i = 0; i < top; i++){ + TypeSpec type = pending_implementations [i].type; + + bool base_implements_type = type.IsInterface && + container.BaseType != null && + container.BaseType.ImplementsInterface (type, false); + + for (int j = 0; j < pending_implementations [i].methods.Count; ++j) { + var mi = pending_implementations[i].methods[j]; + if (mi == null) + continue; + + if (type.IsInterface){ + var need_proxy = + pending_implementations [i].need_proxy [j]; + + if (need_proxy != null) { + DefineProxy (type, need_proxy, mi); + continue; + } + + if (pending_implementations [i].optional) + continue; + + MethodSpec candidate; + if (base_implements_type || BaseImplements (type, mi, out candidate)) + continue; + + if (candidate == null) { + MethodData md = pending_implementations [i].found [j]; + if (md != null) + candidate = md.method.Spec; + } + + Report.SymbolRelatedToPreviousError (mi); + if (candidate != null) { + Report.SymbolRelatedToPreviousError (candidate); + if (candidate.IsStatic) { + Report.Error (736, container.Location, + "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static", + container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ()); + } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) { + Report.Error (737, container.Location, + "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public", + container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ()); + } else { + Report.Error (738, container.Location, + "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'", + container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (), + candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ()); + } + } else { + Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'", + container.GetSignatureForError (), mi.GetSignatureForError ()); + } + } else { + Report.SymbolRelatedToPreviousError (mi); + Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'", + container.GetSignatureForError (), mi.GetSignatureForError ()); + } + errors = true; + } + } + return errors; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/property.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/property.cs new file mode 100644 index 000000000..979a8e83f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/property.cs @@ -0,0 +1,1807 @@ +// +// property.cs: Property based handlers +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Text; +using Mono.CompilerServices.SymbolWriter; + +#if NET_2_1 +using XmlElement = System.Object; +#endif + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // It is used as a base class for all property based members + // This includes properties, indexers, and events + public abstract class PropertyBasedMember : InterfaceMemberBase + { + protected PropertyBasedMember (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, Attributes attrs) + : base (parent, type, mod, allowed_mod, name, attrs) + { + } + + protected void CheckReservedNameConflict (string prefix, MethodSpec accessor) + { + string name; + AParametersCollection parameters; + if (accessor != null) { + name = accessor.Name; + parameters = accessor.Parameters; + } else { + name = prefix + ShortName; + if (IsExplicitImpl) + name = MemberName.Left + "." + name; + + if (this is Indexer) { + parameters = ((Indexer) this).ParameterInfo; + if (prefix[0] == 's') { + var data = new IParameterData[parameters.Count + 1]; + Array.Copy (parameters.FixedParameters, data, data.Length - 1); + data[data.Length - 1] = new ParameterData ("value", Parameter.Modifier.NONE); + var types = new TypeSpec[data.Length]; + Array.Copy (parameters.Types, types, data.Length - 1); + types[data.Length - 1] = member_type; + + parameters = new ParametersImported (data, types, false); + } + } else { + if (prefix[0] == 's') + parameters = ParametersCompiled.CreateFullyResolved (new[] { member_type }); + else + parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + } + + var conflict = MemberCache.FindMember (Parent.Definition, + new MemberFilter (name, 0, MemberKind.Method, parameters, null), + BindingRestriction.DeclaredOnly | BindingRestriction.NoAccessors); + + if (conflict != null) { + Report.SymbolRelatedToPreviousError (conflict); + Report.Error (82, Location, "A member `{0}' is already reserved", conflict.GetSignatureForError ()); + } + } + + public abstract void PrepareEmit (); + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if (!MemberType.IsCLSCompliant ()) { + Report.Warning (3003, 1, Location, "Type of `{0}' is not CLS-compliant", + GetSignatureForError ()); + } + return true; + } + + } + + public class PropertySpec : MemberSpec, IInterfaceMemberSpec + { + PropertyInfo info; + TypeSpec memberType; + MethodSpec set, get; + + public PropertySpec (MemberKind kind, TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, PropertyInfo info, Modifiers modifiers) + : base (kind, declaringType, definition, modifiers) + { + this.info = info; + this.memberType = memberType; + } + + #region Properties + + public MethodSpec Get { + get { + return get; + } + set { + get = value; + get.IsAccessor = true; + } + } + + public MethodSpec Set { + get { + return set; + } + set { + set = value; + set.IsAccessor = true; + } + } + + public bool HasDifferentAccessibility { + get { + return HasGet && HasSet && + (Get.Modifiers & Modifiers.AccessibilityMask) != (Set.Modifiers & Modifiers.AccessibilityMask); + } + } + + public bool HasGet { + get { + return Get != null; + } + } + + public bool HasSet { + get { + return Set != null; + } + } + + public PropertyInfo MetaInfo { + get { + if ((state & StateFlags.PendingMetaInflate) != 0) + throw new NotSupportedException (); + + return info; + } + } + + public TypeSpec MemberType { + get { + return memberType; + } + } + + #endregion + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var ps = (PropertySpec) base.InflateMember (inflator); + ps.memberType = inflator.Inflate (memberType); + return ps; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + return memberType.ResolveMissingDependencies (this); + } + } + + // + // Properties and Indexers both generate PropertyBuilders, we use this to share + // their common bits. + // + abstract public class PropertyBase : PropertyBasedMember { + + public class GetMethod : PropertyMethod + { + static readonly string[] attribute_targets = new string [] { "method", "return" }; + + internal const string Prefix = "get_"; + + public GetMethod (PropertyBase method, Modifiers modifiers, Attributes attrs, Location loc) + : base (method, Prefix, modifiers, attrs, loc) + { + } + + public override void Define (TypeContainer parent) + { + base.Define (parent); + + Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, ModFlags); + + method_data = new MethodData (method, ModFlags, flags, this); + + method_data.Define (parent.PartialContainer, method.GetFullName (MemberName)); + } + + public override TypeSpec ReturnType { + get { + return method.MemberType; + } + } + + public override ParametersCompiled ParameterInfo { + get { + return ParametersCompiled.EmptyReadOnlyParameters; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } + + public class SetMethod : PropertyMethod { + + static readonly string[] attribute_targets = new string[] { "method", "param", "return" }; + + internal const string Prefix = "set_"; + + protected ParametersCompiled parameters; + + public SetMethod (PropertyBase method, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc) + : base (method, Prefix, modifiers, attrs, loc) + { + this.parameters = parameters; + } + + protected override void ApplyToExtraTarget (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Parameter) { + parameters[0].ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyToExtraTarget (a, ctor, cdata, pa); + } + + public override ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + + public override void Define (TypeContainer parent) + { + parameters.Resolve (this); + + base.Define (parent); + + Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, ModFlags); + + method_data = new MethodData (method, ModFlags, flags, this); + + method_data.Define (parent.PartialContainer, method.GetFullName (MemberName)); + } + + public override TypeSpec ReturnType { + get { + return Parent.Compiler.BuiltinTypes.Void; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } + + static readonly string[] attribute_targets = new string[] { "property" }; + + public abstract class PropertyMethod : AbstractPropertyEventMethod + { + const Modifiers AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE; + + protected readonly PropertyBase method; + protected MethodAttributes flags; + + public PropertyMethod (PropertyBase method, string prefix, Modifiers modifiers, Attributes attrs, Location loc) + : base (method, prefix, attrs, loc) + { + this.method = method; + this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, modifiers, 0, loc, Report); + this.ModFlags |= (method.ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE)); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.MethodImpl) { + method.is_external_implementation = a.IsInternalCall (); + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Method; + } + } + + public override bool IsClsComplianceRequired () + { + return method.IsClsComplianceRequired (); + } + + public virtual void Define (TypeContainer parent) + { + var container = parent.PartialContainer; + + // + // Check for custom access modifier + // + if ((ModFlags & Modifiers.AccessibilityMask) == 0) { + ModFlags |= method.ModFlags; + flags = method.flags; + } else { + if (container.Kind == MemberKind.Interface) + Report.Error (275, Location, "`{0}': accessibility modifiers may not be used on accessors in an interface", + GetSignatureForError ()); + else if ((method.ModFlags & Modifiers.ABSTRACT) != 0 && (ModFlags & Modifiers.PRIVATE) != 0) { + Report.Error (442, Location, "`{0}': abstract properties cannot have private accessors", GetSignatureForError ()); + } + + CheckModifiers (ModFlags); + ModFlags |= (method.ModFlags & (~Modifiers.AccessibilityMask)); + ModFlags |= Modifiers.PROPERTY_CUSTOM; + flags = ModifiersExtensions.MethodAttr (ModFlags); + flags |= (method.flags & (~MethodAttributes.MemberAccessMask)); + } + + CheckAbstractAndExtern (block != null); + CheckProtectedModifier (); + + if (block != null) { + if (block.IsIterator) + Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags); + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + } + } + + public bool HasCustomAccessModifier { + get { + return (ModFlags & Modifiers.PROPERTY_CUSTOM) != 0; + } + } + + public PropertyBase Property { + get { + return method; + } + } + + public override ObsoleteAttribute GetAttributeObsolete () + { + return method.GetAttributeObsolete (); + } + + public override string GetSignatureForError() + { + return method.GetSignatureForError () + "." + prefix.Substring (0, 3); + } + + void CheckModifiers (Modifiers modflags) + { + if (!ModifiersExtensions.IsRestrictedModifier (modflags & Modifiers.AccessibilityMask, method.ModFlags & Modifiers.AccessibilityMask)) { + Report.Error (273, Location, + "The accessibility modifier of the `{0}' accessor must be more restrictive than the modifier of the property or indexer `{1}'", + GetSignatureForError (), method.GetSignatureForError ()); + } + } + } + + PropertyMethod get, set, first; + PropertyBuilder PropertyBuilder; + + protected PropertyBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, Modifiers allowed_mod, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, allowed_mod, name, attrs) + { + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Property; + } + } + + public PropertyMethod AccessorFirst { + get { + return first; + } + } + + public PropertyMethod AccessorSecond { + get { + return first == get ? set : get; + } + } + + public override Variance ExpectedMemberTypeVariance { + get { + return (get != null && set != null) ? + Variance.None : set == null ? + Variance.Covariant : + Variance.Contravariant; + } + } + + public PropertyMethod Get { + get { + return get; + } + set { + get = value; + if (first == null) + first = value; + + Parent.AddNameToContainer (get, get.MemberName.Basename); + } + } + + public PropertyMethod Set { + get { + return set; + } + set { + set = value; + if (first == null) + first = value; + + Parent.AddNameToContainer (set, set.MemberName.Basename); + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + #endregion + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.HasSecurityAttribute) { + a.Error_InvalidSecurityParent (); + return; + } + + if (a.Type == pa.Dynamic) { + a.Error_MisusedDynamicAttribute (); + return; + } + + PropertyBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + void CheckMissingAccessor (MemberKind kind, ParametersCompiled parameters, bool get) + { + if (IsExplicitImpl) { + MemberFilter filter; + if (kind == MemberKind.Indexer) + filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, kind, parameters, null); + else + filter = new MemberFilter (MemberName.Name, 0, kind, null, null); + + var implementing = MemberCache.FindMember (InterfaceType, filter, BindingRestriction.DeclaredOnly) as PropertySpec; + + if (implementing == null) + return; + + var accessor = get ? implementing.Get : implementing.Set; + if (accessor != null) { + Report.SymbolRelatedToPreviousError (accessor); + Report.Error (551, Location, "Explicit interface implementation `{0}' is missing accessor `{1}'", + GetSignatureForError (), accessor.GetSignatureForError ()); + } + } + } + + protected override bool CheckOverrideAgainstBase (MemberSpec base_member) + { + var ok = base.CheckOverrideAgainstBase (base_member); + + // + // Check base property accessors conflict + // + var base_prop = (PropertySpec) base_member; + if (Get == null) { + if ((ModFlags & Modifiers.SEALED) != 0 && base_prop.HasGet && !base_prop.Get.IsAccessible (this)) { + // TODO: Should be different error code but csc uses for some reason same + Report.SymbolRelatedToPreviousError (base_prop); + Report.Error (545, Location, + "`{0}': cannot override because `{1}' does not have accessible get accessor", + GetSignatureForError (), base_prop.GetSignatureForError ()); + ok = false; + } + } else { + if (!base_prop.HasGet) { + if (ok) { + Report.SymbolRelatedToPreviousError (base_prop); + Report.Error (545, Get.Location, + "`{0}': cannot override because `{1}' does not have an overridable get accessor", + Get.GetSignatureForError (), base_prop.GetSignatureForError ()); + ok = false; + } + } else if (Get.HasCustomAccessModifier || base_prop.HasDifferentAccessibility) { + if (!CheckAccessModifiers (Get, base_prop.Get)) { + Error_CannotChangeAccessModifiers (Get, base_prop.Get); + ok = false; + } + } + } + + if (Set == null) { + if ((ModFlags & Modifiers.SEALED) != 0 && base_prop.HasSet && !base_prop.Set.IsAccessible (this)) { + // TODO: Should be different error code but csc uses for some reason same + Report.SymbolRelatedToPreviousError (base_prop); + Report.Error (546, Location, + "`{0}': cannot override because `{1}' does not have accessible set accessor", + GetSignatureForError (), base_prop.GetSignatureForError ()); + ok = false; + } + } else { + if (!base_prop.HasSet) { + if (ok) { + Report.SymbolRelatedToPreviousError (base_prop); + Report.Error (546, Set.Location, + "`{0}': cannot override because `{1}' does not have an overridable set accessor", + Set.GetSignatureForError (), base_prop.GetSignatureForError ()); + ok = false; + } + } else if (Set.HasCustomAccessModifier || base_prop.HasDifferentAccessibility) { + if (!CheckAccessModifiers (Set, base_prop.Set)) { + Error_CannotChangeAccessModifiers (Set, base_prop.Set); + ok = false; + } + } + } + + if ((Set == null || !Set.HasCustomAccessModifier) && (Get == null || !Get.HasCustomAccessModifier)) { + if (!CheckAccessModifiers (this, base_prop)) { + Error_CannotChangeAccessModifiers (this, base_prop); + ok = false; + } + } + + return ok; + } + + protected override void DoMemberTypeDependentChecks () + { + base.DoMemberTypeDependentChecks (); + + IsTypePermitted (); + + if (MemberType.IsStatic) + Error_StaticReturnType (); + } + + protected override void DoMemberTypeIndependentChecks () + { + base.DoMemberTypeIndependentChecks (); + + // + // Accessors modifiers check + // + if (AccessorSecond != null) { + if ((Get.ModFlags & Modifiers.AccessibilityMask) != 0 && (Set.ModFlags & Modifiers.AccessibilityMask) != 0) { + Report.Error (274, Location, "`{0}': Cannot specify accessibility modifiers for both accessors of the property or indexer", + GetSignatureForError ()); + } + } else if ((ModFlags & Modifiers.OVERRIDE) == 0 && + (Get == null && (Set.ModFlags & Modifiers.AccessibilityMask) != 0) || + (Set == null && (Get.ModFlags & Modifiers.AccessibilityMask) != 0)) { + Report.Error (276, Location, + "`{0}': accessibility modifiers on accessors may only be used if the property or indexer has both a get and a set accessor", + GetSignatureForError ()); + } + } + + protected bool DefineAccessors () + { + first.Define (Parent); + if (AccessorSecond != null) + AccessorSecond.Define (Parent); + + return true; + } + + protected void DefineBuilders (MemberKind kind, ParametersCompiled parameters) + { + PropertyBuilder = Parent.TypeBuilder.DefineProperty ( + GetFullName (MemberName), PropertyAttributes.None, +#if !BOOTSTRAP_BASIC // Requires trunk version mscorlib + IsStatic ? 0 : CallingConventions.HasThis, +#endif + MemberType.GetMetaInfo (), null, null, + parameters.GetMetaInfo (), null, null); + + PropertySpec spec; + if (kind == MemberKind.Indexer) + spec = new IndexerSpec (Parent.Definition, this, MemberType, parameters, PropertyBuilder, ModFlags); + else + spec = new PropertySpec (kind, Parent.Definition, this, MemberType, PropertyBuilder, ModFlags); + + if (Get != null) { + spec.Get = Get.Spec; + Parent.MemberCache.AddMember (this, Get.Spec.Name, Get.Spec); + } else { + CheckMissingAccessor (kind, parameters, true); + } + + if (Set != null) { + spec.Set = Set.Spec; + Parent.MemberCache.AddMember (this, Set.Spec.Name, Set.Spec); + } else { + CheckMissingAccessor (kind, parameters, false); + } + + Parent.MemberCache.AddMember (this, PropertyBuilder.Name, spec); + } + + public override void Emit () + { + CheckReservedNameConflict (GetMethod.Prefix, get == null ? null : get.Spec); + CheckReservedNameConflict (SetMethod.Prefix, set == null ? null : set.Spec); + + if (OptAttributes != null) + OptAttributes.Emit (); + + if (member_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (PropertyBuilder); + } else if (member_type.HasDynamicElement) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (PropertyBuilder, member_type, Location); + } + + ConstraintChecker.Check (this, member_type, type_expr.Location); + + first.Emit (Parent); + if (AccessorSecond != null) + AccessorSecond.Emit (Parent); + + base.Emit (); + } + + public override bool IsUsed { + get { + if (IsExplicitImpl) + return true; + + return Get.IsUsed | Set.IsUsed; + } + } + + public override void PrepareEmit () + { + AccessorFirst.PrepareEmit (); + if (AccessorSecond != null) + AccessorSecond.PrepareEmit (); + + if (get != null) { + var method = Get.Spec.GetMetaInfo () as MethodBuilder; + if (method != null) + PropertyBuilder.SetGetMethod (method); + } + + if (set != null) { + var method = Set.Spec.GetMetaInfo () as MethodBuilder; + if (method != null) + PropertyBuilder.SetSetMethod (method); + } + } + + protected override void SetMemberName (MemberName new_name) + { + base.SetMemberName (new_name); + + if (Get != null) + Get.UpdateName (this); + + if (Set != null) + Set.UpdateName (this); + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (get != null) + get.WriteDebugSymbol (file); + + if (set != null) + set.WriteDebugSymbol (file); + } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader { + get { return "P:"; } + } + } + + public class Property : PropertyBase + { + public sealed class BackingField : Field + { + readonly Property property; + const Modifiers DefaultModifiers = Modifiers.BACKING_FIELD | Modifiers.COMPILER_GENERATED | Modifiers.PRIVATE | Modifiers.DEBUGGER_HIDDEN; + + public BackingField (Property p, bool readOnly) + : base (p.Parent, p.type_expr, DefaultModifiers | (p.ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE)), + new MemberName ("<" + p.GetFullName (p.MemberName) + ">k__BackingField", p.Location), null) + { + this.property = p; + if (readOnly) + ModFlags |= Modifiers.READONLY; + } + + public Property OriginalProperty { + get { + return property; + } + } + + public override string GetSignatureForError () + { + return property.GetSignatureForError (); + } + } + + static readonly string[] attribute_target_auto = new string[] { "property", "field" }; + + Field backing_field; + + public Property (TypeDefinition parent, FullNamedExpression type, Modifiers mod, + MemberName name, Attributes attrs) + : base (parent, type, mod, + parent.PartialContainer.Kind == MemberKind.Interface ? AllowedModifiersInterface : + parent.PartialContainer.Kind == MemberKind.Struct ? AllowedModifiersStruct : + AllowedModifiersClass, + name, attrs) + { + } + + public Expression Initializer { get; set; } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Field) { + backing_field.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + void CreateAutomaticProperty () + { + // Create backing field + backing_field = new BackingField (this, Initializer != null && Set == null); + if (!backing_field.Define ()) + return; + + if (Initializer != null) { + backing_field.Initializer = Initializer; + Parent.RegisterFieldForInitialization (backing_field, new FieldInitializer (backing_field, Initializer, Location)); + backing_field.ModFlags |= Modifiers.READONLY; + } + + Parent.PartialContainer.Members.Add (backing_field); + + FieldExpr fe = new FieldExpr (backing_field, Location); + if ((backing_field.ModFlags & Modifiers.STATIC) == 0) + fe.InstanceExpression = new CompilerGeneratedThis (Parent.CurrentType, Location); + + // + // Create get block but we careful with location to + // emit only single sequence point per accessor. This allow + // to set a breakpoint on it even with no user code + // + Get.Block = new ToplevelBlock (Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null); + Return r = new Return (fe, Get.Location); + Get.Block.AddStatement (r); + Get.ModFlags |= Modifiers.COMPILER_GENERATED; + + // Create set block + if (Set != null) { + Set.Block = new ToplevelBlock (Compiler, Set.ParameterInfo, Location.Null); + Assign a = new SimpleAssign (fe, new SimpleName ("value", Location.Null), Location.Null); + Set.Block.AddStatement (new StatementExpression (a, Set.Location)); + Set.ModFlags |= Modifiers.COMPILER_GENERATED; + } + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + flags |= MethodAttributes.HideBySig | MethodAttributes.SpecialName; + + bool auto = AccessorFirst.Block == null && (AccessorSecond == null || AccessorSecond.Block == null) && + (ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN)) == 0; + + if (Initializer != null) { + if (!auto) + Report.Error (8050, Location, "`{0}': Only auto-implemented properties can have initializers", + GetSignatureForError ()); + + if (IsInterface) + Report.Error (8053, Location, "`{0}': Properties inside interfaces cannot have initializers", + GetSignatureForError ()); + + if (Compiler.Settings.Version < LanguageVersion.V_6) + Report.FeatureIsNotAvailable (Compiler, Location, "auto-implemented property initializer"); + } + + if (auto) { + if (Get == null) { + Report.Error (8052, Location, "Auto-implemented property `{0}' must have get accessor", + GetSignatureForError ()); + return false; + } + + if (Initializer == null && AccessorSecond == null) { + Report.Error (8051, Location, "Auto-implemented property `{0}' must have set accessor or initializer", + GetSignatureForError ()); + } + + if (Compiler.Settings.Version < LanguageVersion.V_3 && Initializer == null) + Report.FeatureIsNotAvailable (Compiler, Location, "auto-implemented properties"); + + CreateAutomaticProperty (); + } + + if (!DefineAccessors ()) + return false; + + if (AccessorSecond == null) { + PropertyMethod pm; + if (AccessorFirst is GetMethod) + pm = new SetMethod (this, 0, ParametersCompiled.EmptyReadOnlyParameters, null, Location); + else + pm = new GetMethod (this, 0, null, Location); + + Parent.AddNameToContainer (pm, pm.MemberName.Basename); + } + + if (!CheckBase ()) + return false; + + DefineBuilders (MemberKind.Property, ParametersCompiled.EmptyReadOnlyParameters); + return true; + } + + public override void Emit () + { + if ((AccessorFirst.ModFlags & (Modifiers.STATIC | Modifiers.COMPILER_GENERATED)) == Modifiers.COMPILER_GENERATED && Parent.PartialContainer.HasExplicitLayout) { + Report.Error (842, Location, + "Automatically implemented property `{0}' cannot be used inside a type with an explicit StructLayout attribute", + GetSignatureForError ()); + } + + base.Emit (); + } + + public override string[] ValidAttributeTargets { + get { + return Get != null && ((Get.ModFlags & Modifiers.COMPILER_GENERATED) != 0) ? + attribute_target_auto : base.ValidAttributeTargets; + } + } + } + + /// + /// For case when event is declared like property (with add and remove accessors). + /// + public class EventProperty: Event { + public abstract class AEventPropertyAccessor : AEventAccessor + { + protected AEventPropertyAccessor (EventProperty method, string prefix, Attributes attrs, Location loc) + : base (method, prefix, attrs, loc) + { + } + + public override void Define (TypeContainer ds) + { + CheckAbstractAndExtern (block != null); + base.Define (ds); + } + + public override string GetSignatureForError () + { + return method.GetSignatureForError () + "." + prefix.Substring (0, prefix.Length - 1); + } + } + + public sealed class AddDelegateMethod: AEventPropertyAccessor + { + public AddDelegateMethod (EventProperty method, Attributes attrs, Location loc) + : base (method, AddPrefix, attrs, loc) + { + } + } + + public sealed class RemoveDelegateMethod: AEventPropertyAccessor + { + public RemoveDelegateMethod (EventProperty method, Attributes attrs, Location loc) + : base (method, RemovePrefix, attrs, loc) + { + } + } + + static readonly string[] attribute_targets = new string [] { "event" }; + + public EventProperty (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, name, attrs) + { + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override bool Define() + { + if (!base.Define ()) + return false; + + SetIsUsed (); + return true; + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } + + /// + /// Event is declared like field. + /// + public class EventField : Event + { + abstract class EventFieldAccessor : AEventAccessor + { + protected EventFieldAccessor (EventField method, string prefix) + : base (method, prefix, null, method.Location) + { + } + + protected abstract MethodSpec GetOperation (Location loc); + + public override void Emit (TypeDefinition parent) + { + if ((method.ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN)) == 0 && !Compiler.Settings.WriteMetadataOnly) { + block = new ToplevelBlock (Compiler, ParameterInfo, Location) { + IsCompilerGenerated = true + }; + FabricateBodyStatement (); + } + + base.Emit (parent); + } + + void FabricateBodyStatement () + { + // + // Delegate obj1 = backing_field + // do { + // Delegate obj2 = obj1; + // obj1 = Interlocked.CompareExchange (ref backing_field, Delegate.Combine|Remove(obj2, value), obj1); + // } while ((object)obj1 != (object)obj2) + // + + var field_info = ((EventField) method).backing_field; + FieldExpr f_expr = new FieldExpr (field_info, Location); + if (!IsStatic) + f_expr.InstanceExpression = new CompilerGeneratedThis (Parent.CurrentType, Location); + + var obj1 = LocalVariable.CreateCompilerGenerated (field_info.MemberType, block, Location); + var obj2 = LocalVariable.CreateCompilerGenerated (field_info.MemberType, block, Location); + + block.AddStatement (new StatementExpression (new SimpleAssign (new LocalVariableReference (obj1, Location), f_expr))); + + var cond = new BooleanExpression (new Binary (Binary.Operator.Inequality, + new Cast (new TypeExpression (Module.Compiler.BuiltinTypes.Object, Location), new LocalVariableReference (obj1, Location), Location), + new Cast (new TypeExpression (Module.Compiler.BuiltinTypes.Object, Location), new LocalVariableReference (obj2, Location), Location))); + + var body = new ExplicitBlock (block, Location, Location); + block.AddStatement (new Do (body, cond, Location, Location)); + + body.AddStatement (new StatementExpression ( + new SimpleAssign (new LocalVariableReference (obj2, Location), new LocalVariableReference (obj1, Location)))); + + var args_oper = new Arguments (2); + args_oper.Add (new Argument (new LocalVariableReference (obj2, Location))); + args_oper.Add (new Argument (block.GetParameterReference (0, Location))); + + var op_method = GetOperation (Location); + + var args = new Arguments (3); + args.Add (new Argument (f_expr, Argument.AType.Ref)); + args.Add (new Argument (new Cast ( + new TypeExpression (field_info.MemberType, Location), + new Invocation (MethodGroupExpr.CreatePredefined (op_method, op_method.DeclaringType, Location), args_oper), + Location))); + args.Add (new Argument (new LocalVariableReference (obj1, Location))); + + var cas = Module.PredefinedMembers.InterlockedCompareExchange_T.Get (); + if (cas == null) { + if (Module.PredefinedMembers.MonitorEnter_v4.Get () != null || Module.PredefinedMembers.MonitorEnter.Get () != null) { + // Workaround for cripled (e.g. microframework) mscorlib without CompareExchange + body.AddStatement (new Lock ( + block.GetParameterReference (0, Location), + new StatementExpression (new SimpleAssign ( + f_expr, args [1].Expr, Location), Location), Location)); + } else { + Module.PredefinedMembers.InterlockedCompareExchange_T.Resolve (Location); + } + } else { + body.AddStatement (new StatementExpression (new SimpleAssign ( + new LocalVariableReference (obj1, Location), + new Invocation (MethodGroupExpr.CreatePredefined (cas, cas.DeclaringType, Location), args)))); + } + } + } + + sealed class AddDelegateMethod: EventFieldAccessor + { + public AddDelegateMethod (EventField method): + base (method, AddPrefix) + { + } + + protected override MethodSpec GetOperation (Location loc) + { + return Module.PredefinedMembers.DelegateCombine.Resolve (loc); + } + } + + sealed class RemoveDelegateMethod: EventFieldAccessor + { + public RemoveDelegateMethod (EventField method): + base (method, RemovePrefix) + { + } + + protected override MethodSpec GetOperation (Location loc) + { + return Module.PredefinedMembers.DelegateRemove.Resolve (loc); + } + } + + + static readonly string[] attribute_targets = new string [] { "event", "field", "method" }; + static readonly string[] attribute_targets_interface = new string[] { "event", "method" }; + + Expression initializer; + Field backing_field; + List declarators; + + public EventField (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, name, attrs) + { + Add = new AddDelegateMethod (this); + Remove = new RemoveDelegateMethod (this); + } + + #region Properties + + public List Declarators { + get { + return this.declarators; + } + } + + bool HasBackingField { + get { + return !IsInterface && (ModFlags & Modifiers.ABSTRACT) == 0; + } + } + + public Expression Initializer { + get { + return initializer; + } + set { + initializer = value; + } + } + + public override string[] ValidAttributeTargets { + get { + return HasBackingField ? attribute_targets : attribute_targets_interface; + } + } + + #endregion + + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public void AddDeclarator (FieldDeclarator declarator) + { + if (declarators == null) + declarators = new List (2); + + declarators.Add (declarator); + + Parent.AddNameToContainer (this, declarator.Name.Value); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Field) { + backing_field.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + if (a.Target == AttributeTargets.Method) { + int errors = Report.Errors; + Add.ApplyAttributeBuilder (a, ctor, cdata, pa); + if (errors == Report.Errors) + Remove.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override bool Define() + { + var mod_flags_src = ModFlags; + + if (!base.Define ()) + return false; + + if (declarators != null) { + if ((mod_flags_src & Modifiers.DEFAULT_ACCESS_MODIFIER) != 0) + mod_flags_src &= ~(Modifiers.AccessibilityMask | Modifiers.DEFAULT_ACCESS_MODIFIER); + + var t = new TypeExpression (MemberType, TypeExpression.Location); + foreach (var d in declarators) { + var ef = new EventField (Parent, t, mod_flags_src, new MemberName (d.Name.Value, d.Name.Location), OptAttributes); + + if (d.Initializer != null) + ef.initializer = d.Initializer; + + ef.Define (); + Parent.PartialContainer.Members.Add (ef); + } + } + + if (!HasBackingField) { + SetIsUsed (); + return true; + } + + backing_field = new Field (Parent, + new TypeExpression (MemberType, Location), + Modifiers.BACKING_FIELD | Modifiers.COMPILER_GENERATED | Modifiers.PRIVATE | (ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE)), + MemberName, null); + + Parent.PartialContainer.Members.Add (backing_field); + backing_field.Initializer = Initializer; + backing_field.ModFlags &= ~Modifiers.COMPILER_GENERATED; + + // Call define because we passed fields definition + backing_field.Define (); + + // Set backing field for event fields + spec.BackingField = backing_field.Spec; + + return true; + } + } + + public abstract class Event : PropertyBasedMember + { + public abstract class AEventAccessor : AbstractPropertyEventMethod + { + protected readonly Event method; + readonly ParametersCompiled parameters; + + static readonly string[] attribute_targets = new string [] { "method", "param", "return" }; + + public const string AddPrefix = "add_"; + public const string RemovePrefix = "remove_"; + + protected AEventAccessor (Event method, string prefix, Attributes attrs, Location loc) + : base (method, prefix, attrs, loc) + { + this.method = method; + this.ModFlags = method.ModFlags; + this.parameters = ParametersCompiled.CreateImplicitParameter (method.TypeExpression, loc); + } + + public bool IsInterfaceImplementation { + get { return method_data.implementing != null; } + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.MethodImpl) { + method.is_external_implementation = a.IsInternalCall (); + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + protected override void ApplyToExtraTarget (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Parameter) { + parameters[0].ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyToExtraTarget (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Method; + } + } + + public override bool IsClsComplianceRequired () + { + return method.IsClsComplianceRequired (); + } + + public virtual void Define (TypeContainer parent) + { + // Fill in already resolved event type to speed things up and + // avoid confusing duplicate errors + ((Parameter) parameters.FixedParameters[0]).Type = method.member_type; + parameters.Types = new TypeSpec[] { method.member_type }; + + method_data = new MethodData (method, method.ModFlags, + method.flags | MethodAttributes.HideBySig | MethodAttributes.SpecialName, this); + + if (!method_data.Define (parent.PartialContainer, method.GetFullName (MemberName))) + return; + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + + Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, method.ModFlags); + Spec.IsAccessor = true; + } + + public override TypeSpec ReturnType { + get { + return Parent.Compiler.BuiltinTypes.Void; + } + } + + public override ObsoleteAttribute GetAttributeObsolete () + { + return method.GetAttributeObsolete (); + } + + public MethodData MethodData { + get { + return method_data; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + public override ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + } + + AEventAccessor add, remove; + EventBuilder EventBuilder; + protected EventSpec spec; + + protected Event (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, + parent.PartialContainer.Kind == MemberKind.Interface ? AllowedModifiersInterface : + parent.PartialContainer.Kind == MemberKind.Struct ? AllowedModifiersStruct : + AllowedModifiersClass, + name, attrs) + { + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Event; + } + } + + public AEventAccessor Add { + get { + return this.add; + } + set { + add = value; + Parent.AddNameToContainer (value, value.MemberName.Basename); + } + } + + public override Variance ExpectedMemberTypeVariance { + get { + return Variance.Contravariant; + } + } + + public AEventAccessor Remove { + get { + return this.remove; + } + set { + remove = value; + Parent.AddNameToContainer (value, value.MemberName.Basename); + } + } + #endregion + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if ((a.HasSecurityAttribute)) { + a.Error_InvalidSecurityParent (); + return; + } + + EventBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + protected override bool CheckOverrideAgainstBase (MemberSpec base_member) + { + var ok = base.CheckOverrideAgainstBase (base_member); + + if (!CheckAccessModifiers (this, base_member)) { + Error_CannotChangeAccessModifiers (this, base_member); + ok = false; + } + + return ok; + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!MemberType.IsDelegate) { + Report.Error (66, Location, "`{0}': event must be of a delegate type", GetSignatureForError ()); + } + + if (!CheckBase ()) + return false; + + // + // Now define the accessors + // + add.Define (Parent); + remove.Define (Parent); + + EventBuilder = Parent.TypeBuilder.DefineEvent (GetFullName (MemberName), EventAttributes.None, MemberType.GetMetaInfo ()); + + spec = new EventSpec (Parent.Definition, this, MemberType, ModFlags, Add.Spec, remove.Spec); + + Parent.MemberCache.AddMember (this, GetFullName (MemberName), spec); + Parent.MemberCache.AddMember (this, Add.Spec.Name, Add.Spec); + Parent.MemberCache.AddMember (this, Remove.Spec.Name, remove.Spec); + + return true; + } + + public override void Emit () + { + CheckReservedNameConflict (null, add.Spec); + CheckReservedNameConflict (null, remove.Spec); + + if (OptAttributes != null) { + OptAttributes.Emit (); + } + + ConstraintChecker.Check (this, member_type, type_expr.Location); + + Add.Emit (Parent); + Remove.Emit (Parent); + + base.Emit (); + } + + public override void PrepareEmit () + { + add.PrepareEmit (); + remove.PrepareEmit (); + + EventBuilder.SetAddOnMethod (add.MethodData.MethodBuilder); + EventBuilder.SetRemoveOnMethod (remove.MethodData.MethodBuilder); + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + add.WriteDebugSymbol (file); + remove.WriteDebugSymbol (file); + } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader { + get { return "E:"; } + } + } + + public class EventSpec : MemberSpec, IInterfaceMemberSpec + { + MethodSpec add, remove; + FieldSpec backing_field; + + public EventSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec eventType, Modifiers modifiers, MethodSpec add, MethodSpec remove) + : base (MemberKind.Event, declaringType, definition, modifiers) + { + this.AccessorAdd = add; + this.AccessorRemove = remove; + this.MemberType = eventType; + } + + #region Properties + + public MethodSpec AccessorAdd { + get { + return add; + } + set { + add = value; + } + } + + public MethodSpec AccessorRemove { + get { + return remove; + } + set { + remove = value; + } + } + + public FieldSpec BackingField { + get { + return backing_field; + } + set { + backing_field = value; + } + } + + public TypeSpec MemberType { get; private set; } + + #endregion + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var es = (EventSpec) base.InflateMember (inflator); + es.MemberType = inflator.Inflate (MemberType); + + if (backing_field != null) + es.backing_field = (FieldSpec) backing_field.InflateMember (inflator); + + return es; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + return MemberType.ResolveMissingDependencies (this); + } + } + + public class Indexer : PropertyBase, IParametersMember + { + public class GetIndexerMethod : GetMethod, IParametersMember + { + ParametersCompiled parameters; + + public GetIndexerMethod (PropertyBase property, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc) + : base (property, modifiers, attrs, loc) + { + this.parameters = parameters; + } + + public override void Define (TypeContainer parent) + { + // Disable reporting, parameters are resolved twice + Report.DisableReporting (); + try { + parameters.Resolve (this); + } finally { + Report.EnableReporting (); + } + + base.Define (parent); + } + + public override ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + + #region IParametersMember Members + + AParametersCollection IParametersMember.Parameters { + get { + return parameters; + } + } + + TypeSpec IInterfaceMemberSpec.MemberType { + get { + return ReturnType; + } + } + + #endregion + } + + public class SetIndexerMethod : SetMethod, IParametersMember + { + public SetIndexerMethod (PropertyBase property, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc) + : base (property, modifiers, parameters, attrs, loc) + { + } + + #region IParametersMember Members + + AParametersCollection IParametersMember.Parameters { + get { + return parameters; + } + } + + TypeSpec IInterfaceMemberSpec.MemberType { + get { + return ReturnType; + } + } + + #endregion + } + + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.VIRTUAL | + Modifiers.SEALED | + Modifiers.OVERRIDE | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.ABSTRACT; + + const Modifiers AllowedInterfaceModifiers = + Modifiers.NEW; + + readonly ParametersCompiled parameters; + + public Indexer (TypeDefinition parent, FullNamedExpression type, MemberName name, Modifiers mod, ParametersCompiled parameters, Attributes attrs) + : base (parent, type, mod, + parent.PartialContainer.Kind == MemberKind.Interface ? AllowedInterfaceModifiers : AllowedModifiers, + name, attrs) + { + this.parameters = parameters; + } + + #region Properties + + AParametersCollection IParametersMember.Parameters { + get { + return parameters; + } + } + + public ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + + #endregion + + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.IndexerName) { + // Attribute was copied to container + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + protected override bool CheckForDuplications () + { + return Parent.MemberCache.CheckExistingMembersOverloads (this, parameters); + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!DefineParameters (parameters)) + return false; + + if (OptAttributes != null) { + Attribute indexer_attr = OptAttributes.Search (Module.PredefinedAttributes.IndexerName); + if (indexer_attr != null) { + var compiling = indexer_attr.Type.MemberDefinition as TypeContainer; + if (compiling != null) + compiling.Define (); + + if (IsExplicitImpl) { + Report.Error (415, indexer_attr.Location, + "The `{0}' attribute is valid only on an indexer that is not an explicit interface member declaration", + indexer_attr.Type.GetSignatureForError ()); + } else if ((ModFlags & Modifiers.OVERRIDE) != 0) { + Report.Error (609, indexer_attr.Location, + "Cannot set the `IndexerName' attribute on an indexer marked override"); + } else { + string name = indexer_attr.GetIndexerAttributeValue (); + + if (!string.IsNullOrEmpty (name)) { + SetMemberName (new MemberName (MemberName.Left, name, Location)); + } + } + } + } + + if (InterfaceType != null) { + string base_IndexerName = InterfaceType.MemberDefinition.GetAttributeDefaultMember (); + if (base_IndexerName != ShortName) { + SetMemberName (new MemberName (MemberName.Left, base_IndexerName, new TypeExpression (InterfaceType, Location), Location)); + } + } + + Parent.AddNameToContainer (this, MemberName.Basename); + + flags |= MethodAttributes.HideBySig | MethodAttributes.SpecialName; + + if (!DefineAccessors ()) + return false; + + if (!CheckBase ()) + return false; + + DefineBuilders (MemberKind.Indexer, parameters); + return true; + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + if (overload is Indexer) { + caching_flags |= Flags.MethodOverloadsExist; + return true; + } + + return base.EnableOverloadChecks (overload); + } + + public override void Emit () + { + parameters.CheckConstraints (this); + + base.Emit (); + } + + public override string GetSignatureForError () + { + StringBuilder sb = new StringBuilder (Parent.GetSignatureForError ()); + if (MemberName.ExplicitInterface != null) { + sb.Append ("."); + sb.Append (MemberName.ExplicitInterface.GetSignatureForError ()); + } + + sb.Append (".this"); + sb.Append (parameters.GetSignatureForError ("[", "]", parameters.Count)); + return sb.ToString (); + } + + public override string GetSignatureForDocumentation () + { + return base.GetSignatureForDocumentation () + parameters.GetSignatureForDocumentation (); + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + parameters.VerifyClsCompliance (this); + return true; + } + } + + public class IndexerSpec : PropertySpec, IParametersMember + { + AParametersCollection parameters; + + public IndexerSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, AParametersCollection parameters, PropertyInfo info, Modifiers modifiers) + : base (MemberKind.Indexer, declaringType, definition, memberType, info, modifiers) + { + this.parameters = parameters; + } + + #region Properties + public AParametersCollection Parameters { + get { + return parameters; + } + } + #endregion + + public override string GetSignatureForDocumentation () + { + return base.GetSignatureForDocumentation () + parameters.GetSignatureForDocumentation (); + } + + public override string GetSignatureForError () + { + return DeclaringType.GetSignatureForError () + ".this" + parameters.GetSignatureForError ("[", "]", parameters.Count); + } + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var spec = (IndexerSpec) base.InflateMember (inflator); + spec.parameters = parameters.Inflate (inflator); + return spec; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + var missing = base.ResolveMissingDependencies (caller); + + foreach (var pt in parameters.Types) { + var m = pt.GetMissingDependencies (caller); + if (m == null) + continue; + + if (missing == null) + missing = new List (); + + missing.AddRange (m); + } + + return missing; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/reflection.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/reflection.cs new file mode 100644 index 000000000..b233b9439 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/reflection.cs @@ -0,0 +1,550 @@ +// +// reflection.cs: System.Reflection and System.Reflection.Emit specific implementations +// +// Author: Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2009-2010 Novell, Inc. +// +// + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.IO; +using System.Runtime.CompilerServices; +using System.Reflection.Emit; +using System.Security; + +namespace Mono.CSharp +{ +#if STATIC + public class ReflectionImporter + { + public ReflectionImporter (ModuleContainer module, BuiltinTypes builtin) + { + throw new NotSupportedException (); + } + + public void ImportAssembly (Assembly assembly, RootNamespace targetNamespace) + { + throw new NotSupportedException (); + } + + public ImportedModuleDefinition ImportModule (Module module, RootNamespace targetNamespace) + { + throw new NotSupportedException (); + } + + public TypeSpec ImportType (Type type) + { + throw new NotSupportedException (); + } + } +#else + public sealed class ReflectionImporter : MetadataImporter + { + public ReflectionImporter (ModuleContainer module, BuiltinTypes builtin) + : base (module) + { + Initialize (builtin); + } + + public override void AddCompiledType (TypeBuilder builder, TypeSpec spec) + { + } + + protected override MemberKind DetermineKindFromBaseType (Type baseType) + { + if (baseType == typeof (ValueType)) + return MemberKind.Struct; + + if (baseType == typeof (System.Enum)) + return MemberKind.Enum; + + if (baseType == typeof (MulticastDelegate)) + return MemberKind.Delegate; + + return MemberKind.Class; + } + + protected override bool HasVolatileModifier (Type[] modifiers) + { + foreach (var t in modifiers) { + if (t == typeof (IsVolatile)) + return true; + } + + return false; + } + + public void ImportAssembly (Assembly assembly, RootNamespace targetNamespace) + { + // It can be used more than once when importing same assembly + // into 2 or more global aliases + GetAssemblyDefinition (assembly); + + // + // This part tries to simulate loading of top-level + // types only, any missing dependencies are ignores here. + // Full error report is reported later when the type is + // actually used + // + Type[] all_types; + try { + all_types = assembly.GetTypes (); + } catch (ReflectionTypeLoadException e) { + all_types = e.Types; + } + + ImportTypes (all_types, targetNamespace, true); + } + + public ImportedModuleDefinition ImportModule (Module module, RootNamespace targetNamespace) + { + var module_definition = new ImportedModuleDefinition (module); + module_definition.ReadAttributes (); + + Type[] all_types; + try { + all_types = module.GetTypes (); + } catch (ReflectionTypeLoadException e) { + all_types = e.Types; + } + + ImportTypes (all_types, targetNamespace, false); + + return module_definition; + } + + void Initialize (BuiltinTypes builtin) + { + // + // Setup mapping for build-in types to avoid duplication of their definition + // + compiled_types.Add (typeof (object), builtin.Object); + compiled_types.Add (typeof (System.ValueType), builtin.ValueType); + compiled_types.Add (typeof (System.Attribute), builtin.Attribute); + + compiled_types.Add (typeof (int), builtin.Int); + compiled_types.Add (typeof (long), builtin.Long); + compiled_types.Add (typeof (uint), builtin.UInt); + compiled_types.Add (typeof (ulong), builtin.ULong); + compiled_types.Add (typeof (byte), builtin.Byte); + compiled_types.Add (typeof (sbyte), builtin.SByte); + compiled_types.Add (typeof (short), builtin.Short); + compiled_types.Add (typeof (ushort), builtin.UShort); + + compiled_types.Add (typeof (System.Collections.IEnumerator), builtin.IEnumerator); + compiled_types.Add (typeof (System.Collections.IEnumerable), builtin.IEnumerable); + compiled_types.Add (typeof (System.IDisposable), builtin.IDisposable); + + compiled_types.Add (typeof (char), builtin.Char); + compiled_types.Add (typeof (string), builtin.String); + compiled_types.Add (typeof (float), builtin.Float); + compiled_types.Add (typeof (double), builtin.Double); + compiled_types.Add (typeof (decimal), builtin.Decimal); + compiled_types.Add (typeof (bool), builtin.Bool); + compiled_types.Add (typeof (System.IntPtr), builtin.IntPtr); + compiled_types.Add (typeof (System.UIntPtr), builtin.UIntPtr); + + compiled_types.Add (typeof (System.MulticastDelegate), builtin.MulticastDelegate); + compiled_types.Add (typeof (System.Delegate), builtin.Delegate); + compiled_types.Add (typeof (System.Enum), builtin.Enum); + compiled_types.Add (typeof (System.Array), builtin.Array); + compiled_types.Add (typeof (void), builtin.Void); + compiled_types.Add (typeof (System.Type), builtin.Type); + compiled_types.Add (typeof (System.Exception), builtin.Exception); + compiled_types.Add (typeof (System.RuntimeFieldHandle), builtin.RuntimeFieldHandle); + compiled_types.Add (typeof (System.RuntimeTypeHandle), builtin.RuntimeTypeHandle); + } + } + + [System.Runtime.InteropServices.StructLayout (System.Runtime.InteropServices.LayoutKind.Explicit)] + struct SingleConverter + { + [System.Runtime.InteropServices.FieldOffset (0)] + int i; + +#pragma warning disable 414 + [System.Runtime.InteropServices.FieldOffset (0)] + float f; +#pragma warning restore 414 + + public static int SingleToInt32Bits (float v) + { + SingleConverter c = new SingleConverter (); + c.f = v; + return c.i; + } + } + +#endif + + public class AssemblyDefinitionDynamic : AssemblyDefinition + { + // + // In-memory only assembly container + // + public AssemblyDefinitionDynamic (ModuleContainer module, string name) + : base (module, name) + { + } + + // + // Assembly container with file output + // + public AssemblyDefinitionDynamic (ModuleContainer module, string name, string fileName) + : base (module, name, fileName) + { + } + + public Module IncludeModule (string moduleFile) + { + return builder_extra.AddModule (moduleFile); + } + +#if !STATIC + public override ModuleBuilder CreateModuleBuilder () + { + if (file_name == null) + return Builder.DefineDynamicModule (Name, false); + + return base.CreateModuleBuilder (); + } +#endif + // + // Initializes the code generator + // + public bool Create (AppDomain domain, AssemblyBuilderAccess access) + { +#if STATIC || FULL_AOT_RUNTIME + throw new NotSupportedException (); +#else + ResolveAssemblySecurityAttributes (); + var an = CreateAssemblyName (); + + Builder = file_name == null ? + domain.DefineDynamicAssembly (an, access) : + domain.DefineDynamicAssembly (an, access, Dirname (file_name)); + + module.Create (this, CreateModuleBuilder ()); + builder_extra = new AssemblyBuilderMonoSpecific (Builder, Compiler); + return true; +#endif + } + + static string Dirname (string name) + { + int pos = name.LastIndexOf ('/'); + + if (pos != -1) + return name.Substring (0, pos); + + pos = name.LastIndexOf ('\\'); + if (pos != -1) + return name.Substring (0, pos); + + return "."; + } + +#if !STATIC + protected override void SaveModule (PortableExecutableKinds pekind, ImageFileMachine machine) + { + try { + var module_only = typeof (AssemblyBuilder).GetProperty ("IsModuleOnly", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var set_module_only = module_only.GetSetMethod (true); + + set_module_only.Invoke (Builder, new object[] { true }); + } catch { + base.SaveModule (pekind, machine); + } + + Builder.Save (file_name, pekind, machine); + } +#endif + } + + // + // Extension to System.Reflection.Emit.AssemblyBuilder to have fully compatible + // compiler + // + class AssemblyBuilderMonoSpecific : AssemblyBuilderExtension + { + static MethodInfo adder_method; + static MethodInfo add_permission; + static MethodInfo add_type_forwarder; + static MethodInfo win32_icon_define; + static FieldInfo assembly_version; + static FieldInfo assembly_algorithm; + static FieldInfo assembly_culture; + static FieldInfo assembly_flags; + + AssemblyBuilder builder; + + public AssemblyBuilderMonoSpecific (AssemblyBuilder ab, CompilerContext ctx) + : base (ctx) + { + this.builder = ab; + } + + public override Module AddModule (string module) + { + try { + if (adder_method == null) + adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance | BindingFlags.NonPublic); + + return (Module) adder_method.Invoke (builder, new object[] { module }); + } catch { + return base.AddModule (module); + } + } + + public override void AddPermissionRequests (PermissionSet[] permissions) + { + try { + if (add_permission == null) + add_permission = typeof (AssemblyBuilder).GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic); + + add_permission.Invoke (builder, permissions); + } catch { + base.AddPermissionRequests (permissions); + } + } + + public override void AddTypeForwarder (TypeSpec type, Location loc) + { + try { + if (add_type_forwarder == null) { + add_type_forwarder = typeof (AssemblyBuilder).GetMethod ("AddTypeForwarder", BindingFlags.NonPublic | BindingFlags.Instance); + } + + add_type_forwarder.Invoke (builder, new object[] { type.GetMetaInfo () }); + } catch { + base.AddTypeForwarder (type, loc); + } + } + + public override void DefineWin32IconResource (string fileName) + { + try { + if (win32_icon_define == null) + win32_icon_define = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance | BindingFlags.NonPublic); + + win32_icon_define.Invoke (builder, new object[] { fileName }); + } catch { + base.DefineWin32IconResource (fileName); + } + } + + public override void SetAlgorithmId (uint value, Location loc) + { + try { + if (assembly_algorithm == null) + assembly_algorithm = typeof (AssemblyBuilder).GetField ("algid", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField); + + assembly_algorithm.SetValue (builder, value); + } catch { + base.SetAlgorithmId (value, loc); + } + } + + public override void SetCulture (string culture, Location loc) + { + try { + if (assembly_culture == null) + assembly_culture = typeof (AssemblyBuilder).GetField ("culture", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField); + + assembly_culture.SetValue (builder, culture); + } catch { + base.SetCulture (culture, loc); + } + } + + public override void SetFlags (uint flags, Location loc) + { + try { + if (assembly_flags == null) + assembly_flags = typeof (AssemblyBuilder).GetField ("flags", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField); + + assembly_flags.SetValue (builder, flags); + } catch { + base.SetFlags (flags, loc); + } + } + + public override void SetVersion (Version version, Location loc) + { + try { + if (assembly_version == null) + assembly_version = typeof (AssemblyBuilder).GetField ("version", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField); + + assembly_version.SetValue (builder, version.ToString (4)); + } catch { + base.SetVersion (version, loc); + } + } + } + + // + // Reflection based references loader + // + class DynamicLoader : AssemblyReferencesLoader + { + readonly ReflectionImporter importer; + + public DynamicLoader (ReflectionImporter importer, CompilerContext compiler) + : base (compiler) + { + paths.Add (GetSystemDir ()); + + this.importer = importer; + } + + public ReflectionImporter Importer { + get { + return importer; + } + } + + protected override string[] GetDefaultReferences () + { + // + // For now the "default config" is harcoded into the compiler + // we can move this outside later + // + var default_references = new List (8); + + default_references.Add ("System"); + default_references.Add ("System.Xml"); +#if NET_2_1 + default_references.Add ("System.Net"); + default_references.Add ("System.Windows"); + default_references.Add ("System.Windows.Browser"); +#endif + + if (compiler.Settings.Version > LanguageVersion.ISO_2) + default_references.Add ("System.Core"); + if (compiler.Settings.Version > LanguageVersion.V_3) + default_references.Add ("Microsoft.CSharp"); + + return default_references.ToArray (); + } + + // + // Returns the directory where the system assemblies are installed + // + static string GetSystemDir () + { + return Path.GetDirectoryName (typeof (object).Assembly.Location); + } + + public override bool HasObjectType (Assembly assembly) + { + return assembly.GetType (compiler.BuiltinTypes.Object.FullName) != null; + } + + public override Assembly LoadAssemblyFile (string assembly, bool isImplicitReference) + { + Assembly a = null; + + try { + try { + char[] path_chars = { '/', '\\' }; + + if (assembly.IndexOfAny (path_chars) != -1) { + a = Assembly.LoadFrom (assembly); + } else { + string ass = assembly; + if (ass.EndsWith (".dll") || ass.EndsWith (".exe")) + ass = assembly.Substring (0, assembly.Length - 4); + a = Assembly.Load (ass); + } + } catch (FileNotFoundException) { + bool err = !isImplicitReference; + foreach (string dir in paths) { + string full_path = Path.Combine (dir, assembly); + if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe")) + full_path += ".dll"; + + try { + a = Assembly.LoadFrom (full_path); + err = false; + break; + } catch (FileNotFoundException) { + } + } + + if (err) { + Error_FileNotFound (assembly); + return a; + } + } + } catch (BadImageFormatException) { + Error_FileCorrupted (assembly); + } + + return a; + } + + Module LoadModuleFile (AssemblyDefinitionDynamic assembly, string module) + { + string total_log = ""; + + try { + try { + return assembly.IncludeModule (module); + } catch (FileNotFoundException) { + bool err = true; + foreach (string dir in paths) { + string full_path = Path.Combine (dir, module); + if (!module.EndsWith (".netmodule")) + full_path += ".netmodule"; + + try { + return assembly.IncludeModule (full_path); + } catch (FileNotFoundException ff) { + total_log += ff.FusionLog; + } + } + if (err) { + Error_FileNotFound (module); + return null; + } + } + } catch (BadImageFormatException) { + Error_FileCorrupted (module); + } + + return null; + } + + public void LoadModules (AssemblyDefinitionDynamic assembly, RootNamespace targetNamespace) + { + foreach (var moduleName in compiler.Settings.Modules) { + var m = LoadModuleFile (assembly, moduleName); + if (m == null) + continue; + + var md = importer.ImportModule (m, targetNamespace); + assembly.AddModule (md); + } + } + + public override void LoadReferences (ModuleContainer module) + { + Assembly corlib; + List> loaded; + base.LoadReferencesCore (module, out corlib, out loaded); + + if (corlib == null) + return; + + importer.ImportAssembly (corlib, module.GlobalRootNamespace); + foreach (var entry in loaded) { + importer.ImportAssembly (entry.Item2, entry.Item1); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/report.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/report.cs new file mode 100644 index 000000000..e9b16d1c0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/report.cs @@ -0,0 +1,1144 @@ +// +// report.cs: report errors and warnings. +// +// Author: Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001 Ximian, Inc. (http://www.ximian.com) +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// + +using System; +using System.IO; +using System.Text; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Mono.CSharp { + + // + // Errors and warnings manager + // + public class Report + { + public const int RuntimeErrorId = 10000; + + Dictionary warning_regions_table; + + ReportPrinter printer; + + int reporting_disabled; + + readonly CompilerSettings settings; + + /// + /// List of symbols related to reported error/warning. You have to fill it before error/warning is reported. + /// + List extra_information = new List (); + + // + // IF YOU ADD A NEW WARNING YOU HAVE TO ADD ITS ID HERE + // + public static readonly int[] AllWarnings = new int[] { + 28, 67, 78, + 105, 108, 109, 114, 162, 164, 168, 169, 183, 184, 197, + 219, 251, 252, 253, 278, 282, + 402, 414, 419, 420, 429, 436, 437, 440, 458, 464, 465, 467, 469, 472, 473, + 612, 618, 626, 628, 642, 649, 652, 657, 658, 659, 660, 661, 665, 672, 675, 693, + 728, + 809, 824, + 1030, 1058, 1060, 1066, + 1522, 1570, 1571, 1572, 1573, 1574, 1580, 1581, 1584, 1587, 1589, 1590, 1591, 1592, + 1607, 1616, 1633, 1634, 1635, 1685, 1690, 1691, 1692, 1695, 1696, 1697, 1699, + 1700, 1701, 1702, 1709, 1711, 1717, 1718, 1720, 1735, + 1901, 1956, 1981, 1998, + 2002, 2023, 2029, + 3000, 3001, 3002, 3003, 3005, 3006, 3007, 3008, 3009, + 3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019, + 3021, 3022, 3023, 3024, 3026, 3027, + 4014, 4024, 4025, 4026, + 7035, 7080, 7081, 7082, 7095, + 8009, + }; + + static HashSet AllWarningsHashSet; + + public Report (CompilerContext context, ReportPrinter printer) + { + if (context == null) + throw new ArgumentNullException ("settings"); + if (printer == null) + throw new ArgumentNullException ("printer"); + + this.settings = context.Settings; + this.printer = printer; + } + + public void DisableReporting () + { + ++reporting_disabled; + } + + public void EnableReporting () + { + --reporting_disabled; + } + + public void FeatureIsNotAvailable (CompilerContext compiler, Location loc, string feature) + { + string version; + switch (compiler.Settings.Version) { + case LanguageVersion.ISO_1: + version = "1.0"; + break; + case LanguageVersion.ISO_2: + version = "2.0"; + break; + case LanguageVersion.V_3: + version = "3.0"; + break; + case LanguageVersion.V_4: + version = "4.0"; + break; + case LanguageVersion.V_5: + version = "5.0"; + break; + case LanguageVersion.V_6: + version = "6.0"; + break; + default: + throw new InternalErrorException ("Invalid feature version", compiler.Settings.Version); + } + + Error (1644, loc, + "Feature `{0}' cannot be used because it is not part of the C# {1} language specification", + feature, version); + } + + public void FeatureIsNotSupported (Location loc, string feature) + { + Error (1644, loc, + "Feature `{0}' is not supported in Mono mcs1 compiler. Consider using the `gmcs' compiler instead", + feature); + } + + public void RuntimeMissingSupport (Location loc, string feature) + { + Error (-88, loc, "Your .NET Runtime does not support `{0}'. Please use the latest Mono runtime instead.", feature); + } + + /// + /// In most error cases is very useful to have information about symbol that caused the error. + /// Call this method before you call Report.Error when it makes sense. + /// + public void SymbolRelatedToPreviousError (Location loc, string symbol) + { + SymbolRelatedToPreviousError (loc.ToString ()); + } + + public void SymbolRelatedToPreviousError (MemberSpec ms) + { + if (reporting_disabled > 0 || !printer.HasRelatedSymbolSupport) + return; + + var mc = ms.MemberDefinition as MemberCore; + while (ms is ElementTypeSpec) { + ms = ((ElementTypeSpec) ms).Element; + mc = ms.MemberDefinition as MemberCore; + } + + if (mc != null) { + SymbolRelatedToPreviousError (mc); + } else { + if (ms.DeclaringType != null) + ms = ms.DeclaringType; + + var imported_type = ms.MemberDefinition as ImportedTypeDefinition; + if (imported_type != null) { + var iad = imported_type.DeclaringAssembly as ImportedAssemblyDefinition; + SymbolRelatedToPreviousError (iad.Location); + } + } + } + + public void SymbolRelatedToPreviousError (MemberCore mc) + { + SymbolRelatedToPreviousError (mc.Location, mc.GetSignatureForError ()); + } + + public void SymbolRelatedToPreviousError (string loc) + { + string msg = String.Format ("{0} (Location of the symbol related to previous ", loc); + if (extra_information.Contains (msg)) + return; + + extra_information.Add (msg); + } + + public bool CheckWarningCode (int code, Location loc) + { + if (AllWarningsHashSet == null) + AllWarningsHashSet = new HashSet (AllWarnings); + + if (AllWarningsHashSet.Contains (code)) + return true; + + Warning (1691, 1, loc, "`{0}' is not a valid warning number", code); + return false; + } + + public void ExtraInformation (Location loc, string msg) + { + extra_information.Add (String.Format ("{0} {1}", loc, msg)); + } + + public WarningRegions RegisterWarningRegion (Location location) + { + WarningRegions regions; + if (warning_regions_table == null) { + regions = null; + warning_regions_table = new Dictionary (); + } else { + warning_regions_table.TryGetValue (location.File, out regions); + } + + if (regions == null) { + regions = new WarningRegions (); + warning_regions_table.Add (location.File, regions); + } + + return regions; + } + + public void Warning (int code, int level, Location loc, string message) + { + if (reporting_disabled > 0) + return; + + if (!settings.IsWarningEnabled (code, level)) + return; + + if (warning_regions_table != null && !loc.IsNull) { + WarningRegions regions; + if (warning_regions_table.TryGetValue (loc.File, out regions) && !regions.IsWarningEnabled (code, loc.Row)) + return; + } + + AbstractMessage msg; + if (settings.IsWarningAsError (code)) { + message = "Warning as Error: " + message; + msg = new ErrorMessage (code, loc, message, extra_information); + } else { + msg = new WarningMessage (code, loc, message, extra_information); + } + + extra_information.Clear (); + printer.Print (msg, settings.ShowFullPaths); + } + + public void Warning (int code, int level, Location loc, string format, string arg) + { + Warning (code, level, loc, String.Format (format, arg)); + } + + public void Warning (int code, int level, Location loc, string format, string arg1, string arg2) + { + Warning (code, level, loc, String.Format (format, arg1, arg2)); + } + + public void Warning (int code, int level, Location loc, string format, params object[] args) + { + Warning (code, level, loc, String.Format (format, args)); + } + + public void Warning (int code, int level, string message) + { + Warning (code, level, Location.Null, message); + } + + public void Warning (int code, int level, string format, string arg) + { + Warning (code, level, Location.Null, format, arg); + } + + public void Warning (int code, int level, string format, string arg1, string arg2) + { + Warning (code, level, Location.Null, format, arg1, arg2); + } + + public void Warning (int code, int level, string format, params string[] args) + { + Warning (code, level, Location.Null, String.Format (format, args)); + } + + // + // Warnings encountered so far + // + public int Warnings { + get { return printer.WarningsCount; } + } + + public void Error (int code, Location loc, string error) + { + if (reporting_disabled > 0) + return; + + ErrorMessage msg = new ErrorMessage (code, loc, error, extra_information); + extra_information.Clear (); + + printer.Print (msg, settings.ShowFullPaths); + + if (settings.Stacktrace) + Console.WriteLine (FriendlyStackTrace (new StackTrace (true))); + + if (printer.ErrorsCount == settings.FatalCounter) + throw new FatalException (msg.Text); + } + + public void Error (int code, Location loc, string format, string arg) + { + Error (code, loc, String.Format (format, arg)); + } + + public void Error (int code, Location loc, string format, string arg1, string arg2) + { + Error (code, loc, String.Format (format, arg1, arg2)); + } + + public void Error (int code, Location loc, string format, params string[] args) + { + Error (code, loc, String.Format (format, args)); + } + + public void Error (int code, string error) + { + Error (code, Location.Null, error); + } + + public void Error (int code, string format, string arg) + { + Error (code, Location.Null, format, arg); + } + + public void Error (int code, string format, string arg1, string arg2) + { + Error (code, Location.Null, format, arg1, arg2); + } + + public void Error (int code, string format, params string[] args) + { + Error (code, Location.Null, String.Format (format, args)); + } + + // + // Errors encountered so far + // + public int Errors { + get { return printer.ErrorsCount; } + } + + public bool IsDisabled { + get { + return reporting_disabled > 0; + } + } + + public ReportPrinter Printer { + get { return printer; } + } + + public ReportPrinter SetPrinter (ReportPrinter printer) + { + ReportPrinter old = this.printer; + this.printer = printer; + return old; + } + + [Conditional ("MCS_DEBUG")] + static public void Debug (string message, params object[] args) + { + Debug (4, message, args); + } + + [Conditional ("MCS_DEBUG")] + static public void Debug (int category, string message, params object[] args) + { +// if ((category & DebugFlags) == 0) +// return; + + StringBuilder sb = new StringBuilder (message); + + if ((args != null) && (args.Length > 0)) { + sb.Append (": "); + + bool first = true; + foreach (object arg in args) { + if (first) + first = false; + else + sb.Append (", "); + if (arg == null) + sb.Append ("null"); +// else if (arg is ICollection) +// sb.Append (PrintCollection ((ICollection) arg)); + else + sb.Append (arg); + } + } + + Console.WriteLine (sb.ToString ()); + } +/* + static public string PrintCollection (ICollection collection) + { + StringBuilder sb = new StringBuilder (); + + sb.Append (collection.GetType ()); + sb.Append ("("); + + bool first = true; + foreach (object o in collection) { + if (first) + first = false; + else + sb.Append (", "); + sb.Append (o); + } + + sb.Append (")"); + return sb.ToString (); + } +*/ + static string FriendlyStackTrace (StackTrace t) + { + StringBuilder sb = new StringBuilder (); + + bool foundUserCode = false; + + for (int i = 0; i < t.FrameCount; i++) { + StackFrame f = t.GetFrame (i); + var mb = f.GetMethod (); + + if (!foundUserCode && mb.ReflectedType == typeof (Report)) + continue; + + foundUserCode = true; + + sb.Append ("\tin "); + + if (f.GetFileLineNumber () > 0) + sb.AppendFormat ("(at {0}:{1}) ", f.GetFileName (), f.GetFileLineNumber ()); + + sb.AppendFormat ("{0}.{1} (", mb.ReflectedType.Name, mb.Name); + + bool first = true; + foreach (var pi in mb.GetParameters ()) { + if (!first) + sb.Append (", "); + first = false; + + sb.Append (pi.ParameterType.FullName); + } + sb.Append (")\n"); + } + + return sb.ToString (); + } + } + + public abstract class AbstractMessage + { + readonly string[] extra_info; + protected readonly int code; + protected readonly Location location; + readonly string message; + + protected AbstractMessage (int code, Location loc, string msg, List extraInfo) + { + this.code = code; + if (code < 0) + this.code = 8000 - code; + + this.location = loc; + this.message = msg; + if (extraInfo.Count != 0) { + this.extra_info = extraInfo.ToArray (); + } + } + + protected AbstractMessage (AbstractMessage aMsg) + { + this.code = aMsg.code; + this.location = aMsg.location; + this.message = aMsg.message; + this.extra_info = aMsg.extra_info; + } + + public int Code { + get { return code; } + } + + public override bool Equals (object obj) + { + AbstractMessage msg = obj as AbstractMessage; + if (msg == null) + return false; + + return code == msg.code && location.Equals (msg.location) && message == msg.message; + } + + public override int GetHashCode () + { + return code.GetHashCode (); + } + + public abstract bool IsWarning { get; } + + public Location Location { + get { return location; } + } + + public abstract string MessageType { get; } + + public string[] RelatedSymbols { + get { return extra_info; } + } + + public string Text { + get { return message; } + } + } + + sealed class WarningMessage : AbstractMessage + { + public WarningMessage (int code, Location loc, string message, List extra_info) + : base (code, loc, message, extra_info) + { + } + + public override bool IsWarning { + get { return true; } + } + + public override string MessageType { + get { + return "warning"; + } + } + } + + sealed class ErrorMessage : AbstractMessage + { + public ErrorMessage (int code, Location loc, string message, List extraInfo) + : base (code, loc, message, extraInfo) + { + } + + public ErrorMessage (AbstractMessage aMsg) + : base (aMsg) + { + } + + public override bool IsWarning { + get { return false; } + } + + public override string MessageType { + get { + return "error"; + } + } + } + + // + // Generic base for any message writer + // + public abstract class ReportPrinter + { + protected HashSet reported_missing_definitions; + + #region Properties + + public int ErrorsCount { get; protected set; } + + public int WarningsCount { get; private set; } + + // + // When (symbols related to previous ...) can be used + // + public virtual bool HasRelatedSymbolSupport { + get { return true; } + } + + #endregion + + + protected virtual string FormatText (string txt) + { + return txt; + } + + public virtual void Print (AbstractMessage msg, bool showFullPath) + { + if (msg.IsWarning) { + ++WarningsCount; + } else { + ++ErrorsCount; + } + } + + protected void Print (AbstractMessage msg, TextWriter output, bool showFullPath) + { + StringBuilder txt = new StringBuilder (); + if (!msg.Location.IsNull) { + if (showFullPath) + txt.Append (msg.Location.ToStringFullName ()); + else + txt.Append (msg.Location.ToString ()); + + txt.Append (" "); + } + + txt.AppendFormat ("{0} CS{1:0000}: {2}", msg.MessageType, msg.Code, msg.Text); + + if (!msg.IsWarning) + output.WriteLine (FormatText (txt.ToString ())); + else + output.WriteLine (txt.ToString ()); + + if (msg.RelatedSymbols != null) { + foreach (string s in msg.RelatedSymbols) + output.WriteLine (s + msg.MessageType + ")"); + } + } + + // + // Tracks reported missing types. It needs to be session specific + // because we can run in probing mode + // + public bool MissingTypeReported (ITypeDefinition typeDefinition) + { + if (reported_missing_definitions == null) + reported_missing_definitions = new HashSet (); + + if (reported_missing_definitions.Contains (typeDefinition)) + return true; + + reported_missing_definitions.Add (typeDefinition); + return false; + } + + public void Reset () + { + // HACK: Temporary hack for broken repl flow + ErrorsCount = WarningsCount = 0; + } + } + + sealed class NullReportPrinter : ReportPrinter + { + } + + // + // Default message recorder, it uses two types of message groups. + // Common messages: messages reported in all sessions. + // Merged messages: union of all messages in all sessions. + // + // Used by the Lambda expressions to compile the code with various + // parameter values, or by attribute resolver + // + class SessionReportPrinter : ReportPrinter + { + List session_messages; + // + // A collection of exactly same messages reported in all sessions + // + List common_messages; + + // + // A collection of unique messages reported in all sessions + // + List merged_messages; + + bool showFullPaths; + + public void ClearSession () + { + session_messages = null; + } + + public override void Print (AbstractMessage msg, bool showFullPath) + { + // + // This line is useful when debugging recorded messages + // + // Console.WriteLine ("RECORDING: {0}", msg.Text); + + if (session_messages == null) + session_messages = new List (); + + session_messages.Add (msg); + + this.showFullPaths = showFullPath; + base.Print (msg, showFullPath); + } + + public void EndSession () + { + if (session_messages == null) + return; + + // + // Handles the first session + // + if (common_messages == null) { + common_messages = new List (session_messages); + merged_messages = session_messages; + session_messages = null; + return; + } + + // + // Store common messages if any + // + for (int i = 0; i < common_messages.Count; ++i) { + AbstractMessage cmsg = common_messages[i]; + bool common_msg_found = false; + foreach (AbstractMessage msg in session_messages) { + if (cmsg.Equals (msg)) { + common_msg_found = true; + break; + } + } + + if (!common_msg_found) + common_messages.RemoveAt (i); + } + + // + // Merge session and previous messages + // + for (int i = 0; i < session_messages.Count; ++i) { + AbstractMessage msg = session_messages[i]; + bool msg_found = false; + for (int ii = 0; ii < merged_messages.Count; ++ii) { + if (msg.Equals (merged_messages[ii])) { + msg_found = true; + break; + } + } + + if (!msg_found) + merged_messages.Add (msg); + } + } + + public bool IsEmpty { + get { + return merged_messages == null && common_messages == null; + } + } + + // + // Prints collected messages, common messages have a priority + // + public bool Merge (ReportPrinter dest) + { + var messages_to_print = merged_messages; + if (common_messages != null && common_messages.Count > 0) { + messages_to_print = common_messages; + } + + if (messages_to_print == null) + return false; + + bool error_msg = false; + foreach (AbstractMessage msg in messages_to_print) { + dest.Print (msg, showFullPaths); + error_msg |= !msg.IsWarning; + } + + if (reported_missing_definitions != null) { + foreach (var missing in reported_missing_definitions) + dest.MissingTypeReported (missing); + } + + return error_msg; + } + } + + public class StreamReportPrinter : ReportPrinter + { + readonly TextWriter writer; + + public StreamReportPrinter (TextWriter writer) + { + this.writer = writer; + } + + public override void Print (AbstractMessage msg, bool showFullPath) + { + Print (msg, writer, showFullPath); + base.Print (msg, showFullPath); + } + } + + public class ConsoleReportPrinter : StreamReportPrinter + { + static readonly string prefix, postfix; + + static ConsoleReportPrinter () + { + string term = Environment.GetEnvironmentVariable ("TERM"); + bool xterm_colors = false; + + switch (term){ + case "xterm": + case "rxvt": + case "rxvt-unicode": + if (Environment.GetEnvironmentVariable ("COLORTERM") != null){ + xterm_colors = true; + } + break; + + case "xterm-color": + case "xterm-256color": + xterm_colors = true; + break; + } + if (!xterm_colors) + return; + + if (!(UnixUtils.isatty (1) && UnixUtils.isatty (2))) + return; + + string config = Environment.GetEnvironmentVariable ("MCS_COLORS"); + if (config == null){ + config = "errors=red"; + //config = "brightwhite,red"; + } + + if (config == "disable") + return; + + if (!config.StartsWith ("errors=")) + return; + + config = config.Substring (7); + + int p = config.IndexOf (","); + if (p == -1) + prefix = GetForeground (config); + else + prefix = GetBackground (config.Substring (p+1)) + GetForeground (config.Substring (0, p)); + postfix = "\x001b[0m"; + } + + public ConsoleReportPrinter () + : base (Console.Error) + { + } + + public ConsoleReportPrinter (TextWriter writer) + : base (writer) + { + } + + static int NameToCode (string s) + { + switch (s) { + case "black": + return 0; + case "red": + return 1; + case "green": + return 2; + case "yellow": + return 3; + case "blue": + return 4; + case "magenta": + return 5; + case "cyan": + return 6; + case "grey": + case "white": + return 7; + } + return 7; + } + + // + // maps a color name to its xterm color code + // + static string GetForeground (string s) + { + string highcode; + + if (s.StartsWith ("bright")) { + highcode = "1;"; + s = s.Substring (6); + } else + highcode = ""; + + return "\x001b[" + highcode + (30 + NameToCode (s)).ToString () + "m"; + } + + static string GetBackground (string s) + { + return "\x001b[" + (40 + NameToCode (s)).ToString () + "m"; + } + + protected override string FormatText (string txt) + { + if (prefix != null) + return prefix + txt + postfix; + + return txt; + } + } + + class TimeReporter + { + public enum TimerType + { + ParseTotal, + AssemblyBuilderSetup, + CreateTypeTotal, + ReferencesLoading, + ReferencesImporting, + PredefinedTypesInit, + ModuleDefinitionTotal, + EmitTotal, + CloseTypes, + Resouces, + OutputSave, + DebugSave, + } + + readonly Stopwatch[] timers; + Stopwatch total; + + public TimeReporter (bool enabled) + { + if (!enabled) + return; + + timers = new Stopwatch[System.Enum.GetValues(typeof (TimerType)).Length]; + } + + public void Start (TimerType type) + { + if (timers != null) { + var sw = new Stopwatch (); + timers[(int) type] = sw; + sw.Start (); + } + } + + public void StartTotal () + { + total = new Stopwatch (); + total.Start (); + } + + public void Stop (TimerType type) + { + if (timers != null) { + timers[(int) type].Stop (); + } + } + + public void StopTotal () + { + total.Stop (); + } + + public void ShowStats () + { + if (timers == null) + return; + + Dictionary timer_names = new Dictionary { + { TimerType.ParseTotal, "Parsing source files" }, + { TimerType.AssemblyBuilderSetup, "Assembly builder setup" }, + { TimerType.CreateTypeTotal, "Compiled types created" }, + { TimerType.ReferencesLoading, "Referenced assemblies loading" }, + { TimerType.ReferencesImporting, "Referenced assemblies importing" }, + { TimerType.PredefinedTypesInit, "Predefined types initialization" }, + { TimerType.ModuleDefinitionTotal, "Module definition" }, + { TimerType.EmitTotal, "Resolving and emitting members blocks" }, + { TimerType.CloseTypes, "Module types closed" }, + { TimerType.Resouces, "Embedding resources" }, + { TimerType.OutputSave, "Writing output file" }, + { TimerType.DebugSave, "Writing debug symbols file" }, + }; + + int counter = 0; + double percentage = (double) total.ElapsedMilliseconds / 100; + long subtotal = total.ElapsedMilliseconds; + foreach (var timer in timers) { + string msg = timer_names[(TimerType) counter++]; + var ms = timer == null ? 0 : timer.ElapsedMilliseconds; + Console.WriteLine ("{0,4:0.0}% {1,5}ms {2}", ms / percentage, ms, msg); + subtotal -= ms; + } + + Console.WriteLine ("{0,4:0.0}% {1,5}ms Other tasks", subtotal / percentage, subtotal); + Console.WriteLine (); + Console.WriteLine ("Total elapsed time: {0}", total.Elapsed); + } + } + + public class InternalErrorException : Exception { + public InternalErrorException (MemberCore mc, Exception e) + : base (mc.Location + " " + mc.GetSignatureForError (), e) + { + } + + public InternalErrorException () + : base ("Internal error") + { + } + + public InternalErrorException (string message) + : base (message) + { + } + + public InternalErrorException (string message, params object[] args) + : base (String.Format (message, args)) + { + } + + public InternalErrorException (Exception exception, string message, params object[] args) + : base (String.Format (message, args), exception) + { + } + + public InternalErrorException (Exception e, Location loc) + : base (loc.ToString (), e) + { + } + } + + class FatalException : Exception + { + public FatalException (string message) + : base (message) + { + } + } + + /// + /// Handles #pragma warning + /// + public class WarningRegions { + + abstract class PragmaCmd + { + public int Line; + + protected PragmaCmd (int line) + { + Line = line; + } + + public abstract bool IsEnabled (int code, bool previous); + } + + class Disable : PragmaCmd + { + int code; + public Disable (int line, int code) + : base (line) + { + this.code = code; + } + + public override bool IsEnabled (int code, bool previous) + { + return this.code != code && previous; + } + } + + class DisableAll : PragmaCmd + { + public DisableAll (int line) + : base (line) {} + + public override bool IsEnabled(int code, bool previous) + { + return false; + } + } + + class Enable : PragmaCmd + { + int code; + public Enable (int line, int code) + : base (line) + { + this.code = code; + } + + public override bool IsEnabled(int code, bool previous) + { + return this.code == code || previous; + } + } + + class EnableAll : PragmaCmd + { + public EnableAll (int line) + : base (line) {} + + public override bool IsEnabled(int code, bool previous) + { + return true; + } + } + + + List regions = new List (); + + public void WarningDisable (int line) + { + regions.Add (new DisableAll (line)); + } + + public void WarningDisable (Location location, int code, Report Report) + { + if (Report.CheckWarningCode (code, location)) + regions.Add (new Disable (location.Row, code)); + } + + public void WarningEnable (int line) + { + regions.Add (new EnableAll (line)); + } + + public void WarningEnable (Location location, int code, CompilerContext context) + { + if (!context.Report.CheckWarningCode (code, location)) + return; + + if (context.Settings.IsWarningDisabledGlobally (code)) + context.Report.Warning (1635, 1, location, "Cannot restore warning `CS{0:0000}' because it was disabled globally", code); + + regions.Add (new Enable (location.Row, code)); + } + + public bool IsWarningEnabled (int code, int src_line) + { + bool result = true; + foreach (PragmaCmd pragma in regions) { + if (src_line < pragma.Line) + break; + + result = pragma.IsEnabled (code, result); + } + return result; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/settings.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/settings.cs new file mode 100644 index 000000000..9ba66c161 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/settings.cs @@ -0,0 +1,1601 @@ +// +// settings.cs: All compiler settings +// +// Author: Miguel de Icaza (miguel@ximian.com) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Globalization; +using System; + +namespace Mono.CSharp { + + public enum LanguageVersion + { + ISO_1 = 1, + ISO_2 = 2, + V_3 = 3, + V_4 = 4, + V_5 = 5, + V_6 = 6, + Experimental = 100, + + Default = LanguageVersion.V_6, + } + + public enum RuntimeVersion + { + v1, + v2, + v4 + } + + public enum Target + { + Library, Exe, Module, WinExe + } + + public enum Platform + { + AnyCPU, + AnyCPU32Preferred, + Arm, + X86, + X64, + IA64 + } + + public class CompilerSettings + { + public Target Target; + public Platform Platform; + public string TargetExt; + public bool VerifyClsCompliance; + public bool Optimize; + public LanguageVersion Version; + public bool EnhancedWarnings; + public bool LoadDefaultReferences; + public string SdkVersion; + + public string StrongNameKeyFile; + public string StrongNameKeyContainer; + public bool StrongNameDelaySign; + + public int TabSize; + + public bool WarningsAreErrors; + public int WarningLevel; + + // + // Assemblies references to be loaded + // + public List AssemblyReferences; + + // + // External aliases for assemblies + // + public List> AssemblyReferencesAliases; + + // + // Modules to be embedded + // + public List Modules; + + // + // Lookup paths for referenced assemblies + // + public List ReferencesLookupPaths; + + // + // Encoding. + // + public Encoding Encoding; + + // + // If set, enable XML documentation generation + // + public string DocumentationFile; + + public string MainClass; + + // + // Output file + // + public string OutputFile; + + // + // The default compiler checked state + // + public bool Checked; + + // + // If true, the compiler is operating in statement mode, + // this currently turns local variable declaration into + // static variables of a class + // + public bool StatementMode; // TODO: SUPER UGLY + + // + // Whether to allow Unsafe code + // + public bool Unsafe; + + public string Win32ResourceFile; + public string Win32IconFile; + + // + // A list of resource files for embedding + // + public List Resources; + + public bool GenerateDebugInfo; + + #region Compiler debug flags only + public bool ParseOnly, TokenizeOnly, Timestamps; + public int DebugFlags; + public int VerboseParserFlag; + public int FatalCounter; + public bool Stacktrace; + public bool BreakOnInternalError; + #endregion + + public bool ShowFullPaths; + + // + // Whether we are being linked against the standard libraries. + // This is only used to tell whether `System.Object' should + // have a base class or not. + // + public bool StdLib; + + public RuntimeVersion StdLibRuntimeVersion; + + public string RuntimeMetadataVersion; + + public bool WriteMetadataOnly; + + readonly List conditional_symbols; + + readonly List source_files; + + List warnings_as_error; + List warnings_only; + HashSet warning_ignore_table; + + public CompilerSettings () + { + StdLib = true; + Target = Target.Exe; + TargetExt = ".exe"; + Platform = Platform.AnyCPU; + Version = LanguageVersion.Default; + VerifyClsCompliance = true; + Encoding = Encoding.UTF8; + LoadDefaultReferences = true; + StdLibRuntimeVersion = RuntimeVersion.v4; + WarningLevel = 4; + + // Default to 1 or mdb files would be platform speficic + TabSize = 1; + + AssemblyReferences = new List (); + AssemblyReferencesAliases = new List> (); + Modules = new List (); + ReferencesLookupPaths = new List (); + + conditional_symbols = new List (); + // + // Add default mcs define + // + conditional_symbols.Add ("__MonoCS__"); + + source_files = new List (); + } + + #region Properties + + public SourceFile FirstSourceFile { + get { + return source_files.Count > 0 ? source_files [0] : null; + } + } + + public bool HasKeyFileOrContainer { + get { + return StrongNameKeyFile != null || StrongNameKeyContainer != null; + } + } + + public bool NeedsEntryPoint { + get { + return Target == Target.Exe || Target == Target.WinExe; + } + } + + public List SourceFiles { + get { + return source_files; + } + } + + #endregion + + public void AddConditionalSymbol (string symbol) + { + if (!conditional_symbols.Contains (symbol)) + conditional_symbols.Add (symbol); + } + + public void AddWarningAsError (int id) + { + if (warnings_as_error == null) + warnings_as_error = new List (); + + warnings_as_error.Add (id); + } + + public void AddWarningOnly (int id) + { + if (warnings_only == null) + warnings_only = new List (); + + warnings_only.Add (id); + } + + public bool IsConditionalSymbolDefined (string symbol) + { + return conditional_symbols.Contains (symbol); + } + + public bool IsWarningAsError (int code) + { + bool is_error = WarningsAreErrors; + + // Check specific list + if (warnings_as_error != null) + is_error |= warnings_as_error.Contains (code); + + // Ignore excluded warnings + if (warnings_only != null && warnings_only.Contains (code)) + is_error = false; + + return is_error; + } + + public bool IsWarningEnabled (int code, int level) + { + if (WarningLevel < level) + return false; + + return !IsWarningDisabledGlobally (code); + } + + public bool IsWarningDisabledGlobally (int code) + { + return warning_ignore_table != null && warning_ignore_table.Contains (code); + } + + public void SetIgnoreWarning (int code) + { + if (warning_ignore_table == null) + warning_ignore_table = new HashSet (); + + warning_ignore_table.Add (code); + } + } + + public class CommandLineParser + { + enum ParseResult + { + Success, + Error, + Stop, + UnknownOption + } + + static readonly char[] argument_value_separator = { ';', ',' }; + static readonly char[] numeric_value_separator = { ';', ',', ' ' }; + + readonly TextWriter output; + readonly Report report; + bool stop_argument; + + Dictionary source_file_index; + + public event Func UnknownOptionHandler; + + CompilerSettings parser_settings; + + public CommandLineParser (TextWriter errorOutput) + : this (errorOutput, Console.Out) + { + } + + public CommandLineParser (TextWriter errorOutput, TextWriter messagesOutput) + { + var rp = new StreamReportPrinter (errorOutput); + + parser_settings = new CompilerSettings (); + report = new Report (new CompilerContext (parser_settings, rp), rp); + this.output = messagesOutput; + } + + public bool HasBeenStopped { + get { + return stop_argument; + } + } + + void About () + { + output.WriteLine ( + "The Mono C# compiler is Copyright 2001-2011, Novell, Inc.\n\n" + + "The compiler source code is released under the terms of the \n" + + "MIT X11 or GNU GPL licenses\n\n" + + + "For more information on Mono, visit the project Web site\n" + + " http://www.mono-project.com\n\n" + + + "The compiler was written by Miguel de Icaza, Ravi Pratap, Martin Baulig, Marek Safar, Raja R Harinath, Atushi Enomoto"); + } + + public CompilerSettings ParseArguments (string[] args) + { + CompilerSettings settings = new CompilerSettings (); + if (!ParseArguments (settings, args)) + return null; + + return settings; + } + + public bool ParseArguments (CompilerSettings settings, string[] args) + { + if (settings == null) + throw new ArgumentNullException ("settings"); + + List response_file_list = null; + bool parsing_options = true; + stop_argument = false; + source_file_index = new Dictionary (); + + for (int i = 0; i < args.Length; i++) { + string arg = args[i]; + if (arg.Length == 0) + continue; + + if (arg[0] == '@') { + string[] extra_args; + string response_file = arg.Substring (1); + + if (response_file_list == null) + response_file_list = new List (); + + if (response_file_list.Contains (response_file)) { + report.Error (1515, "Response file `{0}' specified multiple times", response_file); + return false; + } + + response_file_list.Add (response_file); + + extra_args = LoadArgs (response_file); + if (extra_args == null) { + report.Error (2011, "Unable to open response file: " + response_file); + return false; + } + + args = AddArgs (args, extra_args); + continue; + } + + if (parsing_options) { + if (arg == "--") { + parsing_options = false; + continue; + } + + bool dash_opt = arg[0] == '-'; + bool slash_opt = arg[0] == '/'; + if (dash_opt) { + switch (ParseOptionUnix (arg, ref args, ref i, settings)) { + case ParseResult.Error: + case ParseResult.Success: + continue; + case ParseResult.Stop: + stop_argument = true; + return true; + case ParseResult.UnknownOption: + if (UnknownOptionHandler != null) { + var ret = UnknownOptionHandler (args, i); + if (ret != -1) { + i = ret; + continue; + } + } + break; + } + } + + if (dash_opt || slash_opt) { + // Try a -CSCOPTION + string csc_opt = dash_opt ? "/" + arg.Substring (1) : arg; + switch (ParseOption (csc_opt, ref args, settings)) { + case ParseResult.Error: + case ParseResult.Success: + continue; + case ParseResult.UnknownOption: + // Need to skip `/home/test.cs' however /test.cs is considered as error + if ((slash_opt && arg.Length > 3 && arg.IndexOf ('/', 2) > 0)) + break; + + if (UnknownOptionHandler != null) { + var ret = UnknownOptionHandler (args, i); + if (ret != -1) { + i = ret; + continue; + } + } + + Error_WrongOption (arg); + return false; + + case ParseResult.Stop: + stop_argument = true; + return true; + } + } + } + + ProcessSourceFiles (arg, false, settings.SourceFiles); + } + + return report.Errors == 0; + } + + void ProcessSourceFiles (string spec, bool recurse, List sourceFiles) + { + string path, pattern; + + SplitPathAndPattern (spec, out path, out pattern); + if (pattern.IndexOf ('*') == -1) { + AddSourceFile (spec, sourceFiles); + return; + } + + string[] files; + try { + files = Directory.GetFiles (path, pattern); + } catch (System.IO.DirectoryNotFoundException) { + report.Error (2001, "Source file `" + spec + "' could not be found"); + return; + } catch (System.IO.IOException) { + report.Error (2001, "Source file `" + spec + "' could not be found"); + return; + } + foreach (string f in files) { + AddSourceFile (f, sourceFiles); + } + + if (!recurse) + return; + + string[] dirs = null; + + try { + dirs = Directory.GetDirectories (path); + } catch { + } + + foreach (string d in dirs) { + + // Don't include path in this string, as each + // directory entry already does + ProcessSourceFiles (d + "/" + pattern, true, sourceFiles); + } + } + + static string[] AddArgs (string[] args, string[] extra_args) + { + string[] new_args; + new_args = new string[extra_args.Length + args.Length]; + + // if args contains '--' we have to take that into account + // split args into first half and second half based on '--' + // and add the extra_args before -- + int split_position = Array.IndexOf (args, "--"); + if (split_position != -1) { + Array.Copy (args, new_args, split_position); + extra_args.CopyTo (new_args, split_position); + Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position); + } else { + args.CopyTo (new_args, 0); + extra_args.CopyTo (new_args, args.Length); + } + + return new_args; + } + + void AddAssemblyReference (string alias, string assembly, CompilerSettings settings) + { + if (assembly.Length == 0) { + report.Error (1680, "Invalid reference alias `{0}='. Missing filename", alias); + return; + } + + if (!IsExternAliasValid (alias)) { + report.Error (1679, "Invalid extern alias for -reference. Alias `{0}' is not a valid identifier", alias); + return; + } + + settings.AssemblyReferencesAliases.Add (Tuple.Create (alias, assembly)); + } + + void AddResource (AssemblyResource res, CompilerSettings settings) + { + if (settings.Resources == null) { + settings.Resources = new List (); + settings.Resources.Add (res); + return; + } + + if (settings.Resources.Contains (res)) { + report.Error (1508, "The resource identifier `{0}' has already been used in this assembly", res.Name); + return; + } + + settings.Resources.Add (res); + } + + void AddSourceFile (string fileName, List sourceFiles) + { + string path = Path.GetFullPath (fileName); + + int index; + if (source_file_index.TryGetValue (path, out index)) { + string other_name = sourceFiles[index - 1].Name; + if (fileName.Equals (other_name)) + report.Warning (2002, 1, "Source file `{0}' specified multiple times", other_name); + else + report.Warning (2002, 1, "Source filenames `{0}' and `{1}' both refer to the same file: {2}", fileName, other_name, path); + + return; + } + + var unit = new SourceFile (fileName, path, sourceFiles.Count + 1); + sourceFiles.Add (unit); + source_file_index.Add (path, unit.Index); + } + + public bool ProcessWarningsList (string text, Action action) + { + bool valid = true; + foreach (string wid in text.Split (numeric_value_separator, StringSplitOptions.RemoveEmptyEntries)) { + int id; + if (!int.TryParse (wid, NumberStyles.AllowLeadingWhite, CultureInfo.InvariantCulture, out id)) { + report.Error (1904, "`{0}' is not a valid warning number", wid); + valid = false; + continue; + } + + if (report.CheckWarningCode (id, Location.Null)) + action (id); + } + + return valid; + } + + void Error_RequiresArgument (string option) + { + report.Error (2006, "Missing argument for `{0}' option", option); + } + + void Error_RequiresFileName (string option) + { + report.Error (2005, "Missing file specification for `{0}' option", option); + } + + void Error_WrongOption (string option) + { + report.Error (2007, "Unrecognized command-line option: `{0}'", option); + } + + static bool IsExternAliasValid (string identifier) + { + return Tokenizer.IsValidIdentifier (identifier); + } + + static string[] LoadArgs (string file) + { + StreamReader f; + var args = new List (); + string line; + try { + f = new StreamReader (file); + } catch { + return null; + } + + StringBuilder sb = new StringBuilder (); + + while ((line = f.ReadLine ()) != null) { + int t = line.Length; + + for (int i = 0; i < t; i++) { + char c = line[i]; + + if (c == '"' || c == '\'') { + char end = c; + + for (i++; i < t; i++) { + c = line[i]; + + if (c == end) + break; + sb.Append (c); + } + } else if (c == ' ') { + if (sb.Length > 0) { + args.Add (sb.ToString ()); + sb.Length = 0; + } + } else + sb.Append (c); + } + if (sb.Length > 0) { + args.Add (sb.ToString ()); + sb.Length = 0; + } + } + + return args.ToArray (); + } + + void OtherFlags () + { + output.WriteLine ( + "Other flags in the compiler\n" + + " --fatal[=COUNT] Makes error after COUNT fatal\n" + + " --lint Enhanced warnings\n" + + " --metadata-only Produced assembly will contain metadata only\n" + + " --parse Only parses the source file\n" + + " --runtime:VERSION Sets mscorlib.dll metadata version: v1, v2, v4\n" + + " --stacktrace Shows stack trace at error location\n" + + " --timestamp Displays time stamps of various compiler events\n" + + " -v Verbose parsing (for debugging the parser)\n" + + " --mcs-debug X Sets MCS debugging level to X\n" + + " --break-on-ice Breaks compilation on internal compiler error"); + } + + // + // This parses the -arg and /arg options to the compiler, even if the strings + // in the following text use "/arg" on the strings. + // + ParseResult ParseOption (string option, ref string[] args, CompilerSettings settings) + { + int idx = option.IndexOf (':'); + string arg, value; + + if (idx == -1) { + arg = option; + value = ""; + } else { + arg = option.Substring (0, idx); + + value = option.Substring (idx + 1); + } + + switch (arg.ToLowerInvariant ()) { + case "/nologo": + return ParseResult.Success; + + case "/t": + case "/target": + switch (value) { + case "exe": + settings.Target = Target.Exe; + break; + + case "winexe": + settings.Target = Target.WinExe; + break; + + case "library": + settings.Target = Target.Library; + settings.TargetExt = ".dll"; + break; + + case "module": + settings.Target = Target.Module; + settings.TargetExt = ".netmodule"; + break; + + default: + report.Error (2019, "Invalid target type for -target. Valid options are `exe', `winexe', `library' or `module'"); + return ParseResult.Error; + } + return ParseResult.Success; + + case "/out": + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + settings.OutputFile = value; + return ParseResult.Success; + + case "/o": + case "/o+": + case "/optimize": + case "/optimize+": + settings.Optimize = true; + return ParseResult.Success; + + case "/o-": + case "/optimize-": + settings.Optimize = false; + return ParseResult.Success; + + // TODO: Not supported by csc 3.5+ + case "/incremental": + case "/incremental+": + case "/incremental-": + // nothing. + return ParseResult.Success; + + case "/d": + case "/define": { + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + foreach (string d in value.Split (argument_value_separator)) { + string conditional = d.Trim (); + if (!Tokenizer.IsValidIdentifier (conditional)) { + report.Warning (2029, 1, "Invalid conditional define symbol `{0}'", conditional); + continue; + } + + settings.AddConditionalSymbol (conditional); + } + return ParseResult.Success; + } + + case "/bugreport": + // + // We should collect data, runtime, etc and store in the file specified + // + output.WriteLine ("To file bug reports, please visit: http://www.mono-project.com/Bugs"); + return ParseResult.Success; + + case "/pkg": { + string packages; + + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + packages = String.Join (" ", value.Split (new Char[] { ';', ',', '\n', '\r' })); + string pkgout = Driver.GetPackageFlags (packages, report); + + if (pkgout == null) + return ParseResult.Error; + + string[] xargs = pkgout.Trim (new Char[] { ' ', '\n', '\r', '\t' }).Split (new Char[] { ' ', '\t' }); + args = AddArgs (args, xargs); + return ParseResult.Success; + } + + case "/linkres": + case "/linkresource": + case "/res": + case "/resource": + AssemblyResource res = null; + string[] s = value.Split (argument_value_separator, StringSplitOptions.RemoveEmptyEntries); + switch (s.Length) { + case 1: + if (s[0].Length == 0) + goto default; + res = new AssemblyResource (s[0], Path.GetFileName (s[0])); + break; + case 2: + res = new AssemblyResource (s[0], s[1]); + break; + case 3: + if (s[2] != "public" && s[2] != "private") { + report.Error (1906, "Invalid resource visibility option `{0}'. Use either `public' or `private' instead", s[2]); + return ParseResult.Error; + } + res = new AssemblyResource (s[0], s[1], s[2] == "private"); + break; + default: + report.Error (-2005, "Wrong number of arguments for option `{0}'", option); + return ParseResult.Error; + } + + if (res != null) { + res.IsEmbeded = arg[1] == 'r' || arg[1] == 'R'; + AddResource (res, settings); + } + + return ParseResult.Success; + + case "/recurse": + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + ProcessSourceFiles (value, true, settings.SourceFiles); + return ParseResult.Success; + + case "/r": + case "/reference": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + string[] refs = value.Split (argument_value_separator); + foreach (string r in refs) { + if (r.Length == 0) + continue; + + string val = r; + int index = val.IndexOf ('='); + if (index > -1) { + string alias = r.Substring (0, index); + string assembly = r.Substring (index + 1); + AddAssemblyReference (alias, assembly, settings); + if (refs.Length != 1) { + report.Error (2034, "Cannot specify multiple aliases using single /reference option"); + return ParseResult.Error; + } + } else { + settings.AssemblyReferences.Add (val); + } + } + return ParseResult.Success; + } + case "/addmodule": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + string[] refs = value.Split (argument_value_separator); + foreach (string r in refs) { + settings.Modules.Add (r); + } + return ParseResult.Success; + } + case "/win32res": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + if (settings.Win32IconFile != null) + report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time"); + + settings.Win32ResourceFile = value; + return ParseResult.Success; + } + case "/win32icon": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + if (settings.Win32ResourceFile != null) + report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time"); + + settings.Win32IconFile = value; + return ParseResult.Success; + } + case "/doc": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + settings.DocumentationFile = value; + return ParseResult.Success; + } + case "/lib": { + string[] libdirs; + + if (value.Length == 0) { + return ParseResult.Error; + } + + libdirs = value.Split (argument_value_separator); + foreach (string dir in libdirs) + settings.ReferencesLookupPaths.Add (dir); + return ParseResult.Success; + } + + case "/debug-": + settings.GenerateDebugInfo = false; + return ParseResult.Success; + + case "/debug": + if (value.Equals ("full", StringComparison.OrdinalIgnoreCase) || value.Equals ("pdbonly", StringComparison.OrdinalIgnoreCase) || idx < 0) { + settings.GenerateDebugInfo = true; + return ParseResult.Success; + } + + if (value.Length > 0) { + report.Error (1902, "Invalid debug option `{0}'. Valid options are `full' or `pdbonly'", value); + } else { + Error_RequiresArgument (option); + } + + return ParseResult.Error; + + case "/debug+": + settings.GenerateDebugInfo = true; + return ParseResult.Success; + + case "/checked": + case "/checked+": + settings.Checked = true; + return ParseResult.Success; + + case "/checked-": + settings.Checked = false; + return ParseResult.Success; + + case "/clscheck": + case "/clscheck+": + settings.VerifyClsCompliance = true; + return ParseResult.Success; + + case "/clscheck-": + settings.VerifyClsCompliance = false; + return ParseResult.Success; + + case "/unsafe": + case "/unsafe+": + settings.Unsafe = true; + return ParseResult.Success; + + case "/unsafe-": + settings.Unsafe = false; + return ParseResult.Success; + + case "/warnaserror": + case "/warnaserror+": + if (value.Length == 0) { + settings.WarningsAreErrors = true; + parser_settings.WarningsAreErrors = true; + } else { + if (!ProcessWarningsList (value, settings.AddWarningAsError)) + return ParseResult.Error; + } + return ParseResult.Success; + + case "/warnaserror-": + if (value.Length == 0) { + settings.WarningsAreErrors = false; + } else { + if (!ProcessWarningsList (value, settings.AddWarningOnly)) + return ParseResult.Error; + } + return ParseResult.Success; + + case "/warn": + case "/w": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + SetWarningLevel (value, settings); + return ParseResult.Success; + + case "/nowarn": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + if (!ProcessWarningsList (value, settings.SetIgnoreWarning)) + return ParseResult.Error; + + return ParseResult.Success; + + case "/noconfig": + settings.LoadDefaultReferences = false; + return ParseResult.Success; + + case "/platform": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + switch (value.ToLowerInvariant ()) { + case "arm": + settings.Platform = Platform.Arm; + break; + case "anycpu": + settings.Platform = Platform.AnyCPU; + break; + case "x86": + settings.Platform = Platform.X86; + break; + case "x64": + settings.Platform = Platform.X64; + break; + case "itanium": + settings.Platform = Platform.IA64; + break; + case "anycpu32bitpreferred": + settings.Platform = Platform.AnyCPU32Preferred; + break; + default: + report.Error (1672, "Invalid -platform option `{0}'. Valid options are `anycpu', `anycpu32bitpreferred', `arm', `x86', `x64' or `itanium'", + value); + return ParseResult.Error; + } + + return ParseResult.Success; + + case "/sdk": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + settings.SdkVersion = value; + return ParseResult.Success; + + // We just ignore this. + case "/errorreport": + case "/filealign": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + return ParseResult.Success; + + case "/helpinternal": + OtherFlags (); + return ParseResult.Stop; + + case "/help": + case "/?": + Usage (); + return ParseResult.Stop; + + case "/main": + case "/m": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + settings.MainClass = value; + return ParseResult.Success; + + case "/nostdlib": + case "/nostdlib+": + settings.StdLib = false; + return ParseResult.Success; + + case "/nostdlib-": + settings.StdLib = true; + return ParseResult.Success; + + case "/fullpaths": + settings.ShowFullPaths = true; + return ParseResult.Success; + + case "/keyfile": + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + settings.StrongNameKeyFile = value; + return ParseResult.Success; + + case "/keycontainer": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + settings.StrongNameKeyContainer = value; + return ParseResult.Success; + + case "/delaysign+": + case "/delaysign": + settings.StrongNameDelaySign = true; + return ParseResult.Success; + + case "/delaysign-": + settings.StrongNameDelaySign = false; + return ParseResult.Success; + + case "/langversion": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + switch (value.ToLowerInvariant ()) { + case "iso-1": + case "1": + settings.Version = LanguageVersion.ISO_1; + return ParseResult.Success; + case "default": + settings.Version = LanguageVersion.Default; + return ParseResult.Success; + case "2": + case "iso-2": + settings.Version = LanguageVersion.ISO_2; + return ParseResult.Success; + case "3": + settings.Version = LanguageVersion.V_3; + return ParseResult.Success; + case "4": + settings.Version = LanguageVersion.V_4; + return ParseResult.Success; + case "5": + settings.Version = LanguageVersion.V_5; + return ParseResult.Success; + case "6": + settings.Version = LanguageVersion.V_6; + return ParseResult.Success; + case "experimental": + settings.Version = LanguageVersion.Experimental; + return ParseResult.Success; + case "future": + report.Warning (8000, 1, "Language version `future' is no longer supported"); + goto case "6"; + } + + report.Error (1617, "Invalid -langversion option `{0}'. It must be `ISO-1', `ISO-2', Default or value in range 1 to 6", value); + return ParseResult.Error; + + case "/codepage": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + switch (value) { + case "utf8": + settings.Encoding = Encoding.UTF8; + break; + case "reset": + settings.Encoding = Encoding.Default; + break; + default: + try { + settings.Encoding = Encoding.GetEncoding (int.Parse (value)); + } catch { + report.Error (2016, "Code page `{0}' is invalid or not installed", value); + } + return ParseResult.Error; + } + return ParseResult.Success; + + case "runtimemetadataversion": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + settings.RuntimeMetadataVersion = value; + return ParseResult.Success; + + default: + return ParseResult.UnknownOption; + } + } + + // + // Currently handles the Unix-like command line options, but will be + // deprecated in favor of the CSCParseOption, which will also handle the + // options that start with a dash in the future. + // + ParseResult ParseOptionUnix (string arg, ref string[] args, ref int i, CompilerSettings settings) + { + switch (arg){ + case "-v": + settings.VerboseParserFlag++; + return ParseResult.Success; + + case "--version": + Version (); + return ParseResult.Stop; + + case "--parse": + settings.ParseOnly = true; + return ParseResult.Success; + + case "--main": case "-m": + report.Warning (-29, 1, "Compatibility: Use -main:CLASS instead of --main CLASS or -m CLASS"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + settings.MainClass = args[++i]; + return ParseResult.Success; + + case "--unsafe": + report.Warning (-29, 1, "Compatibility: Use -unsafe instead of --unsafe"); + settings.Unsafe = true; + return ParseResult.Success; + + case "/?": case "/h": case "/help": + case "--help": + Usage (); + return ParseResult.Stop; + + case "--define": + report.Warning (-29, 1, "Compatibility: Use -d:SYMBOL instead of --define SYMBOL"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + settings.AddConditionalSymbol (args [++i]); + return ParseResult.Success; + + case "--tokenize": + settings.TokenizeOnly = true; + return ParseResult.Success; + + case "-o": + case "--output": + report.Warning (-29, 1, "Compatibility: Use -out:FILE instead of --output FILE or -o FILE"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + settings.OutputFile = args[++i]; + return ParseResult.Success; + + case "--checked": + report.Warning (-29, 1, "Compatibility: Use -checked instead of --checked"); + settings.Checked = true; + return ParseResult.Success; + + case "--stacktrace": + settings.Stacktrace = true; + return ParseResult.Success; + + case "--linkresource": + case "--linkres": + report.Warning (-29, 1, "Compatibility: Use -linkres:VALUE instead of --linkres VALUE"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + AddResource (new AssemblyResource (args[++i], args[i]), settings); + return ParseResult.Success; + + case "--resource": + case "--res": + report.Warning (-29, 1, "Compatibility: Use -res:VALUE instead of --res VALUE"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + AddResource (new AssemblyResource (args[++i], args[i], true), settings); + return ParseResult.Success; + + case "--target": + report.Warning (-29, 1, "Compatibility: Use -target:KIND instead of --target KIND"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + string type = args [++i]; + switch (type){ + case "library": + settings.Target = Target.Library; + settings.TargetExt = ".dll"; + break; + + case "exe": + settings.Target = Target.Exe; + break; + + case "winexe": + settings.Target = Target.WinExe; + break; + + case "module": + settings.Target = Target.Module; + settings.TargetExt = ".dll"; + break; + default: + report.Error (2019, "Invalid target type for -target. Valid options are `exe', `winexe', `library' or `module'"); + break; + } + return ParseResult.Success; + + case "-r": + report.Warning (-29, 1, "Compatibility: Use -r:LIBRARY instead of -r library"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + string val = args [++i]; + int idx = val.IndexOf ('='); + if (idx > -1) { + string alias = val.Substring (0, idx); + string assembly = val.Substring (idx + 1); + AddAssemblyReference (alias, assembly, settings); + return ParseResult.Success; + } + + settings.AssemblyReferences.Add (val); + return ParseResult.Success; + + case "-L": + report.Warning (-29, 1, "Compatibility: Use -lib:ARG instead of --L arg"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + settings.ReferencesLookupPaths.Add (args [++i]); + return ParseResult.Success; + + case "--lint": + settings.EnhancedWarnings = true; + return ParseResult.Success; + + case "--nostdlib": + report.Warning (-29, 1, "Compatibility: Use -nostdlib instead of --nostdlib"); + settings.StdLib = false; + return ParseResult.Success; + + case "--nowarn": + report.Warning (-29, 1, "Compatibility: Use -nowarn instead of --nowarn"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + int warn = 0; + + try { + warn = int.Parse (args [++i]); + } catch { + Usage (); + Environment.Exit (1); + } + settings.SetIgnoreWarning (warn); + return ParseResult.Success; + + case "--wlevel": + report.Warning (-29, 1, "Compatibility: Use -warn:LEVEL instead of --wlevel LEVEL"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + SetWarningLevel (args [++i], settings); + return ParseResult.Success; + + case "--mcs-debug": + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + try { + settings.DebugFlags = int.Parse (args [++i]); + } catch { + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + return ParseResult.Success; + + case "--about": + About (); + return ParseResult.Stop; + + case "--recurse": + report.Warning (-29, 1, "Compatibility: Use -recurse:PATTERN option instead --recurse PATTERN"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + ProcessSourceFiles (args [++i], true, settings.SourceFiles); + return ParseResult.Success; + + case "--timestamp": + settings.Timestamps = true; + return ParseResult.Success; + + case "--debug": case "-g": + report.Warning (-29, 1, "Compatibility: Use -debug option instead of -g or --debug"); + settings.GenerateDebugInfo = true; + return ParseResult.Success; + + case "--noconfig": + report.Warning (-29, 1, "Compatibility: Use -noconfig option instead of --noconfig"); + settings.LoadDefaultReferences = false; + return ParseResult.Success; + + case "--metadata-only": + settings.WriteMetadataOnly = true; + return ParseResult.Success; + + case "--break-on-ice": + settings.BreakOnInternalError = true; + return ParseResult.Success; + + default: + if (arg.StartsWith ("--fatal", StringComparison.Ordinal)){ + int fatal = 1; + if (arg.StartsWith ("--fatal=", StringComparison.Ordinal)) + int.TryParse (arg.Substring (8), out fatal); + + settings.FatalCounter = fatal; + return ParseResult.Success; + } + if (arg.StartsWith ("--runtime:", StringComparison.Ordinal)) { + string version = arg.Substring (10); + + switch (version) { + case "v1": + case "V1": + settings.StdLibRuntimeVersion = RuntimeVersion.v1; + break; + case "v2": + case "V2": + settings.StdLibRuntimeVersion = RuntimeVersion.v2; + break; + case "v4": + case "V4": + settings.StdLibRuntimeVersion = RuntimeVersion.v4; + break; + } + return ParseResult.Success; + } + + return ParseResult.UnknownOption; + } + } + + void SetWarningLevel (string s, CompilerSettings settings) + { + int level = -1; + + try { + level = int.Parse (s); + } catch { + } + if (level < 0 || level > 4) { + report.Error (1900, "Warning level must be in the range 0-4"); + return; + } + settings.WarningLevel = level; + } + + // + // Given a path specification, splits the path from the file/pattern + // + static void SplitPathAndPattern (string spec, out string path, out string pattern) + { + int p = spec.LastIndexOf ('/'); + if (p != -1) { + // + // Windows does not like /file.cs, switch that to: + // "\", "file.cs" + // + if (p == 0) { + path = "\\"; + pattern = spec.Substring (1); + } else { + path = spec.Substring (0, p); + pattern = spec.Substring (p + 1); + } + return; + } + + p = spec.LastIndexOf ('\\'); + if (p != -1) { + path = spec.Substring (0, p); + pattern = spec.Substring (p + 1); + return; + } + + path = "."; + pattern = spec; + } + + void Usage () + { + output.WriteLine ( + "Mono C# compiler, Copyright 2001-2011 Novell, Inc., Copyright 2011-2012 Xamarin, Inc\n" + + "mcs [options] source-files\n" + + " --about About the Mono C# compiler\n" + + " -addmodule:M1[,Mn] Adds the module to the generated assembly\n" + + " -checked[+|-] Sets default aritmetic overflow context\n" + + " -clscheck[+|-] Disables CLS Compliance verifications\n" + + " -codepage:ID Sets code page to the one in ID (number, utf8, reset)\n" + + " -define:S1[;S2] Defines one or more conditional symbols (short: -d)\n" + + " -debug[+|-], -g Generate debugging information\n" + + " -delaysign[+|-] Only insert the public key into the assembly (no signing)\n" + + " -doc:FILE Process documentation comments to XML file\n" + + " -fullpaths Any issued error or warning uses absolute file path\n" + + " -help Lists all compiler options (short: -?)\n" + + " -keycontainer:NAME The key pair container used to sign the output assembly\n" + + " -keyfile:FILE The key file used to strongname the ouput assembly\n" + + " -langversion:TEXT Specifies language version: ISO-1, ISO-2, 3, 4, 5, Default or Future\n" + + " -lib:PATH1[,PATHn] Specifies the location of referenced assemblies\n" + + " -main:CLASS Specifies the class with the Main method (short: -m)\n" + + " -noconfig Disables implicitly referenced assemblies\n" + + " -nostdlib[+|-] Does not reference mscorlib.dll library\n" + + " -nowarn:W1[,Wn] Suppress one or more compiler warnings\n" + + " -optimize[+|-] Enables advanced compiler optimizations (short: -o)\n" + + " -out:FILE Specifies output assembly name\n" + + " -pkg:P1[,Pn] References packages P1..Pn\n" + + " -platform:ARCH Specifies the target platform of the output assembly\n" + + " ARCH can be one of: anycpu, anycpu32bitpreferred, arm,\n" + + " x86, x64 or itanium. The default is anycpu.\n" + + " -recurse:SPEC Recursively compiles files according to SPEC pattern\n" + + " -reference:A1[,An] Imports metadata from the specified assembly (short: -r)\n" + + " -reference:ALIAS=A Imports metadata using specified extern alias (short: -r)\n" + + " -sdk:VERSION Specifies SDK version of referenced assemblies\n" + + " VERSION can be one of: 2, 4, 4.5 (default) or a custom value\n" + + " -target:KIND Specifies the format of the output assembly (short: -t)\n" + + " KIND can be one of: exe, winexe, library, module\n" + + " -unsafe[+|-] Allows to compile code which uses unsafe keyword\n" + + " -warnaserror[+|-] Treats all warnings as errors\n" + + " -warnaserror[+|-]:W1[,Wn] Treats one or more compiler warnings as errors\n" + + " -warn:0-4 Sets warning level, the default is 4 (short -w:)\n" + + " -helpinternal Shows internal and advanced compiler options\n" + + "\n" + + "Resources:\n" + + " -linkresource:FILE[,ID] Links FILE as a resource (short: -linkres)\n" + + " -resource:FILE[,ID] Embed FILE as a resource (short: -res)\n" + + " -win32res:FILE Specifies Win32 resource file (.res)\n" + + " -win32icon:FILE Use this icon for the output\n" + + " @file Read response file for more options\n\n" + + "Options can be of the form -option or /option"); + } + + void Version () + { + string version = System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType.Assembly.GetName ().Version.ToString (); + output.WriteLine ("Mono C# compiler version {0}", version); + } + } + + public class RootContext + { + // + // Contains the parsed tree + // + static ModuleContainer root; + + static public ModuleContainer ToplevelTypes { + get { return root; } + set { root = value; } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/statement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/statement.cs new file mode 100644 index 000000000..18249431b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/statement.cs @@ -0,0 +1,8074 @@ +// +// statement.cs: Statement representation for the IL tree. +// +// Authors: +// Miguel de Icaza (miguel@ximian.com) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003, 2004 Novell, Inc. +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + public abstract class Statement { + public Location loc; + protected bool reachable; + + public bool IsUnreachable { + get { + return !reachable; + } + } + + /// + /// Resolves the statement, true means that all sub-statements + /// did resolve ok. + /// + public virtual bool Resolve (BlockContext bc) + { + return true; + } + + /// + /// Return value indicates whether all code paths emitted return. + /// + protected abstract void DoEmit (EmitContext ec); + + public virtual void Emit (EmitContext ec) + { + ec.Mark (loc); + DoEmit (ec); + + if (ec.StatementEpilogue != null) { + ec.EmitEpilogue (); + } + } + + // + // This routine must be overrided in derived classes and make copies + // of all the data that might be modified if resolved + // + protected abstract void CloneTo (CloneContext clonectx, Statement target); + + public Statement Clone (CloneContext clonectx) + { + Statement s = (Statement) this.MemberwiseClone (); + CloneTo (clonectx, s); + return s; + } + + public virtual Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree"); + return null; + } + + public virtual object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + // + // Return value indicates whether statement has unreachable end + // + protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc); + + public bool FlowAnalysis (FlowAnalysisContext fc) + { + if (reachable) { + fc.UnreachableReported = false; + var res = DoFlowAnalysis (fc); + return res; + } + + // + // Special handling cases + // + if (this is Block) { + return DoFlowAnalysis (fc); + } + + if (this is EmptyStatement || loc.IsNull) + return true; + + if (fc.UnreachableReported) + return true; + + fc.Report.Warning (162, 2, loc, "Unreachable code detected"); + fc.UnreachableReported = true; + return true; + } + + public virtual Reachability MarkReachable (Reachability rc) + { + if (!rc.IsUnreachable) + reachable = true; + + return rc; + } + + protected void CheckExitBoundaries (BlockContext bc, Block scope) + { + if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) { + bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method"); + return; + } + + for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) { + if (b.IsFinallyBlock) { + Error_FinallyClauseExit (bc); + break; + } + } + } + + protected void Error_FinallyClauseExit (BlockContext bc) + { + bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause"); + } + } + + public sealed class EmptyStatement : Statement + { + public EmptyStatement (Location loc) + { + this.loc = loc; + } + + public override bool Resolve (BlockContext ec) + { + return true; + } + + public override void Emit (EmitContext ec) + { + } + + protected override void DoEmit (EmitContext ec) + { + throw new NotSupportedException (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // nothing needed. + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class If : Statement { + Expression expr; + public Statement TrueStatement; + public Statement FalseStatement; + + bool true_returns, false_returns; + + public If (Expression bool_expr, Statement true_statement, Location l) + : this (bool_expr, true_statement, null, l) + { + } + + public If (Expression bool_expr, + Statement true_statement, + Statement false_statement, + Location l) + { + this.expr = bool_expr; + TrueStatement = true_statement; + FalseStatement = false_statement; + loc = l; + } + + public Expression Expr { + get { + return this.expr; + } + } + + public override bool Resolve (BlockContext ec) + { + expr = expr.Resolve (ec); + + var ok = TrueStatement.Resolve (ec); + + if (FalseStatement != null) { + ok &= FalseStatement.Resolve (ec); + } + + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + Label false_target = ec.DefineLabel (); + Label end; + + // + // If we're a boolean constant, Resolve() already + // eliminated dead code for us. + // + Constant c = expr as Constant; + if (c != null){ + c.EmitSideEffect (ec); + + if (!c.IsDefaultValue) + TrueStatement.Emit (ec); + else if (FalseStatement != null) + FalseStatement.Emit (ec); + + return; + } + + expr.EmitBranchable (ec, false_target, false); + + TrueStatement.Emit (ec); + + if (FalseStatement != null){ + bool branch_emitted = false; + + end = ec.DefineLabel (); + if (!true_returns){ + ec.Emit (OpCodes.Br, end); + branch_emitted = true; + } + + ec.MarkLabel (false_target); + FalseStatement.Emit (ec); + + if (branch_emitted) + ec.MarkLabel (end); + } else { + ec.MarkLabel (false_target); + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysisConditional (fc); + + var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse); + + fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue; + + var res = TrueStatement.FlowAnalysis (fc); + + if (FalseStatement == null) { + var c = expr as Constant; + if (c != null && !c.IsDefaultValue) + return true_returns; + + if (true_returns) + fc.DefiniteAssignment = da_false; + else + fc.DefiniteAssignment &= da_false; + + return false; + } + + if (true_returns) { + fc.DefiniteAssignment = da_false; + return FalseStatement.FlowAnalysis (fc); + } + + var da_true = fc.DefiniteAssignment; + + fc.DefiniteAssignment = da_false; + res &= FalseStatement.FlowAnalysis (fc); + + if (!TrueStatement.IsUnreachable) { + if (false_returns || FalseStatement.IsUnreachable) + fc.DefiniteAssignment = da_true; + else + fc.DefiniteAssignment &= da_true; + } + + return res; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + base.MarkReachable (rc); + + var c = expr as Constant; + if (c != null) { + bool take = !c.IsDefaultValue; + if (take) { + rc = TrueStatement.MarkReachable (rc); + } else { + if (FalseStatement != null) + rc = FalseStatement.MarkReachable (rc); + } + + return rc; + } + + var true_rc = TrueStatement.MarkReachable (rc); + true_returns = true_rc.IsUnreachable; + + if (FalseStatement == null) + return rc; + + var false_rc = FalseStatement.MarkReachable (rc); + false_returns = false_rc.IsUnreachable; + + return true_rc & false_rc; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + If target = (If) t; + + target.expr = expr.Clone (clonectx); + target.TrueStatement = TrueStatement.Clone (clonectx); + if (FalseStatement != null) + target.FalseStatement = FalseStatement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Do : LoopStatement + { + public Expression expr; + bool iterator_reachable, end_reachable; + + public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation) + : base (statement) + { + expr = bool_expr; + loc = doLocation; + WhileLocation = whileLocation; + } + + public Location WhileLocation { + get; private set; + } + + public override bool Resolve (BlockContext bc) + { + var ok = base.Resolve (bc); + + expr = expr.Resolve (bc); + + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + Label loop = ec.DefineLabel (); + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + + ec.LoopBegin = ec.DefineLabel (); + ec.LoopEnd = ec.DefineLabel (); + + ec.MarkLabel (loop); + Statement.Emit (ec); + ec.MarkLabel (ec.LoopBegin); + + // Mark start of while condition + ec.Mark (WhileLocation); + + // + // Dead code elimination + // + if (expr is Constant) { + bool res = !((Constant) expr).IsDefaultValue; + + expr.EmitSideEffect (ec); + if (res) + ec.Emit (OpCodes.Br, loop); + } else { + expr.EmitBranchable (ec, loop, true); + } + + ec.MarkLabel (ec.LoopEnd); + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + var res = Statement.FlowAnalysis (fc); + + expr.FlowAnalysisConditional (fc); + + fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse; + + if (res && !iterator_reachable) + return !end_reachable; + + if (!end_reachable) { + var c = expr as Constant; + if (c != null && !c.IsDefaultValue) + return true; + } + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + var body_rc = Statement.MarkReachable (rc); + + if (body_rc.IsUnreachable && !iterator_reachable) { + expr = new UnreachableExpression (expr); + return end_reachable ? rc : Reachability.CreateUnreachable (); + } + + if (!end_reachable) { + var c = expr as Constant; + if (c != null && !c.IsDefaultValue) + return Reachability.CreateUnreachable (); + } + + return rc; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Do target = (Do) t; + + target.Statement = Statement.Clone (clonectx); + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void SetEndReachable () + { + end_reachable = true; + } + + public override void SetIteratorReachable () + { + iterator_reachable = true; + } + } + + public class While : LoopStatement + { + public Expression expr; + bool empty, infinite, end_reachable; + List end_reachable_das; + + public While (BooleanExpression bool_expr, Statement statement, Location l) + : base (statement) + { + this.expr = bool_expr; + loc = l; + } + + public override bool Resolve (BlockContext bc) + { + bool ok = true; + + expr = expr.Resolve (bc); + if (expr == null) + ok = false; + + var c = expr as Constant; + if (c != null) { + empty = c.IsDefaultValue; + infinite = !empty; + } + + ok &= base.Resolve (bc); + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + if (empty) { + expr.EmitSideEffect (ec); + return; + } + + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + + ec.LoopBegin = ec.DefineLabel (); + ec.LoopEnd = ec.DefineLabel (); + + // + // Inform whether we are infinite or not + // + if (expr is Constant) { + // expr is 'true', since the 'empty' case above handles the 'false' case + ec.MarkLabel (ec.LoopBegin); + + if (ec.EmitAccurateDebugInfo) + ec.Emit (OpCodes.Nop); + + expr.EmitSideEffect (ec); + Statement.Emit (ec); + ec.Emit (OpCodes.Br, ec.LoopBegin); + + // + // Inform that we are infinite (ie, `we return'), only + // if we do not `break' inside the code. + // + ec.MarkLabel (ec.LoopEnd); + } else { + Label while_loop = ec.DefineLabel (); + + ec.Emit (OpCodes.Br, ec.LoopBegin); + ec.MarkLabel (while_loop); + + Statement.Emit (ec); + + ec.MarkLabel (ec.LoopBegin); + + ec.Mark (loc); + expr.EmitBranchable (ec, while_loop, true); + + ec.MarkLabel (ec.LoopEnd); + } + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysisConditional (fc); + + fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue; + var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse); + + Statement.FlowAnalysis (fc); + + // + // Special case infinite while with breaks + // + if (end_reachable_das != null) { + da_false = DefiniteAssignmentBitSet.And (end_reachable_das); + end_reachable_das = null; + } + + fc.DefiniteAssignment = da_false; + + if (infinite && !end_reachable) + return true; + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + base.MarkReachable (rc); + + // + // Special case unreachable while body + // + if (empty) { + Statement.MarkReachable (Reachability.CreateUnreachable ()); + return rc; + } + + Statement.MarkReachable (rc); + + // + // When infinite while end is unreachable via break anything what follows is unreachable too + // + if (infinite && !end_reachable) + return Reachability.CreateUnreachable (); + + return rc; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + While target = (While) t; + + target.expr = expr.Clone (clonectx); + target.Statement = Statement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void AddEndDefiniteAssignment (FlowAnalysisContext fc) + { + if (!infinite) + return; + + if (end_reachable_das == null) + end_reachable_das = new List (); + + end_reachable_das.Add (fc.DefiniteAssignment); + } + + public override void SetEndReachable () + { + end_reachable = true; + } + } + + public class For : LoopStatement + { + bool infinite, empty, iterator_reachable, end_reachable; + List end_reachable_das; + + public For (Location l) + : base (null) + { + loc = l; + } + + public Statement Initializer { + get; set; + } + + public Expression Condition { + get; set; + } + + public Statement Iterator { + get; set; + } + + public override bool Resolve (BlockContext bc) + { + Initializer.Resolve (bc); + + if (Condition != null) { + Condition = Condition.Resolve (bc); + var condition_constant = Condition as Constant; + if (condition_constant != null) { + if (condition_constant.IsDefaultValue) { + empty = true; + } else { + infinite = true; + } + } + } else { + infinite = true; + } + + return base.Resolve (bc) && Iterator.Resolve (bc); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + Initializer.FlowAnalysis (fc); + + DefiniteAssignmentBitSet da_false; + if (Condition != null) { + Condition.FlowAnalysisConditional (fc); + fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue; + da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse); + } else { + da_false = fc.BranchDefiniteAssignment (); + } + + Statement.FlowAnalysis (fc); + + Iterator.FlowAnalysis (fc); + + // + // Special case infinite for with breaks + // + if (end_reachable_das != null) { + da_false = DefiniteAssignmentBitSet.And (end_reachable_das); + end_reachable_das = null; + } + + fc.DefiniteAssignment = da_false; + + if (infinite && !end_reachable) + return true; + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + Initializer.MarkReachable (rc); + + var body_rc = Statement.MarkReachable (rc); + if (!body_rc.IsUnreachable || iterator_reachable) { + Iterator.MarkReachable (rc); + } + + // + // When infinite for end is unreachable via break anything what follows is unreachable too + // + if (infinite && !end_reachable) { + return Reachability.CreateUnreachable (); + } + + return rc; + } + + protected override void DoEmit (EmitContext ec) + { + if (Initializer != null) + Initializer.Emit (ec); + + if (empty) { + Condition.EmitSideEffect (ec); + return; + } + + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + Label loop = ec.DefineLabel (); + Label test = ec.DefineLabel (); + + ec.LoopBegin = ec.DefineLabel (); + ec.LoopEnd = ec.DefineLabel (); + + ec.Emit (OpCodes.Br, test); + ec.MarkLabel (loop); + Statement.Emit (ec); + + ec.MarkLabel (ec.LoopBegin); + Iterator.Emit (ec); + + ec.MarkLabel (test); + // + // If test is null, there is no test, and we are just + // an infinite loop + // + if (Condition != null) { + ec.Mark (Condition.Location); + + // + // The Resolve code already catches the case for + // Test == Constant (false) so we know that + // this is true + // + if (Condition is Constant) { + Condition.EmitSideEffect (ec); + ec.Emit (OpCodes.Br, loop); + } else { + Condition.EmitBranchable (ec, loop, true); + } + + } else + ec.Emit (OpCodes.Br, loop); + ec.MarkLabel (ec.LoopEnd); + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + For target = (For) t; + + if (Initializer != null) + target.Initializer = Initializer.Clone (clonectx); + if (Condition != null) + target.Condition = Condition.Clone (clonectx); + if (Iterator != null) + target.Iterator = Iterator.Clone (clonectx); + target.Statement = Statement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void AddEndDefiniteAssignment (FlowAnalysisContext fc) + { + if (!infinite) + return; + + if (end_reachable_das == null) + end_reachable_das = new List (); + + end_reachable_das.Add (fc.DefiniteAssignment); + } + + public override void SetEndReachable () + { + end_reachable = true; + } + + public override void SetIteratorReachable () + { + iterator_reachable = true; + } + } + + public abstract class LoopStatement : Statement + { + protected LoopStatement (Statement statement) + { + Statement = statement; + } + + public Statement Statement { get; set; } + + public override bool Resolve (BlockContext bc) + { + var prev_loop = bc.EnclosingLoop; + var prev_los = bc.EnclosingLoopOrSwitch; + bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this; + var ok = Statement.Resolve (bc); + bc.EnclosingLoopOrSwitch = prev_los; + bc.EnclosingLoop = prev_loop; + + return ok; + } + + // + // Needed by possibly infinite loops statements (for, while) and switch statment + // + public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc) + { + } + + public virtual void SetEndReachable () + { + } + + public virtual void SetIteratorReachable () + { + } + } + + public class StatementExpression : Statement + { + ExpressionStatement expr; + + public StatementExpression (ExpressionStatement expr) + { + this.expr = expr; + loc = expr.StartLocation; + } + + public StatementExpression (ExpressionStatement expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public ExpressionStatement Expr { + get { + return this.expr; + } + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + StatementExpression target = (StatementExpression) t; + target.expr = (ExpressionStatement) expr.Clone (clonectx); + } + + protected override void DoEmit (EmitContext ec) + { + expr.EmitStatement (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + expr.MarkReachable (rc); + return rc; + } + + public override bool Resolve (BlockContext ec) + { + expr = expr.ResolveStatement (ec); + return expr != null; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class StatementErrorExpression : Statement + { + Expression expr; + + public StatementErrorExpression (Expression expr) + { + this.expr = expr; + this.loc = expr.StartLocation; + } + + public Expression Expr { + get { + return expr; + } + } + + public override bool Resolve (BlockContext bc) + { + expr.Error_InvalidExpressionStatement (bc); + return true; + } + + protected override void DoEmit (EmitContext ec) + { + throw new NotSupportedException (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + var t = (StatementErrorExpression) target; + + t.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Simple version of statement list not requiring a block + // + public class StatementList : Statement + { + List statements; + + public StatementList (Statement first, Statement second) + { + statements = new List { first, second }; + } + + #region Properties + public IList Statements { + get { + return statements; + } + } + #endregion + + public void Add (Statement statement) + { + statements.Add (statement); + } + + public override bool Resolve (BlockContext ec) + { + foreach (var s in statements) + s.Resolve (ec); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + foreach (var s in statements) + s.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + foreach (var s in statements) + s.FlowAnalysis (fc); + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + Reachability res = rc; + foreach (var s in statements) + res = s.MarkReachable (rc); + + return res; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + StatementList t = (StatementList) target; + + t.statements = new List (statements.Count); + foreach (Statement s in statements) + t.statements.Add (s.Clone (clonectx)); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // For statements which require special handling when inside try or catch block + // + public abstract class ExitStatement : Statement + { + protected bool unwind_protect; + + protected abstract bool DoResolve (BlockContext bc); + protected abstract bool IsLocalExit { get; } + + public override bool Resolve (BlockContext bc) + { + var res = DoResolve (bc); + + if (!IsLocalExit) { + // + // We are inside finally scope but is it the scope we are exiting + // + if (bc.HasSet (ResolveContext.Options.FinallyScope)) { + + for (var b = bc.CurrentBlock; b != null; b = b.Parent) { + if (b.IsFinallyBlock) { + Error_FinallyClauseExit (bc); + break; + } + + if (b is ParametersBlock) + break; + } + } + } + + unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope); + return res; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (IsLocalExit) + return true; + + if (fc.TryFinally != null) { + fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment)); + } else { + fc.ParametersBlock.CheckControlExit (fc); + } + + return true; + } + } + + /// + /// Implements the return statement + /// + public class Return : ExitStatement + { + Expression expr; + + public Return (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + #region Properties + + public Expression Expr { + get { + return expr; + } + protected set { + expr = value; + } + } + + protected override bool IsLocalExit { + get { + return false; + } + } + + #endregion + + protected override bool DoResolve (BlockContext ec) + { + var block_return_type = ec.ReturnType; + + if (expr == null) { + if (block_return_type.Kind == MemberKind.Void) + return true; + + // + // Return must not be followed by an expression when + // the method return type is Task + // + if (ec.CurrentAnonymousMethod is AsyncInitializer) { + var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey; + if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) { + // + // Extra trick not to emit ret/leave inside awaiter body + // + expr = EmptyExpression.Null; + return true; + } + + if (storey.ReturnType.IsGenericTask) + block_return_type = storey.ReturnType.TypeArguments[0]; + } + + if (ec.CurrentIterator != null) { + Error_ReturnFromIterator (ec); + } else if (block_return_type != InternalType.ErrorType) { + ec.Report.Error (126, loc, + "An object of a type convertible to `{0}' is required for the return statement", + block_return_type.GetSignatureForError ()); + } + + return false; + } + + expr = expr.Resolve (ec); + + AnonymousExpression am = ec.CurrentAnonymousMethod; + if (am == null) { + if (block_return_type.Kind == MemberKind.Void) { + ec.Report.Error (127, loc, + "`{0}': A return keyword must not be followed by any expression when method returns void", + ec.GetSignatureForError ()); + + return false; + } + } else { + if (am.IsIterator) { + Error_ReturnFromIterator (ec); + return false; + } + + var async_block = am as AsyncInitializer; + if (async_block != null) { + if (expr != null) { + var storey = (AsyncTaskStorey) am.Storey; + var async_type = storey.ReturnType; + + if (async_type == null && async_block.ReturnTypeInference != null) { + if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn)) + ec.Report.Error (4029, loc, "Cannot return an expression of type `void'"); + else + async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type); + return true; + } + + if (async_type.Kind == MemberKind.Void) { + ec.Report.Error (8030, loc, + "Anonymous function or lambda expression converted to a void returning delegate cannot return a value"); + return false; + } + + if (!async_type.IsGenericTask) { + if (this is ContextualReturn) + return true; + + if (async_block.DelegateType != null) { + ec.Report.Error (8031, loc, + "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task'"); + } else { + ec.Report.Error (1997, loc, + "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task' return type", + ec.GetSignatureForError ()); + } + return false; + } + + // + // The return type is actually Task type argument + // + if (expr.Type == async_type) { + ec.Report.Error (4016, loc, + "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'", + ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ()); + } else { + block_return_type = async_type.TypeArguments[0]; + } + } + } else { + if (block_return_type.Kind == MemberKind.Void) { + ec.Report.Error (8030, loc, + "Anonymous function or lambda expression converted to a void returning delegate cannot return a value"); + return false; + } + + var l = am as AnonymousMethodBody; + if (l != null && expr != null) { + if (l.ReturnTypeInference != null) { + l.ReturnTypeInference.AddCommonTypeBound (expr.Type); + return true; + } + + // + // Try to optimize simple lambda. Only when optimizations are enabled not to cause + // unexpected debugging experience + // + if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) { + l.DirectMethodGroupConversion = expr.CanReduceLambda (l); + } + } + } + } + + if (expr == null) + return false; + + if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) { + expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc); + + if (expr == null) { + if (am != null && block_return_type == ec.ReturnType) { + ec.Report.Error (1662, loc, + "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type", + am.ContainerType, am.GetSignatureForError ()); + } + return false; + } + } + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + if (expr != null) { + + var async_body = ec.CurrentAnonymousMethod as AsyncInitializer; + if (async_body != null) { + var storey = (AsyncTaskStorey)async_body.Storey; + Label exit_label = async_body.BodyEnd; + + // + // It's null for await without async + // + if (storey.HoistedReturnValue != null) { + // + // Special case hoisted return value (happens in try/finally scenario) + // + if (ec.TryFinallyUnwind != null) { + if (storey.HoistedReturnValue is VariableReference) { + storey.HoistedReturnValue = ec.GetTemporaryField (storey.HoistedReturnValue.Type); + } + + exit_label = TryFinally.EmitRedirectedReturn (ec, async_body); + } + + var async_return = (IAssignMethod)storey.HoistedReturnValue; + async_return.EmitAssign (ec, expr, false, false); + ec.EmitEpilogue (); + } else { + expr.Emit (ec); + + if (ec.TryFinallyUnwind != null) + exit_label = TryFinally.EmitRedirectedReturn (ec, async_body); + } + + ec.Emit (OpCodes.Leave, exit_label); + return; + } + + expr.Emit (ec); + ec.EmitEpilogue (); + + if (unwind_protect || ec.EmitAccurateDebugInfo) + ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ()); + } + + if (unwind_protect) { + ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ()); + } else if (ec.EmitAccurateDebugInfo) { + ec.Emit (OpCodes.Br, ec.CreateReturnLabel ()); + } else { + ec.Emit (OpCodes.Ret); + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (expr != null) + expr.FlowAnalysis (fc); + + base.DoFlowAnalysis (fc); + return true; + } + + void Error_ReturnFromIterator (ResolveContext rc) + { + rc.Report.Error (1622, loc, + "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration"); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Return target = (Return) t; + // It's null for simple return; + if (expr != null) + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Goto : ExitStatement + { + string target; + LabeledStatement label; + TryFinally try_finally; + + public Goto (string label, Location l) + { + loc = l; + target = label; + } + + public string Target { + get { return target; } + } + + protected override bool IsLocalExit { + get { + return true; + } + } + + protected override bool DoResolve (BlockContext bc) + { + label = bc.CurrentBlock.LookupLabel (target); + if (label == null) { + Error_UnknownLabel (bc, target, loc); + return false; + } + + try_finally = bc.CurrentTryBlock as TryFinally; + + CheckExitBoundaries (bc, label.Block); + + return true; + } + + public static void Error_UnknownLabel (BlockContext bc, string label, Location loc) + { + bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement", + label); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (fc.LabelStack == null) { + fc.LabelStack = new List (); + } else if (fc.LabelStack.Contains (label)) { + return true; + } + + fc.LabelStack.Add (label); + label.Block.ScanGotoJump (label, fc); + fc.LabelStack.Remove (label); + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + base.MarkReachable (rc); + + if (try_finally != null) { + if (try_finally.FinallyBlock.HasReachableClosingBrace) { + label.AddGotoReference (rc, false); + } else { + label.AddGotoReference (rc, true); + } + } else { + label.AddGotoReference (rc, false); + } + + return Reachability.CreateUnreachable (); + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // Nothing to clone + } + + protected override void DoEmit (EmitContext ec) + { + if (label == null) + throw new InternalErrorException ("goto emitted before target resolved"); + + Label l = label.LabelTarget (ec); + + if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) { + var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod; + l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block); + } + + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l); + } + + bool IsLeavingFinally (Block labelBlock) + { + var b = try_finally.Statement as Block; + while (b != null) { + if (b == labelBlock) + return true; + + b = b.Parent; + } + + return false; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class LabeledStatement : Statement { + string name; + bool defined; + bool referenced; + bool finalTarget; + Label label; + Block block; + + public LabeledStatement (string name, Block block, Location l) + { + this.name = name; + this.block = block; + this.loc = l; + } + + public Label LabelTarget (EmitContext ec) + { + if (defined) + return label; + + label = ec.DefineLabel (); + defined = true; + return label; + } + + public Block Block { + get { + return block; + } + } + + public string Name { + get { return name; } + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + var t = (LabeledStatement) target; + + t.block = clonectx.RemapBlockCopy (block); + } + + public override bool Resolve (BlockContext bc) + { + return true; + } + + protected override void DoEmit (EmitContext ec) + { + LabelTarget (ec); + ec.MarkLabel (label); + + if (finalTarget) + ec.Emit (OpCodes.Br_S, label); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (!referenced) { + fc.Report.Warning (164, 2, loc, "This label has not been referenced"); + } + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + if (referenced) + rc = new Reachability (); + + return rc; + } + + public void AddGotoReference (Reachability rc, bool finalTarget) + { + if (referenced) + return; + + referenced = true; + MarkReachable (rc); + + // + // Label is final target when goto jumps out of try block with + // finally clause. In that case we need leave with target but in C# + // terms the label is unreachable. Using finalTarget we emit + // explicit label not just marker + // + if (finalTarget) { + this.finalTarget = true; + return; + } + + block.ScanGotoJump (this); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + + /// + /// `goto default' statement + /// + public class GotoDefault : SwitchGoto + { + public GotoDefault (Location l) + : base (l) + { + } + + public override bool Resolve (BlockContext bc) + { + if (bc.Switch == null) { + Error_GotoCaseRequiresSwitchBlock (bc); + return false; + } + + bc.Switch.RegisterGotoCase (null, null); + base.Resolve (bc); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec)); + } + + public override Reachability MarkReachable (Reachability rc) + { + if (!rc.IsUnreachable) { + var label = switch_statement.DefaultLabel; + if (label.IsUnreachable) { + label.MarkReachable (rc); + switch_statement.Block.ScanGotoJump (label); + } + } + + return base.MarkReachable (rc); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// `goto case' statement + /// + public class GotoCase : SwitchGoto + { + Expression expr; + + public GotoCase (Expression e, Location l) + : base (l) + { + expr = e; + } + + public Expression Expr { + get { + return expr; + } + } + + public SwitchLabel Label { get; set; } + + public override bool Resolve (BlockContext ec) + { + if (ec.Switch == null) { + Error_GotoCaseRequiresSwitchBlock (ec); + return false; + } + + Constant c = expr.ResolveLabelConstant (ec); + if (c == null) { + return false; + } + + Constant res; + if (ec.Switch.IsNullable && c is NullLiteral) { + res = c; + } else { + TypeSpec type = ec.Switch.SwitchType; + res = c.Reduce (ec, type); + if (res == null) { + c.Error_ValueCannotBeConverted (ec, type, true); + return false; + } + + if (!Convert.ImplicitStandardConversionExists (c, type)) + ec.Report.Warning (469, 2, loc, + "The `goto case' value is not implicitly convertible to type `{0}'", + type.GetSignatureForError ()); + + } + + ec.Switch.RegisterGotoCase (this, res); + base.Resolve (ec); + expr = res; + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec)); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + GotoCase target = (GotoCase) t; + + target.expr = expr.Clone (clonectx); + } + + public override Reachability MarkReachable (Reachability rc) + { + if (!rc.IsUnreachable) { + var label = switch_statement.FindLabel ((Constant) expr); + if (label.IsUnreachable) { + label.MarkReachable (rc); + switch_statement.Block.ScanGotoJump (label); + } + } + + return base.MarkReachable (rc); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public abstract class SwitchGoto : Statement + { + protected bool unwind_protect; + protected Switch switch_statement; + + protected SwitchGoto (Location loc) + { + this.loc = loc; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // Nothing to clone + } + + public override bool Resolve (BlockContext bc) + { + CheckExitBoundaries (bc, bc.Switch.Block); + + unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope); + switch_statement = bc.Switch; + + return true; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + + protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc) + { + bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement"); + } + } + + public class Throw : Statement { + Expression expr; + + public Throw (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + public Expression Expr { + get { + return this.expr; + } + } + + public override bool Resolve (BlockContext ec) + { + if (expr == null) { + if (!ec.HasSet (ResolveContext.Options.CatchScope)) { + ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause"); + } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) { + for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) { + if (b.IsFinallyBlock) { + ec.Report.Error (724, loc, + "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause"); + break; + } + } + } + + return true; + } + + expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue); + + if (expr == null) + return false; + + var et = ec.BuiltinTypes.Exception; + if (Convert.ImplicitConversionExists (ec, expr, et)) + expr = Convert.ImplicitConversion (ec, expr, et, loc); + else + ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception"); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + if (expr == null) { + var atv = ec.AsyncThrowVariable; + if (atv != null) { + if (atv.HoistedVariant != null) { + atv.HoistedVariant.Emit (ec); + } else { + atv.Emit (ec); + } + + ec.Emit (OpCodes.Throw); + } else { + ec.Emit (OpCodes.Rethrow); + } + } else { + expr.Emit (ec); + + ec.Emit (OpCodes.Throw); + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (expr != null) + expr.FlowAnalysis (fc); + + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Throw target = (Throw) t; + + if (expr != null) + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Break : LocalExitStatement + { + public Break (Location l) + : base (l) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + protected override void DoEmit (EmitContext ec) + { + var l = ec.LoopEnd; + + if (ec.TryFinallyUnwind != null) { + var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod; + l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block); + } + + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + enclosing_loop.AddEndDefiniteAssignment (fc); + return true; + } + + protected override bool DoResolve (BlockContext bc) + { + enclosing_loop = bc.EnclosingLoopOrSwitch; + return base.DoResolve (bc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + if (!rc.IsUnreachable) + enclosing_loop.SetEndReachable (); + + return Reachability.CreateUnreachable (); + } + } + + public class Continue : LocalExitStatement + { + public Continue (Location l) + : base (l) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + + protected override void DoEmit (EmitContext ec) + { + var l = ec.LoopBegin; + + if (ec.TryFinallyUnwind != null) { + var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod; + l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block); + } + + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l); + } + + protected override bool DoResolve (BlockContext bc) + { + enclosing_loop = bc.EnclosingLoop; + return base.DoResolve (bc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + if (!rc.IsUnreachable) + enclosing_loop.SetIteratorReachable (); + + return Reachability.CreateUnreachable (); + } + } + + public abstract class LocalExitStatement : ExitStatement + { + protected LoopStatement enclosing_loop; + + protected LocalExitStatement (Location loc) + { + this.loc = loc; + } + + protected override bool IsLocalExit { + get { + return true; + } + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + // nothing needed. + } + + protected override bool DoResolve (BlockContext bc) + { + if (enclosing_loop == null) { + bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue"); + return false; + } + + var block = enclosing_loop.Statement as Block; + + // Don't need to do extra checks for simple statements loops + if (block != null) { + CheckExitBoundaries (bc, block); + } + + return true; + } + } + + public interface ILocalVariable + { + void Emit (EmitContext ec); + void EmitAssign (EmitContext ec); + void EmitAddressOf (EmitContext ec); + } + + public interface INamedBlockVariable + { + Block Block { get; } + Expression CreateReferenceExpression (ResolveContext rc, Location loc); + bool IsDeclared { get; } + bool IsParameter { get; } + Location Location { get; } + } + + public class BlockVariableDeclarator + { + LocalVariable li; + Expression initializer; + + public BlockVariableDeclarator (LocalVariable li, Expression initializer) + { + if (li.Type != null) + throw new ArgumentException ("Expected null variable type"); + + this.li = li; + this.initializer = initializer; + } + + #region Properties + + public LocalVariable Variable { + get { + return li; + } + } + + public Expression Initializer { + get { + return initializer; + } + set { + initializer = value; + } + } + + #endregion + + public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx) + { + var t = (BlockVariableDeclarator) MemberwiseClone (); + if (initializer != null) + t.initializer = initializer.Clone (cloneCtx); + + return t; + } + } + + public class BlockVariable : Statement + { + Expression initializer; + protected FullNamedExpression type_expr; + protected LocalVariable li; + protected List declarators; + TypeSpec type; + + public BlockVariable (FullNamedExpression type, LocalVariable li) + { + this.type_expr = type; + this.li = li; + this.loc = type_expr.Location; + } + + protected BlockVariable (LocalVariable li) + { + this.li = li; + } + + #region Properties + + public List Declarators { + get { + return declarators; + } + } + + public Expression Initializer { + get { + return initializer; + } + set { + initializer = value; + } + } + + public FullNamedExpression TypeExpression { + get { + return type_expr; + } + } + + public LocalVariable Variable { + get { + return li; + } + } + + #endregion + + public void AddDeclarator (BlockVariableDeclarator decl) + { + if (declarators == null) + declarators = new List (); + + declarators.Add (decl); + } + + static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li) + { + if (bc.Report.Errors != 0) + return; + + var container = bc.CurrentMemberDefinition.Parent.PartialContainer; + + Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC, + new MemberName (li.Name, li.Location), null); + + container.AddField (f); + f.Define (); + + li.HoistedVariant = new HoistedEvaluatorVariable (f); + li.SetIsUsed (); + } + + public override bool Resolve (BlockContext bc) + { + return Resolve (bc, true); + } + + public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers) + { + if (type == null && !li.IsCompilerGenerated) { + var vexpr = type_expr as VarExpr; + + // + // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with + // same name exists or as a keyword when no type was found + // + if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) { + if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3) + bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable"); + + if (li.IsFixed) { + bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable"); + return false; + } + + if (li.IsConstant) { + bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant"); + return false; + } + + if (Initializer == null) { + bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer"); + return false; + } + + if (declarators != null) { + bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators"); + declarators = null; + } + + Initializer = Initializer.Resolve (bc); + if (Initializer != null) { + ((VarExpr) type_expr).InferType (bc, Initializer); + type = type_expr.Type; + } else { + // Set error type to indicate the var was placed correctly but could + // not be infered + // + // var a = missing (); + // + type = InternalType.ErrorType; + } + } + + if (type == null) { + type = type_expr.ResolveAsType (bc); + if (type == null) + return false; + + if (li.IsConstant && !type.IsConstantCompatible) { + Const.Error_InvalidConstantType (type, loc, bc.Report); + } + } + + if (type.IsStatic) + FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report); + + li.Type = type; + } + + bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock; + if (eval_global) { + CreateEvaluatorVariable (bc, li); + } else if (type != InternalType.ErrorType) { + li.PrepareAssignmentAnalysis (bc); + } + + if (initializer != null) { + initializer = ResolveInitializer (bc, li, initializer); + // li.Variable.DefinitelyAssigned + } + + if (declarators != null) { + foreach (var d in declarators) { + d.Variable.Type = li.Type; + if (eval_global) { + CreateEvaluatorVariable (bc, d.Variable); + } else if (type != InternalType.ErrorType) { + d.Variable.PrepareAssignmentAnalysis (bc); + } + + if (d.Initializer != null && resolveDeclaratorInitializers) { + d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer); + // d.Variable.DefinitelyAssigned + } + } + } + + return true; + } + + protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer) + { + var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location); + return a.ResolveStatement (bc); + } + + protected override void DoEmit (EmitContext ec) + { + li.CreateBuilder (ec); + + if (Initializer != null && !IsUnreachable) + ((ExpressionStatement) Initializer).EmitStatement (ec); + + if (declarators != null) { + foreach (var d in declarators) { + d.Variable.CreateBuilder (ec); + if (d.Initializer != null && !IsUnreachable) { + ec.Mark (d.Variable.Location); + ((ExpressionStatement) d.Initializer).EmitStatement (ec); + } + } + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (Initializer != null) + Initializer.FlowAnalysis (fc); + + if (declarators != null) { + foreach (var d in declarators) { + if (d.Initializer != null) + d.Initializer.FlowAnalysis (fc); + } + } + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + var init = initializer as ExpressionStatement; + if (init != null) + init.MarkReachable (rc); + + return base.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + BlockVariable t = (BlockVariable) target; + + if (type_expr != null) + t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx); + + if (initializer != null) + t.initializer = initializer.Clone (clonectx); + + if (declarators != null) { + t.declarators = null; + foreach (var d in declarators) + t.AddDeclarator (d.Clone (clonectx)); + } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class BlockConstant : BlockVariable + { + public BlockConstant (FullNamedExpression type, LocalVariable li) + : base (type, li) + { + } + + public override void Emit (EmitContext ec) + { + // Nothing to emit, not even sequence point + } + + protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer) + { + initializer = initializer.Resolve (bc); + if (initializer == null) + return null; + + var c = initializer as Constant; + if (c == null) { + initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name); + return null; + } + + c = c.ConvertImplicitly (li.Type); + if (c == null) { + if (TypeSpec.IsReferenceType (li.Type)) + initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name); + else + initializer.Error_ValueCannotBeConverted (bc, li.Type, false); + + return null; + } + + li.ConstantValue = c; + return initializer; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // The information about a user-perceived local variable + // + public sealed class LocalVariable : INamedBlockVariable, ILocalVariable + { + [Flags] + public enum Flags + { + Used = 1, + IsThis = 1 << 1, + AddressTaken = 1 << 2, + CompilerGenerated = 1 << 3, + Constant = 1 << 4, + ForeachVariable = 1 << 5, + FixedVariable = 1 << 6, + UsingVariable = 1 << 7, + IsLocked = 1 << 8, + + ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable + } + + TypeSpec type; + readonly string name; + readonly Location loc; + readonly Block block; + Flags flags; + Constant const_value; + + public VariableInfo VariableInfo; + HoistedVariable hoisted_variant; + + LocalBuilder builder; + + public LocalVariable (Block block, string name, Location loc) + { + this.block = block; + this.name = name; + this.loc = loc; + } + + public LocalVariable (Block block, string name, Flags flags, Location loc) + : this (block, name, loc) + { + this.flags = flags; + } + + // + // Used by variable declarators + // + public LocalVariable (LocalVariable li, string name, Location loc) + : this (li.block, name, li.flags, loc) + { + } + + #region Properties + + public bool AddressTaken { + get { + return (flags & Flags.AddressTaken) != 0; + } + } + + public Block Block { + get { + return block; + } + } + + public Constant ConstantValue { + get { + return const_value; + } + set { + const_value = value; + } + } + + // + // Hoisted local variable variant + // + public HoistedVariable HoistedVariant { + get { + return hoisted_variant; + } + set { + hoisted_variant = value; + } + } + + public bool IsDeclared { + get { + return type != null; + } + } + + public bool IsCompilerGenerated { + get { + return (flags & Flags.CompilerGenerated) != 0; + } + } + + public bool IsConstant { + get { + return (flags & Flags.Constant) != 0; + } + } + + public bool IsLocked { + get { + return (flags & Flags.IsLocked) != 0; + } + set { + flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked; + } + } + + public bool IsThis { + get { + return (flags & Flags.IsThis) != 0; + } + } + + public bool IsFixed { + get { + return (flags & Flags.FixedVariable) != 0; + } + } + + bool INamedBlockVariable.IsParameter { + get { + return false; + } + } + + public bool IsReadonly { + get { + return (flags & Flags.ReadonlyMask) != 0; + } + } + + public Location Location { + get { + return loc; + } + } + + public string Name { + get { + return name; + } + } + + public TypeSpec Type { + get { + return type; + } + set { + type = value; + } + } + + #endregion + + public void CreateBuilder (EmitContext ec) + { + if ((flags & Flags.Used) == 0) { + if (VariableInfo == null) { + // Missing flow analysis or wrong variable flags + throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name); + } + + if (VariableInfo.IsEverAssigned) + ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name); + else + ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name); + } + + if (HoistedVariant != null) + return; + + if (builder != null) { + if ((flags & Flags.CompilerGenerated) != 0) + return; + + // To avoid Used warning duplicates + throw new InternalErrorException ("Already created variable `{0}'", name); + } + + // + // All fixed variabled are pinned, a slot has to be alocated + // + builder = ec.DeclareLocal (Type, IsFixed); + if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0) + ec.DefineLocalVariable (name, builder); + } + + public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc) + { + LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc); + li.Type = type; + return li; + } + + public Expression CreateReferenceExpression (ResolveContext rc, Location loc) + { + if (IsConstant && const_value != null) + return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc); + + return new LocalVariableReference (this, loc); + } + + public void Emit (EmitContext ec) + { + // TODO: Need something better for temporary variables + if ((flags & Flags.CompilerGenerated) != 0) + CreateBuilder (ec); + + ec.Emit (OpCodes.Ldloc, builder); + } + + public void EmitAssign (EmitContext ec) + { + // TODO: Need something better for temporary variables + if ((flags & Flags.CompilerGenerated) != 0) + CreateBuilder (ec); + + ec.Emit (OpCodes.Stloc, builder); + } + + public void EmitAddressOf (EmitContext ec) + { + // TODO: Need something better for temporary variables + if ((flags & Flags.CompilerGenerated) != 0) + CreateBuilder (ec); + + ec.Emit (OpCodes.Ldloca, builder); + } + + public static string GetCompilerGeneratedName (Block block) + { + // HACK: Debugger depends on the name semantics + return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X"); + } + + public string GetReadOnlyContext () + { + switch (flags & Flags.ReadonlyMask) { + case Flags.FixedVariable: + return "fixed variable"; + case Flags.ForeachVariable: + return "foreach iteration variable"; + case Flags.UsingVariable: + return "using variable"; + } + + throw new InternalErrorException ("Variable is not readonly"); + } + + public bool IsThisAssigned (FlowAnalysisContext fc, Block block) + { + if (VariableInfo == null) + throw new Exception (); + + if (IsAssigned (fc)) + return true; + + return VariableInfo.IsFullyInitialized (fc, block.StartLocation); + } + + public bool IsAssigned (FlowAnalysisContext fc) + { + return fc.IsDefinitelyAssigned (VariableInfo); + } + + public void PrepareAssignmentAnalysis (BlockContext bc) + { + // + // No need to run assignment analysis for these guys + // + if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0) + return; + + VariableInfo = VariableInfo.Create (bc, this); + } + + // + // Mark the variables as referenced in the user code + // + public void SetIsUsed () + { + flags |= Flags.Used; + } + + public void SetHasAddressTaken () + { + flags |= (Flags.AddressTaken | Flags.Used); + } + + public override string ToString () + { + return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location); + } + } + + /// + /// Block represents a C# block. + /// + /// + /// + /// This class is used in a number of places: either to represent + /// explicit blocks that the programmer places or implicit blocks. + /// + /// Implicit blocks are used as labels or to introduce variable + /// declarations. + /// + /// Top-level blocks derive from Block, and they are called ToplevelBlock + /// they contain extra information that is not necessary on normal blocks. + /// + public class Block : Statement { + [Flags] + public enum Flags + { + Unchecked = 1, + ReachableEnd = 8, + Unsafe = 16, + HasCapturedVariable = 64, + HasCapturedThis = 1 << 7, + IsExpressionTree = 1 << 8, + CompilerGenerated = 1 << 9, + HasAsyncModifier = 1 << 10, + Resolved = 1 << 11, + YieldBlock = 1 << 12, + AwaitBlock = 1 << 13, + FinallyBlock = 1 << 14, + CatchBlock = 1 << 15, + Iterator = 1 << 20, + NoFlowAnalysis = 1 << 21, + InitializationEmitted = 1 << 22 + } + + public Block Parent; + public Location StartLocation; + public Location EndLocation; + + public ExplicitBlock Explicit; + public ParametersBlock ParametersBlock; + + protected Flags flags; + + // + // The statements in this block + // + protected List statements; + + protected List scope_initializers; + + int? resolving_init_idx; + + Block original; + +#if DEBUG + static int id; + public int ID = id++; + + static int clone_id_counter; + int clone_id; +#endif + +// int assignable_slots; + + public Block (Block parent, Location start, Location end) + : this (parent, 0, start, end) + { + } + + public Block (Block parent, Flags flags, Location start, Location end) + { + if (parent != null) { + // the appropriate constructors will fixup these fields + ParametersBlock = parent.ParametersBlock; + Explicit = parent.Explicit; + } + + this.Parent = parent; + this.flags = flags; + this.StartLocation = start; + this.EndLocation = end; + this.loc = start; + statements = new List (4); + + this.original = this; + } + + #region Properties + + public Block Original { + get { + return original; + } + protected set { + original = value; + } + } + + public bool IsCompilerGenerated { + get { return (flags & Flags.CompilerGenerated) != 0; } + set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; } + } + + + public bool IsCatchBlock { + get { + return (flags & Flags.CatchBlock) != 0; + } + } + + public bool IsFinallyBlock { + get { + return (flags & Flags.FinallyBlock) != 0; + } + } + + public bool Unchecked { + get { return (flags & Flags.Unchecked) != 0; } + set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; } + } + + public bool Unsafe { + get { return (flags & Flags.Unsafe) != 0; } + set { flags |= Flags.Unsafe; } + } + + public List Statements { + get { return statements; } + } + + #endregion + + public void SetEndLocation (Location loc) + { + EndLocation = loc; + } + + public void AddLabel (LabeledStatement target) + { + ParametersBlock.TopBlock.AddLabel (target.Name, target); + } + + public void AddLocalName (LocalVariable li) + { + AddLocalName (li.Name, li); + } + + public void AddLocalName (string name, INamedBlockVariable li) + { + ParametersBlock.TopBlock.AddLocalName (name, li, false); + } + + public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason) + { + if (reason == null) { + Error_AlreadyDeclared (name, variable); + return; + } + + ParametersBlock.TopBlock.Report.Error (136, variable.Location, + "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " + + "to `{0}', which is already used in a `{1}' scope to denote something else", + name, reason); + } + + public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable) + { + var pi = variable as ParametersBlock.ParameterInfo; + if (pi != null) { + pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report); + } else { + ParametersBlock.TopBlock.Report.Error (128, variable.Location, + "A local variable named `{0}' is already defined in this scope", name); + } + } + + public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc) + { + ParametersBlock.TopBlock.Report.Error (412, loc, + "The type parameter name `{0}' is the same as local variable or parameter name", + name); + } + + // + // It should be used by expressions which require to + // register a statement during resolve process. + // + public void AddScopeStatement (Statement s) + { + if (scope_initializers == null) + scope_initializers = new List (); + + // + // Simple recursive helper, when resolve scope initializer another + // new scope initializer can be added, this ensures it's initialized + // before existing one. For now this can happen with expression trees + // in base ctor initializer only + // + if (resolving_init_idx.HasValue) { + scope_initializers.Insert (resolving_init_idx.Value, s); + ++resolving_init_idx; + } else { + scope_initializers.Add (s); + } + } + + public void InsertStatement (int index, Statement s) + { + statements.Insert (index, s); + } + + public void AddStatement (Statement s) + { + statements.Add (s); + } + + public LabeledStatement LookupLabel (string name) + { + return ParametersBlock.GetLabel (name, this); + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + MarkReachableScope (rc); + + foreach (var s in statements) { + rc = s.MarkReachable (rc); + if (rc.IsUnreachable) { + if ((flags & Flags.ReachableEnd) != 0) + return new Reachability (); + + return rc; + } + } + + flags |= Flags.ReachableEnd; + + return rc; + } + + public void MarkReachableScope (Reachability rc) + { + base.MarkReachable (rc); + + if (scope_initializers != null) { + foreach (var si in scope_initializers) + si.MarkReachable (rc); + } + } + + public override bool Resolve (BlockContext bc) + { + if ((flags & Flags.Resolved) != 0) + return true; + + Block prev_block = bc.CurrentBlock; + bc.CurrentBlock = this; + + // + // Compiler generated scope statements + // + if (scope_initializers != null) { + for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) { + scope_initializers[resolving_init_idx.Value].Resolve (bc); + } + + resolving_init_idx = null; + } + + bool ok = true; + int statement_count = statements.Count; + for (int ix = 0; ix < statement_count; ix++){ + Statement s = statements [ix]; + + if (!s.Resolve (bc)) { + ok = false; + if (!bc.IsInProbingMode) + statements [ix] = new EmptyStatement (s.loc); + + continue; + } + } + + bc.CurrentBlock = prev_block; + + flags |= Flags.Resolved; + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + for (int ix = 0; ix < statements.Count; ix++){ + statements [ix].Emit (ec); + } + } + + public override void Emit (EmitContext ec) + { + if (scope_initializers != null) + EmitScopeInitializers (ec); + + DoEmit (ec); + } + + protected void EmitScopeInitializers (EmitContext ec) + { + foreach (Statement s in scope_initializers) + s.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (scope_initializers != null) { + foreach (var si in scope_initializers) + si.FlowAnalysis (fc); + } + + return DoFlowAnalysis (fc, 0); + } + + bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex) + { + bool end_unreachable = !reachable; + for (; startIndex < statements.Count; ++startIndex) { + var s = statements[startIndex]; + + end_unreachable = s.FlowAnalysis (fc); + if (s.IsUnreachable) { + statements [startIndex] = RewriteUnreachableStatement (s); + continue; + } + + // + // Statement end reachability is needed mostly due to goto support. Consider + // + // if (cond) { + // goto X; + // } else { + // goto Y; + // } + // X: + // + // X label is reachable only via goto not as another statement after if. We need + // this for flow-analysis only to carry variable info correctly. + // + if (end_unreachable) { + for (++startIndex; startIndex < statements.Count; ++startIndex) { + s = statements[startIndex]; + if (s is SwitchLabel) { + s.FlowAnalysis (fc); + break; + } + + if (s.IsUnreachable) { + s.FlowAnalysis (fc); + statements [startIndex] = RewriteUnreachableStatement (s); + } + } + } + } + + // + // The condition should be true unless there is forward jumping goto + // + // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace) + // Debug.Fail (); + + return !Explicit.HasReachableClosingBrace; + } + + static Statement RewriteUnreachableStatement (Statement s) + { + // LAMESPEC: It's not clear whether declararion statement should be part of reachability + // analysis. Even csc report unreachable warning for it but it's actually used hence + // we try to emulate this behaviour + // + // Consider: + // goto L; + // int v; + // L: + // v = 1; + + if (s is BlockVariable) + return s; + + return new EmptyStatement (s.loc); + } + + public void ScanGotoJump (Statement label) + { + int i; + for (i = 0; i < statements.Count; ++i) { + if (statements[i] == label) + break; + } + + var rc = new Reachability (); + for (++i; i < statements.Count; ++i) { + var s = statements[i]; + rc = s.MarkReachable (rc); + if (rc.IsUnreachable) + return; + } + + flags |= Flags.ReachableEnd; + } + + public void ScanGotoJump (Statement label, FlowAnalysisContext fc) + { + int i; + for (i = 0; i < statements.Count; ++i) { + if (statements[i] == label) + break; + } + + DoFlowAnalysis (fc, ++i); + } + +#if DEBUG + public override string ToString () + { + return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation); + } +#endif + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Block target = (Block) t; +#if DEBUG + target.clone_id = ++clone_id_counter; +#endif + + clonectx.AddBlockMap (this, target); + if (original != this) + clonectx.AddBlockMap (original, target); + + target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock)); + target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit)); + + if (Parent != null) + target.Parent = clonectx.RemapBlockCopy (Parent); + + target.statements = new List (statements.Count); + foreach (Statement s in statements) + target.statements.Add (s.Clone (clonectx)); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ExplicitBlock : Block + { + protected AnonymousMethodStorey am_storey; + + public ExplicitBlock (Block parent, Location start, Location end) + : this (parent, (Flags) 0, start, end) + { + } + + public ExplicitBlock (Block parent, Flags flags, Location start, Location end) + : base (parent, flags, start, end) + { + this.Explicit = this; + } + + #region Properties + + public AnonymousMethodStorey AnonymousMethodStorey { + get { + return am_storey; + } + } + + public bool HasAwait { + get { + return (flags & Flags.AwaitBlock) != 0; + } + } + + public bool HasCapturedThis { + set { + flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; + } + get { + return (flags & Flags.HasCapturedThis) != 0; + } + } + + // + // Used to indicate that the block has reference to parent + // block and cannot be made static when defining anonymous method + // + public bool HasCapturedVariable { + set { + flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; + } + get { + return (flags & Flags.HasCapturedVariable) != 0; + } + } + + public bool HasReachableClosingBrace { + get { + return (flags & Flags.ReachableEnd) != 0; + } + set { + flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd; + } + } + + public bool HasYield { + get { + return (flags & Flags.YieldBlock) != 0; + } + } + + #endregion + + // + // Creates anonymous method storey in current block + // + public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec) + { + // + // Return same story for iterator and async blocks unless we are + // in nested anonymous method + // + if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original) + return ec.CurrentAnonymousMethod.Storey; + + if (am_storey == null) { + MemberBase mc = ec.MemberContext as MemberBase; + + // + // Creates anonymous method storey for this block + // + am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class); + } + + return am_storey; + } + + public void EmitScopeInitialization (EmitContext ec) + { + if ((flags & Flags.InitializationEmitted) != 0) + return; + + if (am_storey != null) { + DefineStoreyContainer (ec, am_storey); + am_storey.EmitStoreyInstantiation (ec, this); + } + + if (scope_initializers != null) + EmitScopeInitializers (ec); + + flags |= Flags.InitializationEmitted; + } + + public override void Emit (EmitContext ec) + { + EmitScopeInitialization (ec); + + if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) { + ec.Emit (OpCodes.Nop); + } + + if (Parent != null) + ec.BeginScope (); + + DoEmit (ec); + + if (Parent != null) + ec.EndScope (); + + if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) && + !IsCompilerGenerated && ec.Mark (EndLocation)) { + ec.Emit (OpCodes.Nop); + } + } + + protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey) + { + if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) { + storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey); + storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator; + } + + // + // Creates anonymous method storey + // + storey.CreateContainer (); + storey.DefineContainer (); + + if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) { + + // + // Only first storey in path will hold this reference. All children blocks will + // reference it indirectly using $ref field + // + for (Block b = Original.Explicit; b != null; b = b.Parent) { + if (b.Parent != null) { + var s = b.Parent.Explicit.AnonymousMethodStorey; + if (s != null) { + storey.HoistedThis = s.HoistedThis; + break; + } + } + + if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) { + if (storey.HoistedThis == null) + storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis; + + if (storey.HoistedThis != null) + break; + } + } + + // + // We are the first storey on path and 'this' has to be hoisted + // + if (storey.HoistedThis == null) { + foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) { + // + // ThisReferencesFromChildrenBlock holds all reference even if they + // are not on this path. It saves some memory otherwise it'd have to + // be in every explicit block. We run this check to see if the reference + // is valid for this storey + // + Block block_on_path = ref_block; + for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent); + + if (block_on_path == null) + continue; + + if (storey.HoistedThis == null) { + storey.AddCapturedThisField (ec, null); + } + + for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) { + ParametersBlock pb; + AnonymousMethodStorey b_storey = b.AnonymousMethodStorey; + + if (b_storey != null) { + // + // Don't add storey cross reference for `this' when the storey ends up not + // beeing attached to any parent + // + if (b.ParametersBlock.StateMachine == null) { + AnonymousMethodStorey s = null; + for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) { + s = ab.Explicit.AnonymousMethodStorey; + if (s != null) + break; + } + + // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost + if (s == null) { + var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey; + b.AnonymousMethodStorey.AddCapturedThisField (ec, parent); + break; + } + + } + + // + // Stop propagation inside same top block + // + if (b.ParametersBlock == ParametersBlock.Original) { + b_storey.AddParentStoreyReference (ec, storey); +// b_storey.HoistedThis = storey.HoistedThis; + break; + } + + b = pb = b.ParametersBlock; + } else { + pb = b as ParametersBlock; + } + + if (pb != null && pb.StateMachine != null) { + if (pb.StateMachine == storey) + break; + + // + // If we are state machine with no parent. We can hook into parent without additional + // reference and capture this directly + // + ExplicitBlock parent_storey_block = pb; + while (parent_storey_block.Parent != null) { + parent_storey_block = parent_storey_block.Parent.Explicit; + if (parent_storey_block.AnonymousMethodStorey != null) { + break; + } + } + + if (parent_storey_block.AnonymousMethodStorey == null) { + pb.StateMachine.AddCapturedThisField (ec, null); + b.HasCapturedThis = true; + continue; + } + + pb.StateMachine.AddParentStoreyReference (ec, storey); + } + + // + // Add parent storey reference only when this is not captured directly + // + if (b_storey != null) { + b_storey.AddParentStoreyReference (ec, storey); + b_storey.HoistedThis = storey.HoistedThis; + } + } + } + } + } + + var ref_blocks = storey.ReferencesFromChildrenBlock; + if (ref_blocks != null) { + foreach (ExplicitBlock ref_block in ref_blocks) { + for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) { + if (b.AnonymousMethodStorey != null) { + b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey); + + // + // Stop propagation inside same top block + // + if (b.ParametersBlock == ParametersBlock.Original) + break; + + b = b.ParametersBlock; + } + + var pb = b as ParametersBlock; + if (pb != null && pb.StateMachine != null) { + if (pb.StateMachine == storey) + break; + + pb.StateMachine.AddParentStoreyReference (ec, storey); + } + + b.HasCapturedVariable = true; + } + } + } + + storey.Define (); + storey.PrepareEmit (); + storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey); + } + + public void RegisterAsyncAwait () + { + var block = this; + while ((block.flags & Flags.AwaitBlock) == 0) { + block.flags |= Flags.AwaitBlock; + + if (block is ParametersBlock) + return; + + block = block.Parent.Explicit; + } + } + + public void RegisterIteratorYield () + { + ParametersBlock.TopBlock.IsIterator = true; + + var block = this; + while ((block.flags & Flags.YieldBlock) == 0) { + block.flags |= Flags.YieldBlock; + + if (block.Parent == null) + return; + + block = block.Parent.Explicit; + } + } + + public void SetCatchBlock () + { + flags |= Flags.CatchBlock; + } + + public void SetFinallyBlock () + { + flags |= Flags.FinallyBlock; + } + + public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock) + { + tryBlock.statements = statements; + statements = new List (1); + statements.Add (tf); + } + } + + // + // ParametersBlock was introduced to support anonymous methods + // and lambda expressions + // + public class ParametersBlock : ExplicitBlock + { + public class ParameterInfo : INamedBlockVariable + { + readonly ParametersBlock block; + readonly int index; + public VariableInfo VariableInfo; + bool is_locked; + + public ParameterInfo (ParametersBlock block, int index) + { + this.block = block; + this.index = index; + } + + #region Properties + + public ParametersBlock Block { + get { + return block; + } + } + + Block INamedBlockVariable.Block { + get { + return block; + } + } + + public bool IsDeclared { + get { + return true; + } + } + + public bool IsParameter { + get { + return true; + } + } + + public bool IsLocked { + get { + return is_locked; + } + set { + is_locked = value; + } + } + + public Location Location { + get { + return Parameter.Location; + } + } + + public Parameter Parameter { + get { + return block.Parameters [index]; + } + } + + public TypeSpec ParameterType { + get { + return Parameter.Type; + } + } + + #endregion + + public Expression CreateReferenceExpression (ResolveContext rc, Location loc) + { + return new ParameterReference (this, loc); + } + } + + // + // Block is converted into an expression + // + sealed class BlockScopeExpression : Expression + { + Expression child; + readonly ParametersBlock block; + + public BlockScopeExpression (Expression child, ParametersBlock block) + { + this.child = child; + this.block = block; + } + + public override bool ContainsEmitWithAwait () + { + return child.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (child == null) + return null; + + child = child.Resolve (ec); + if (child == null) + return null; + + eclass = child.eclass; + type = child.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + block.EmitScopeInitializers (ec); + child.Emit (ec); + } + } + + protected ParametersCompiled parameters; + protected ParameterInfo[] parameter_info; + protected bool resolved; + protected ToplevelBlock top_block; + protected StateMachine state_machine; + protected Dictionary labels; + + public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0) + : base (parent, 0, start, start) + { + if (parameters == null) + throw new ArgumentNullException ("parameters"); + + this.parameters = parameters; + ParametersBlock = this; + + this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock)); + + this.top_block = parent.ParametersBlock.top_block; + ProcessParameters (); + } + + protected ParametersBlock (ParametersCompiled parameters, Location start) + : base (null, 0, start, start) + { + if (parameters == null) + throw new ArgumentNullException ("parameters"); + + this.parameters = parameters; + ParametersBlock = this; + } + + // + // It's supposed to be used by method body implementation of anonymous methods + // + protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters) + : base (null, 0, source.StartLocation, source.EndLocation) + { + this.parameters = parameters; + this.statements = source.statements; + this.scope_initializers = source.scope_initializers; + + this.resolved = true; + this.reachable = source.reachable; + this.am_storey = source.am_storey; + this.state_machine = source.state_machine; + this.flags = source.flags & Flags.ReachableEnd; + + ParametersBlock = this; + + // + // Overwrite original for comparison purposes when linking cross references + // between anonymous methods + // + Original = source.Original; + } + + #region Properties + + public bool IsAsync { + get { + return (flags & Flags.HasAsyncModifier) != 0; + } + set { + flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier; + } + } + + // + // Block has been converted to expression tree + // + public bool IsExpressionTree { + get { + return (flags & Flags.IsExpressionTree) != 0; + } + } + + // + // The parameters for the block. + // + public ParametersCompiled Parameters { + get { + return parameters; + } + } + + public StateMachine StateMachine { + get { + return state_machine; + } + } + + public ToplevelBlock TopBlock { + get { + return top_block; + } + set { + top_block = value; + } + } + + public bool Resolved { + get { + return (flags & Flags.Resolved) != 0; + } + } + + public int TemporaryLocalsCount { get; set; } + + #endregion + + // + // Checks whether all `out' parameters have been assigned. + // + public void CheckControlExit (FlowAnalysisContext fc) + { + CheckControlExit (fc, fc.DefiniteAssignment); + } + + public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat) + { + if (parameter_info == null) + return; + + foreach (var p in parameter_info) { + if (p.VariableInfo == null) + continue; + + if (p.VariableInfo.IsAssigned (dat)) + continue; + + fc.Report.Error (177, p.Location, + "The out parameter `{0}' must be assigned to before control leaves the current method", + p.Parameter.Name); + } + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + base.CloneTo (clonectx, t); + + var target = (ParametersBlock) t; + + // + // Clone label statements as well as they contain block reference + // + var pb = this; + while (true) { + if (pb.labels != null) { + target.labels = new Dictionary (); + + foreach (var entry in pb.labels) { + var list = entry.Value as List; + + if (list != null) { + var list_clone = new List (); + foreach (var lentry in list) { + list_clone.Add (RemapLabeledStatement (lentry, lentry.Block, clonectx.RemapBlockCopy (lentry.Block))); + } + + target.labels.Add (entry.Key, list_clone); + } else { + var labeled = (LabeledStatement) entry.Value; + target.labels.Add (entry.Key, RemapLabeledStatement (labeled, labeled.Block, clonectx.RemapBlockCopy (labeled.Block))); + } + } + + break; + } + + if (pb.Parent == null) + break; + + pb = pb.Parent.ParametersBlock; + } + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (statements.Count == 1) { + Expression expr = statements[0].CreateExpressionTree (ec); + if (scope_initializers != null) + expr = new BlockScopeExpression (expr, this); + + return expr; + } + + return base.CreateExpressionTree (ec); + } + + public override void Emit (EmitContext ec) + { + if (state_machine != null && state_machine.OriginalSourceBlock != this) { + DefineStoreyContainer (ec, state_machine); + state_machine.EmitStoreyInstantiation (ec, this); + } + + base.Emit (ec); + } + + public void EmitEmbedded (EmitContext ec) + { + if (state_machine != null && state_machine.OriginalSourceBlock != this) { + DefineStoreyContainer (ec, state_machine); + state_machine.EmitStoreyInstantiation (ec, this); + } + + base.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + var res = base.DoFlowAnalysis (fc); + + if (HasReachableClosingBrace) + CheckControlExit (fc); + + return res; + } + + public LabeledStatement GetLabel (string name, Block block) + { + // + // Cloned parameters blocks can have their own cloned version of top-level labels + // + if (labels == null) { + if (Parent != null) + return Parent.ParametersBlock.GetLabel (name, block); + + return null; + } + + object value; + if (!labels.TryGetValue (name, out value)) { + return null; + } + + var label = value as LabeledStatement; + Block b = block; + if (label != null) { + if (IsLabelVisible (label, b)) + return label; + + } else { + List list = (List) value; + for (int i = 0; i < list.Count; ++i) { + label = list[i]; + if (IsLabelVisible (label, b)) + return label; + } + } + + return null; + } + + static bool IsLabelVisible (LabeledStatement label, Block b) + { + do { + if (label.Block == b) + return true; + b = b.Parent; + } while (b != null); + + return false; + } + + public ParameterInfo GetParameterInfo (Parameter p) + { + for (int i = 0; i < parameters.Count; ++i) { + if (parameters[i] == p) + return parameter_info[i]; + } + + throw new ArgumentException ("Invalid parameter"); + } + + public ParameterReference GetParameterReference (int index, Location loc) + { + return new ParameterReference (parameter_info[index], loc); + } + + public Statement PerformClone () + { + CloneContext clonectx = new CloneContext (); + return Clone (clonectx); + } + + protected void ProcessParameters () + { + if (parameters.Count == 0) + return; + + parameter_info = new ParameterInfo[parameters.Count]; + for (int i = 0; i < parameter_info.Length; ++i) { + var p = parameters.FixedParameters[i]; + if (p == null) + continue; + + // TODO: Should use Parameter only and more block there + parameter_info[i] = new ParameterInfo (this, i); + if (p.Name != null) + AddLocalName (p.Name, parameter_info[i]); + } + } + + static LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block src, Block dst) + { + var src_stmts = src.Statements; + for (int i = 0; i < src_stmts.Count; ++i) { + if (src_stmts[i] == stmt) + return (LabeledStatement) dst.Statements[i]; + } + + throw new InternalErrorException ("Should never be reached"); + } + + public override bool Resolve (BlockContext bc) + { + // TODO: if ((flags & Flags.Resolved) != 0) + + if (resolved) + return true; + + resolved = true; + + if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) + flags |= Flags.IsExpressionTree; + + try { + PrepareAssignmentAnalysis (bc); + + if (!base.Resolve (bc)) + return false; + + } catch (Exception e) { + if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError) + throw; + + if (bc.CurrentBlock != null) { + bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message); + } else { + bc.Report.Error (587, "Internal compiler error: {0}", e.Message); + } + } + + // + // If an asynchronous body of F is either an expression classified as nothing, or a + // statement block where no return statements have expressions, the inferred return type is Task + // + if (IsAsync) { + var am = bc.CurrentAnonymousMethod as AnonymousMethodBody; + if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) { + am.ReturnTypeInference = null; + am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec; + return true; + } + } + + return true; + } + + void PrepareAssignmentAnalysis (BlockContext bc) + { + for (int i = 0; i < parameters.Count; ++i) { + var par = parameters.FixedParameters[i]; + + if ((par.ModFlags & Parameter.Modifier.OUT) == 0) + continue; + + parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par); + } + } + + public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable) + { + var iterator = new Iterator (this, method, host, iterator_type, is_enumerable); + var stateMachine = new IteratorStorey (iterator); + + state_machine = stateMachine; + iterator.SetStateMachine (stateMachine); + + var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated); + tlb.Original = this; + tlb.state_machine = stateMachine; + tlb.AddStatement (new Return (iterator, iterator.Location)); + return tlb; + } + + public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc) + { + for (int i = 0; i < parameters.Count; i++) { + Parameter p = parameters[i]; + Parameter.Modifier mod = p.ModFlags; + if ((mod & Parameter.Modifier.RefOutMask) != 0) { + host.Compiler.Report.Error (1988, p.Location, + "Async methods cannot have ref or out parameters"); + return this; + } + + if (p is ArglistParameter) { + host.Compiler.Report.Error (4006, p.Location, + "__arglist is not allowed in parameter list of async methods"); + return this; + } + + if (parameters.Types[i].IsPointer) { + host.Compiler.Report.Error (4005, p.Location, + "Async methods cannot have unsafe parameters"); + return this; + } + } + + if (!HasAwait) { + host.Compiler.Report.Warning (1998, 1, loc, + "Async block lacks `await' operator and will run synchronously"); + } + + var block_type = host.Module.Compiler.BuiltinTypes.Void; + var initializer = new AsyncInitializer (this, host, block_type); + initializer.Type = block_type; + initializer.DelegateType = delegateType; + + var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType); + + state_machine = stateMachine; + initializer.SetStateMachine (stateMachine); + + const Flags flags = Flags.CompilerGenerated; + + var b = this is ToplevelBlock ? + new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) : + new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier); + + b.Original = this; + b.state_machine = stateMachine; + b.AddStatement (new AsyncInitializerStatement (initializer)); + return b; + } + } + + // + // + // + public class ToplevelBlock : ParametersBlock + { + LocalVariable this_variable; + CompilerContext compiler; + Dictionary names; + + List this_references; + + public ToplevelBlock (CompilerContext ctx, Location loc) + : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc) + { + } + + public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0) + : base (parameters, start) + { + this.compiler = ctx; + this.flags = flags; + top_block = this; + + ProcessParameters (); + } + + // + // Recreates a top level block from parameters block. Used for + // compiler generated methods where the original block comes from + // explicit child block. This works for already resolved blocks + // only to ensure we resolve them in the correct flow order + // + public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters) + : base (source, parameters) + { + this.compiler = source.TopBlock.compiler; + top_block = this; + } + + public bool IsIterator { + get { + return (flags & Flags.Iterator) != 0; + } + set { + flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator; + } + } + + public Report Report { + get { + return compiler.Report; + } + } + + // + // Used by anonymous blocks to track references of `this' variable + // + public List ThisReferencesFromChildrenBlock { + get { + return this_references; + } + } + + // + // Returns the "this" instance variable of this block. + // See AddThisVariable() for more information. + // + public LocalVariable ThisVariable { + get { + return this_variable; + } + } + + public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks) + { + if (names == null) + names = new Dictionary (); + + object value; + if (!names.TryGetValue (name, out value)) { + names.Add (name, li); + return; + } + + INamedBlockVariable existing = value as INamedBlockVariable; + List existing_list; + if (existing != null) { + existing_list = new List (); + existing_list.Add (existing); + names[name] = existing_list; + } else { + existing_list = (List) value; + } + + // + // A collision checking between local names + // + var variable_block = li.Block.Explicit; + for (int i = 0; i < existing_list.Count; ++i) { + existing = existing_list[i]; + Block b = existing.Block.Explicit; + + // Collision at same level + if (variable_block == b) { + li.Block.Error_AlreadyDeclared (name, li); + break; + } + + // Collision with parent + Block parent = variable_block; + while ((parent = parent.Parent) != null) { + if (parent == b) { + li.Block.Error_AlreadyDeclared (name, li, "parent or current"); + i = existing_list.Count; + break; + } + } + + if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) { + // Collision with children + while ((b = b.Parent) != null) { + if (variable_block == b) { + li.Block.Error_AlreadyDeclared (name, li, "child"); + i = existing_list.Count; + break; + } + } + } + } + + existing_list.Add (li); + } + + public void AddLabel (string name, LabeledStatement label) + { + if (labels == null) + labels = new Dictionary (); + + object value; + if (!labels.TryGetValue (name, out value)) { + labels.Add (name, label); + return; + } + + LabeledStatement existing = value as LabeledStatement; + List existing_list; + if (existing != null) { + existing_list = new List (); + existing_list.Add (existing); + labels[name] = existing_list; + } else { + existing_list = (List) value; + } + + // + // A collision checking between labels + // + for (int i = 0; i < existing_list.Count; ++i) { + existing = existing_list[i]; + Block b = existing.Block; + + // Collision at same level + if (label.Block == b) { + Report.SymbolRelatedToPreviousError (existing.loc, name); + Report.Error (140, label.loc, "The label `{0}' is a duplicate", name); + break; + } + + // Collision with parent + b = label.Block; + while ((b = b.Parent) != null) { + if (existing.Block == b) { + Report.Error (158, label.loc, + "The label `{0}' shadows another label by the same name in a contained scope", name); + i = existing_list.Count; + break; + } + } + + // Collision with with children + b = existing.Block; + while ((b = b.Parent) != null) { + if (label.Block == b) { + Report.Error (158, label.loc, + "The label `{0}' shadows another label by the same name in a contained scope", name); + i = existing_list.Count; + break; + } + } + } + + existing_list.Add (label); + } + + public void AddThisReferenceFromChildrenBlock (ExplicitBlock block) + { + if (this_references == null) + this_references = new List (); + + if (!this_references.Contains (block)) + this_references.Add (block); + } + + public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block) + { + this_references.Remove (block); + } + + // + // Creates an arguments set from all parameters, useful for method proxy calls + // + public Arguments GetAllParametersArguments () + { + int count = parameters.Count; + Arguments args = new Arguments (count); + for (int i = 0; i < count; ++i) { + var pi = parameter_info[i]; + var arg_expr = GetParameterReference (i, pi.Location); + + Argument.AType atype_modifier; + switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) { + case Parameter.Modifier.REF: + atype_modifier = Argument.AType.Ref; + break; + case Parameter.Modifier.OUT: + atype_modifier = Argument.AType.Out; + break; + default: + atype_modifier = 0; + break; + } + + args.Add (new Argument (arg_expr, atype_modifier)); + } + + return args; + } + + // + // Lookup inside a block, the returned value can represent 3 states + // + // true+variable: A local name was found and it's valid + // false+variable: A local name was found in a child block only + // false+null: No local name was found + // + public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable) + { + if (names == null) + return false; + + object value; + if (!names.TryGetValue (name, out value)) + return false; + + variable = value as INamedBlockVariable; + Block b = block; + if (variable != null) { + do { + if (variable.Block == b.Original) + return true; + + b = b.Parent; + } while (b != null); + + b = variable.Block; + do { + if (block == b) + return false; + + b = b.Parent; + } while (b != null); + } else { + List list = (List) value; + for (int i = 0; i < list.Count; ++i) { + variable = list[i]; + do { + if (variable.Block == b.Original) + return true; + + b = b.Parent; + } while (b != null); + + b = variable.Block; + do { + if (block == b) + return false; + + b = b.Parent; + } while (b != null); + + b = block; + } + } + + variable = null; + return false; + } + + public void IncludeBlock (ParametersBlock pb, ToplevelBlock block) + { + if (block.names != null) { + foreach (var n in block.names) { + var variable = n.Value as INamedBlockVariable; + if (variable != null) { + if (variable.Block.ParametersBlock == pb) + AddLocalName (n.Key, variable, false); + continue; + } + + foreach (var v in (List) n.Value) + if (v.Block.ParametersBlock == pb) + AddLocalName (n.Key, v, false); + } + } + } + + // + // This is used by non-static `struct' constructors which do not have an + // initializer - in this case, the constructor must initialize all of the + // struct's fields. To do this, we add a "this" variable and use the flow + // analysis code to ensure that it's been fully initialized before control + // leaves the constructor. + // + public void AddThisVariable (BlockContext bc) + { + if (this_variable != null) + throw new InternalErrorException (StartLocation.ToString ()); + + this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation); + this_variable.Type = bc.CurrentType; + this_variable.PrepareAssignmentAnalysis (bc); + } + + public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat) + { + // + // If we're a non-static struct constructor which doesn't have an + // initializer, then we must initialize all of the struct's fields. + // + if (this_variable != null) + this_variable.IsThisAssigned (fc, this); + + base.CheckControlExit (fc, dat); + } + + public override void Emit (EmitContext ec) + { + if (Report.Errors > 0) + return; + + try { + if (IsCompilerGenerated) { + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + base.Emit (ec); + } + } else { + base.Emit (ec); + } + + // + // If `HasReturnLabel' is set, then we already emitted a + // jump to the end of the method, so we must emit a `ret' + // there. + // + // Unfortunately, System.Reflection.Emit automatically emits + // a leave to the end of a finally block. This is a problem + // if no code is following the try/finally block since we may + // jump to a point after the end of the method. + // As a workaround, we're always creating a return label in + // this case. + // + if (ec.HasReturnLabel || HasReachableClosingBrace) { + if (ec.HasReturnLabel) + ec.MarkLabel (ec.ReturnLabel); + + if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated) + ec.Mark (EndLocation); + + if (ec.ReturnType.Kind != MemberKind.Void) + ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ()); + + ec.Emit (OpCodes.Ret); + } + + } catch (Exception e) { + throw new InternalErrorException (e, StartLocation); + } + } + + public bool Resolve (BlockContext bc, IMethodData md) + { + if (resolved) + return true; + + var errors = bc.Report.Errors; + + base.Resolve (bc); + + if (bc.Report.Errors > errors) + return false; + + MarkReachable (new Reachability ()); + + if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) { + // TODO: var md = bc.CurrentMemberDefinition; + bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ()); + } + + if ((flags & Flags.NoFlowAnalysis) != 0) + return true; + + var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset); + try { + FlowAnalysis (fc); + } catch (Exception e) { + throw new InternalErrorException (e, StartLocation); + } + + return true; + } + } + + public class SwitchLabel : Statement + { + Constant converted; + Expression label; + + Label? il_label; + + // + // if expr == null, then it is the default case. + // + public SwitchLabel (Expression expr, Location l) + { + label = expr; + loc = l; + } + + public bool IsDefault { + get { + return label == null; + } + } + + public Expression Label { + get { + return label; + } + } + + public Location Location { + get { + return loc; + } + } + + public Constant Converted { + get { + return converted; + } + set { + converted = value; + } + } + + public bool SectionStart { get; set; } + + public Label GetILLabel (EmitContext ec) + { + if (il_label == null){ + il_label = ec.DefineLabel (); + } + + return il_label.Value; + } + + protected override void DoEmit (EmitContext ec) + { + ec.MarkLabel (GetILLabel (ec)); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (!SectionStart) + return false; + + fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment); + return false; + } + + public override bool Resolve (BlockContext bc) + { + if (ResolveAndReduce (bc)) + bc.Switch.RegisterLabel (bc, this); + + return true; + } + + // + // Resolves the expression, reduces it to a literal if possible + // and then converts it to the requested type. + // + bool ResolveAndReduce (BlockContext rc) + { + if (IsDefault) + return true; + + var c = label.ResolveLabelConstant (rc); + if (c == null) + return false; + + if (rc.Switch.IsNullable && c is NullLiteral) { + converted = c; + return true; + } + + converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType); + return converted != null; + } + + public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with) + { + ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null); + ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ()); + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + var t = (SwitchLabel) target; + if (label != null) + t.label = label.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public string GetSignatureForError () + { + string label; + if (converted == null) + label = "default"; + else + label = converted.GetValueAsLiteral (); + + return string.Format ("case {0}:", label); + } + } + + public class Switch : LoopStatement + { + // structure used to hold blocks of keys while calculating table switch + sealed class LabelsRange : IComparable + { + public readonly long min; + public long max; + public readonly List label_values; + + public LabelsRange (long value) + { + min = max = value; + label_values = new List (); + label_values.Add (value); + } + + public LabelsRange (long min, long max, ICollection values) + { + this.min = min; + this.max = max; + this.label_values = new List (values); + } + + public long Range { + get { + return max - min + 1; + } + } + + public bool AddValue (long value) + { + var gap = value - min + 1; + // Ensure the range has > 50% occupancy + if (gap > 2 * (label_values.Count + 1) || gap <= 0) + return false; + + max = value; + label_values.Add (value); + return true; + } + + public int CompareTo (LabelsRange other) + { + int nLength = label_values.Count; + int nLengthOther = other.label_values.Count; + if (nLengthOther == nLength) + return (int) (other.min - min); + + return nLength - nLengthOther; + } + } + + sealed class DispatchStatement : Statement + { + readonly Switch body; + + public DispatchStatement (Switch body) + { + this.body = body; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotImplementedException (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void DoEmit (EmitContext ec) + { + body.EmitDispatch (ec); + } + } + + class MissingBreak : Statement + { + readonly SwitchLabel label; + + public MissingBreak (SwitchLabel sl) + { + this.label = sl; + this.loc = sl.loc; + } + + public bool FallOut { get; set; } + + protected override void DoEmit (EmitContext ec) + { + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (FallOut) { + fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'", + label.GetSignatureForError ()); + } else { + fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another", + label.GetSignatureForError ()); + } + return true; + } + } + + public Expression Expr; + + // + // Mapping of all labels to their SwitchLabels + // + Dictionary labels; + Dictionary string_labels; + List case_labels; + + List> goto_cases; + List end_reachable_das; + + /// + /// The governing switch type + /// + public TypeSpec SwitchType; + + Expression new_expr; + + SwitchLabel case_null; + SwitchLabel case_default; + + Label defaultLabel, nullLabel; + VariableReference value; + ExpressionStatement string_dictionary; + FieldExpr switch_cache_field; + ExplicitBlock block; + bool end_reachable; + + // + // Nullable Types support + // + Nullable.Unwrap unwrap; + + public Switch (Expression e, ExplicitBlock block, Location l) + : base (block) + { + Expr = e; + this.block = block; + loc = l; + } + + public SwitchLabel ActiveLabel { get; set; } + + public ExplicitBlock Block { + get { + return block; + } + } + + public SwitchLabel DefaultLabel { + get { + return case_default; + } + } + + public bool IsNullable { + get { + return unwrap != null; + } + } + + public List RegisteredLabels { + get { + return case_labels; + } + } + + // + // Determines the governing type for a switch. The returned + // expression might be the expression from the switch, or an + // expression that includes any potential conversions to + // + static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr) + { + switch (expr.Type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.String: + case BuiltinTypeSpec.Type.Bool: + return expr; + } + + if (expr.Type.IsEnum) + return expr; + + // + // Try to find a *user* defined implicit conversion. + // + // If there is no implicit conversion, or if there are multiple + // conversions, we have to report an error + // + Expression converted = null; + foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) { + + if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType) + break; + + var restr = Convert.UserConversionRestriction.ImplicitOnly | + Convert.UserConversionRestriction.ProbingOnly; + + if (unwrapExpr) + restr |= Convert.UserConversionRestriction.NullableSourceOnly; + + var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null); + if (e == null) + continue; + + // + // Ignore over-worked ImplicitUserConversions that do + // an implicit conversion in addition to the user conversion. + // + var uc = e as UserCast; + if (uc == null) + continue; + + if (converted != null){ +// rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous "); + return null; + } + + converted = e; + } + return converted; + } + + public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable) + { + var types = module.Compiler.BuiltinTypes; + + // LAMESPEC: For some reason it does not contain bool which looks like csc bug + TypeSpec[] stypes = new[] { + types.SByte, + types.Byte, + types.Short, + types.UShort, + types.Int, + types.UInt, + types.Long, + types.ULong, + types.Char, + types.String + }; + + if (nullable != null) { + + Array.Resize (ref stypes, stypes.Length + 9); + + for (int i = 0; i < 9; ++i) { + stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] }); + } + } + + return stypes; + } + + public void RegisterLabel (BlockContext rc, SwitchLabel sl) + { + case_labels.Add (sl); + + if (sl.IsDefault) { + if (case_default != null) { + sl.Error_AlreadyOccurs (rc, case_default); + } else { + case_default = sl; + } + + return; + } + + try { + if (string_labels != null) { + string string_value = sl.Converted.GetValue () as string; + if (string_value == null) + case_null = sl; + else + string_labels.Add (string_value, sl); + } else { + if (sl.Converted is NullLiteral) { + case_null = sl; + } else { + labels.Add (sl.Converted.GetValueAsLong (), sl); + } + } + } catch (ArgumentException) { + if (string_labels != null) + sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]); + else + sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]); + } + } + + // + // This method emits code for a lookup-based switch statement (non-string) + // Basically it groups the cases into blocks that are at least half full, + // and then spits out individual lookup opcodes for each block. + // It emits the longest blocks first, and short blocks are just + // handled with direct compares. + // + void EmitTableSwitch (EmitContext ec, Expression val) + { + if (labels != null && labels.Count > 0) { + List ranges; + if (string_labels != null) { + // We have done all hard work for string already + // setup single range only + ranges = new List (1); + ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys)); + } else { + var element_keys = new long[labels.Count]; + labels.Keys.CopyTo (element_keys, 0); + Array.Sort (element_keys); + + // + // Build possible ranges of switch labes to reduce number + // of comparisons + // + ranges = new List (element_keys.Length); + var range = new LabelsRange (element_keys[0]); + ranges.Add (range); + for (int i = 1; i < element_keys.Length; ++i) { + var l = element_keys[i]; + if (range.AddValue (l)) + continue; + + range = new LabelsRange (l); + ranges.Add (range); + } + + // sort the blocks so we can tackle the largest ones first + ranges.Sort (); + } + + Label lbl_default = defaultLabel; + TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType; + + for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) { + LabelsRange kb = ranges[range_index]; + lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel (); + + // Optimize small ranges using simple equality check + if (kb.Range <= 2) { + foreach (var key in kb.label_values) { + SwitchLabel sl = labels[key]; + if (sl == case_default || sl == case_null) + continue; + + if (sl.Converted.IsZeroInteger) { + val.EmitBranchable (ec, sl.GetILLabel (ec), false); + } else { + val.Emit (ec); + sl.Converted.Emit (ec); + ec.Emit (OpCodes.Beq, sl.GetILLabel (ec)); + } + } + } else { + // TODO: if all the keys in the block are the same and there are + // no gaps/defaults then just use a range-check. + if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) { + // TODO: optimize constant/I4 cases + + // check block range (could be > 2^31) + val.Emit (ec); + ec.EmitLong (kb.min); + ec.Emit (OpCodes.Blt, lbl_default); + + val.Emit (ec); + ec.EmitLong (kb.max); + ec.Emit (OpCodes.Bgt, lbl_default); + + // normalize range + val.Emit (ec); + if (kb.min != 0) { + ec.EmitLong (kb.min); + ec.Emit (OpCodes.Sub); + } + + ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels! + } else { + // normalize range + val.Emit (ec); + int first = (int) kb.min; + if (first > 0) { + ec.EmitInt (first); + ec.Emit (OpCodes.Sub); + } else if (first < 0) { + ec.EmitInt (-first); + ec.Emit (OpCodes.Add); + } + } + + // first, build the list of labels for the switch + int iKey = 0; + long cJumps = kb.Range; + Label[] switch_labels = new Label[cJumps]; + for (int iJump = 0; iJump < cJumps; iJump++) { + var key = kb.label_values[iKey]; + if (key == kb.min + iJump) { + switch_labels[iJump] = labels[key].GetILLabel (ec); + iKey++; + } else { + switch_labels[iJump] = lbl_default; + } + } + + // emit the switch opcode + ec.Emit (OpCodes.Switch, switch_labels); + } + + // mark the default for this block + if (range_index != 0) + ec.MarkLabel (lbl_default); + } + + // the last default just goes to the end + if (ranges.Count > 0) + ec.Emit (OpCodes.Br, lbl_default); + } + } + + public SwitchLabel FindLabel (Constant value) + { + SwitchLabel sl = null; + + if (string_labels != null) { + string s = value.GetValue () as string; + if (s == null) { + if (case_null != null) + sl = case_null; + else if (case_default != null) + sl = case_default; + } else { + string_labels.TryGetValue (s, out sl); + } + } else { + if (value is NullLiteral) { + sl = case_null; + } else { + labels.TryGetValue (value.GetValueAsLong (), out sl); + } + } + + return sl; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + Expr.FlowAnalysis (fc); + + var prev_switch = fc.SwitchInitialDefinitiveAssignment; + var InitialDefinitiveAssignment = fc.DefiniteAssignment; + fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment; + + block.FlowAnalysis (fc); + + fc.SwitchInitialDefinitiveAssignment = prev_switch; + + if (end_reachable_das != null) { + var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das); + InitialDefinitiveAssignment |= sections_das; + end_reachable_das = null; + } + + fc.DefiniteAssignment = InitialDefinitiveAssignment; + + return case_default != null && !end_reachable; + } + + public override bool Resolve (BlockContext ec) + { + Expr = Expr.Resolve (ec); + if (Expr == null) + return false; + + // + // LAMESPEC: User conversion from non-nullable governing type has a priority + // + new_expr = SwitchGoverningType (ec, Expr, false); + + if (new_expr == null) { + if (Expr.Type.IsNullableType) { + unwrap = Nullable.Unwrap.Create (Expr, false); + if (unwrap == null) + return false; + + // + // Unwrap + user conversion using non-nullable type is not allowed but user operator + // involving nullable Expr and nullable governing type is + // + new_expr = SwitchGoverningType (ec, unwrap, true); + } + } + + if (new_expr == null) { + if (Expr.Type != InternalType.ErrorType) { + ec.Report.Error (151, loc, + "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type", + Expr.Type.GetSignatureForError ()); + } + + return false; + } + + SwitchType = new_expr.Type; + if (SwitchType.IsNullableType) { + new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true); + SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType); + } + + if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) { + ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type"); + return false; + } + + if (block.Statements.Count == 0) + return true; + + if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) { + string_labels = new Dictionary (); + } else { + labels = new Dictionary (); + } + + case_labels = new List (); + + var constant = new_expr as Constant; + + // + // Don't need extra variable for constant switch or switch with + // only default case + // + if (constant == null) { + // + // Store switch expression for comparison purposes + // + value = new_expr as VariableReference; + if (value == null && !HasOnlyDefaultSection ()) { + var current_block = ec.CurrentBlock; + ec.CurrentBlock = Block; + // Create temporary variable inside switch scope + value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc); + value.Resolve (ec); + ec.CurrentBlock = current_block; + } + } + + Switch old_switch = ec.Switch; + ec.Switch = this; + var parent_los = ec.EnclosingLoopOrSwitch; + ec.EnclosingLoopOrSwitch = this; + + var ok = Statement.Resolve (ec); + + ec.EnclosingLoopOrSwitch = parent_los; + ec.Switch = old_switch; + + // + // Check if all goto cases are valid. Needs to be done after switch + // is resolved because goto can jump forward in the scope. + // + if (goto_cases != null) { + foreach (var gc in goto_cases) { + if (gc.Item1 == null) { + if (DefaultLabel == null) { + Goto.Error_UnknownLabel (ec, "default", loc); + } + + continue; + } + + var sl = FindLabel (gc.Item2); + if (sl == null) { + Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc); + } else { + gc.Item1.Label = sl; + } + } + } + + if (!ok) + return false; + + if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) { + ResolveStringSwitchMap (ec); + } + + // + // Anonymous storey initialization has to happen before + // any generated switch dispatch + // + block.InsertStatement (0, new DispatchStatement (this)); + + return true; + } + + bool HasOnlyDefaultSection () + { + for (int i = 0; i < block.Statements.Count; ++i) { + var s = block.Statements[i] as SwitchLabel; + + if (s == null || s.IsDefault) + continue; + + return false; + } + + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + base.MarkReachable (rc); + + block.MarkReachableScope (rc); + + if (block.Statements.Count == 0) + return rc; + + SwitchLabel constant_label = null; + var constant = new_expr as Constant; + + if (constant != null) { + constant_label = FindLabel (constant) ?? case_default; + if (constant_label == null) { + block.Statements.RemoveAt (0); + return rc; + } + } + + var section_rc = new Reachability (); + SwitchLabel prev_label = null; + + for (int i = 0; i < block.Statements.Count; ++i) { + var s = block.Statements[i]; + var sl = s as SwitchLabel; + + if (sl != null && sl.SectionStart) { + // + // Section is marked already via goto case + // + if (!sl.IsUnreachable) { + section_rc = new Reachability (); + continue; + } + + if (constant_label != null && constant_label != sl) + section_rc = Reachability.CreateUnreachable (); + else if (section_rc.IsUnreachable) { + section_rc = new Reachability (); + } else { + if (prev_label != null) { + sl.SectionStart = false; + s = new MissingBreak (prev_label); + s.MarkReachable (rc); + block.Statements.Insert (i - 1, s); + ++i; + } + } + + prev_label = sl; + } + + section_rc = s.MarkReachable (section_rc); + } + + if (!section_rc.IsUnreachable && prev_label != null) { + prev_label.SectionStart = false; + var s = new MissingBreak (prev_label) { + FallOut = true + }; + + s.MarkReachable (rc); + block.Statements.Add (s); + } + + // + // Reachability can affect parent only when all possible paths are handled but + // we still need to run reachability check on switch body to check for fall-through + // + if (case_default == null && constant_label == null) + return rc; + + // + // We have at least one local exit from the switch + // + if (end_reachable) + return rc; + + return Reachability.CreateUnreachable (); + } + + public void RegisterGotoCase (GotoCase gotoCase, Constant value) + { + if (goto_cases == null) + goto_cases = new List> (); + + goto_cases.Add (Tuple.Create (gotoCase, value)); + } + + // + // Converts string switch into string hashtable + // + void ResolveStringSwitchMap (ResolveContext ec) + { + FullNamedExpression string_dictionary_type; + if (ec.Module.PredefinedTypes.Dictionary.Define ()) { + string_dictionary_type = new TypeExpression ( + ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec, + new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }), + loc); + } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) { + string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc); + } else { + ec.Module.PredefinedTypes.Dictionary.Resolve (); + return; + } + + var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer; + Field field = new Field (ctype, string_dictionary_type, + Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, + new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null); + if (!field.Define ()) + return; + ctype.AddField (field); + + var init = new List (); + int counter = -1; + labels = new Dictionary (string_labels.Count); + string value = null; + + foreach (SwitchLabel sl in case_labels) { + + if (sl.SectionStart) + labels.Add (++counter, sl); + + if (sl == case_default || sl == case_null) + continue; + + value = (string) sl.Converted.GetValue (); + var init_args = new List (2); + init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location)); + + sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc); + init_args.Add (sl.Converted); + + init.Add (new CollectionElementInitializer (init_args, loc)); + } + + Arguments args = new Arguments (1); + args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc))); + Expression initializer = new NewInitialize (string_dictionary_type, args, + new CollectionOrObjectInitializers (init, loc), loc); + + switch_cache_field = new FieldExpr (field, loc); + string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec)); + } + + void DoEmitStringSwitch (EmitContext ec) + { + Label l_initialized = ec.DefineLabel (); + + // + // Skip initialization when value is null + // + value.EmitBranchable (ec, nullLabel, false); + + // + // Check if string dictionary is initialized and initialize + // + switch_cache_field.EmitBranchable (ec, l_initialized, true); + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + string_dictionary.EmitStatement (ec); + } + ec.MarkLabel (l_initialized); + + LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int); + + ResolveContext rc = new ResolveContext (ec.MemberContext); + + if (switch_cache_field.Type.IsGeneric) { + Arguments get_value_args = new Arguments (2); + get_value_args.Add (new Argument (value)); + get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out)); + Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc); + if (get_item == null) + return; + + // + // A value was not found, go to default case + // + get_item.EmitBranchable (ec, defaultLabel, false); + } else { + Arguments get_value_args = new Arguments (1); + get_value_args.Add (new Argument (value)); + + Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc); + if (get_item == null) + return; + + LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object); + get_item_object.EmitAssign (ec, get_item, true, false); + ec.Emit (OpCodes.Brfalse, defaultLabel); + + ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable, + new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc); + + get_item_int.EmitStatement (ec); + get_item_object.Release (ec); + } + + EmitTableSwitch (ec, string_switch_variable); + string_switch_variable.Release (ec); + } + + // + // Emits switch using simple if/else comparison for small label count (4 + optional default) + // + void EmitShortSwitch (EmitContext ec) + { + MethodSpec equal_method = null; + if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) { + equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc); + } + + if (equal_method != null) { + value.EmitBranchable (ec, nullLabel, false); + } + + for (int i = 0; i < case_labels.Count; ++i) { + var label = case_labels [i]; + if (label == case_default || label == case_null) + continue; + + var constant = label.Converted; + + if (equal_method != null) { + value.Emit (ec); + constant.Emit (ec); + + var call = new CallEmitter (); + call.EmitPredefined (ec, equal_method, new Arguments (0)); + ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec)); + continue; + } + + if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) { + value.EmitBranchable (ec, label.GetILLabel (ec), false); + continue; + } + + value.Emit (ec); + constant.Emit (ec); + ec.Emit (OpCodes.Beq, label.GetILLabel (ec)); + } + + ec.Emit (OpCodes.Br, defaultLabel); + } + + void EmitDispatch (EmitContext ec) + { + if (value == null) { + // + // Constant switch, we've already done the work if there is only 1 label + // referenced + // + int reachable = 0; + foreach (var sl in case_labels) { + if (sl.IsUnreachable) + continue; + + if (reachable++ > 0) { + var constant = (Constant) new_expr; + var constant_label = FindLabel (constant) ?? case_default; + + ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec)); + break; + } + } + + return; + } + + if (string_dictionary != null) { + DoEmitStringSwitch (ec); + } else if (case_labels.Count < 4 || string_labels != null) { + EmitShortSwitch (ec); + } else { + EmitTableSwitch (ec, value); + } + } + + protected override void DoEmit (EmitContext ec) + { + // + // Setup the codegen context + // + Label old_end = ec.LoopEnd; + Switch old_switch = ec.Switch; + + ec.LoopEnd = ec.DefineLabel (); + ec.Switch = this; + + defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec); + nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec); + + if (value != null) { + ec.Mark (loc); + if (IsNullable) { + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, nullLabel); + value.EmitAssign (ec, new_expr, false, false); + } else if (new_expr != value) { + value.EmitAssign (ec, new_expr, false, false); + } + + + // + // Next statement is compiler generated we don't need extra + // nop when we can use the statement for sequence point + // + ec.Mark (block.StartLocation); + block.IsCompilerGenerated = true; + } else { + new_expr.EmitSideEffect (ec); + } + + block.Emit (ec); + + // Restore context state. + ec.MarkLabel (ec.LoopEnd); + + // + // Restore the previous context + // + ec.LoopEnd = old_end; + ec.Switch = old_switch; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Switch target = (Switch) t; + + target.Expr = Expr.Clone (clonectx); + target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void AddEndDefiniteAssignment (FlowAnalysisContext fc) + { + if (case_default == null && !(new_expr is Constant)) + return; + + if (end_reachable_das == null) + end_reachable_das = new List (); + + end_reachable_das.Add (fc.DefiniteAssignment); + } + + public override void SetEndReachable () + { + end_reachable = true; + } + } + + // A place where execution can restart in a state machine + public abstract class ResumableStatement : Statement + { + bool prepared; + protected Label resume_point; + + public Label PrepareForEmit (EmitContext ec) + { + if (!prepared) { + prepared = true; + resume_point = ec.DefineLabel (); + } + return resume_point; + } + + public virtual Label PrepareForDispose (EmitContext ec, Label end) + { + return end; + } + + public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher) + { + } + } + + public abstract class TryFinallyBlock : ExceptionStatement + { + protected Statement stmt; + Label dispose_try_block; + bool prepared_for_dispose, emitted_dispose; + Method finally_host; + + protected TryFinallyBlock (Statement stmt, Location loc) + : base (loc) + { + this.stmt = stmt; + } + + #region Properties + + public Statement Statement { + get { + return stmt; + } + } + + #endregion + + protected abstract void EmitTryBody (EmitContext ec); + public abstract void EmitFinallyBody (EmitContext ec); + + public override Label PrepareForDispose (EmitContext ec, Label end) + { + if (!prepared_for_dispose) { + prepared_for_dispose = true; + dispose_try_block = ec.DefineLabel (); + } + return dispose_try_block; + } + + protected sealed override void DoEmit (EmitContext ec) + { + EmitTryBodyPrepare (ec); + EmitTryBody (ec); + + bool beginFinally = EmitBeginFinallyBlock (ec); + + Label start_finally = ec.DefineLabel (); + if (resume_points != null && beginFinally) { + var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod; + + ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally); + ec.Emit (OpCodes.Brfalse_S, start_finally); + ec.Emit (OpCodes.Endfinally); + } + + ec.MarkLabel (start_finally); + + if (finally_host != null) { + finally_host.Define (); + finally_host.PrepareEmit (); + finally_host.Emit (); + + // Now it's safe to add, to close it properly and emit sequence points + finally_host.Parent.AddMember (finally_host); + + var ce = new CallEmitter (); + ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true); + } else { + EmitFinallyBody (ec); + } + + if (beginFinally) + ec.EndExceptionBlock (); + } + + public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher) + { + if (emitted_dispose) + return; + + emitted_dispose = true; + + Label end_of_try = ec.DefineLabel (); + + // Ensure that the only way we can get into this code is through a dispatcher + if (have_dispatcher) + ec.Emit (OpCodes.Br, end); + + ec.BeginExceptionBlock (); + + ec.MarkLabel (dispose_try_block); + + Label[] labels = null; + for (int i = 0; i < resume_points.Count; ++i) { + ResumableStatement s = resume_points[i]; + Label ret = s.PrepareForDispose (ec, end_of_try); + if (ret.Equals (end_of_try) && labels == null) + continue; + if (labels == null) { + labels = new Label[resume_points.Count]; + for (int j = 0; j < i; ++j) + labels[j] = end_of_try; + } + labels[i] = ret; + } + + if (labels != null) { + int j; + for (j = 1; j < labels.Length; ++j) + if (!labels[0].Equals (labels[j])) + break; + bool emit_dispatcher = j < labels.Length; + + if (emit_dispatcher) { + ec.Emit (OpCodes.Ldloc, pc); + ec.EmitInt (first_resume_pc); + ec.Emit (OpCodes.Sub); + ec.Emit (OpCodes.Switch, labels); + } + + foreach (ResumableStatement s in resume_points) + s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher); + } + + ec.MarkLabel (end_of_try); + + ec.BeginFinallyBlock (); + + if (finally_host != null) { + var ce = new CallEmitter (); + ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true); + } else { + EmitFinallyBody (ec); + } + + ec.EndExceptionBlock (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + var res = stmt.FlowAnalysis (fc); + parent = null; + return res; + } + + protected virtual bool EmitBeginFinallyBlock (EmitContext ec) + { + ec.BeginFinallyBlock (); + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Statement.MarkReachable (rc); + } + + public override bool Resolve (BlockContext bc) + { + bool ok; + + parent = bc.CurrentTryBlock; + bc.CurrentTryBlock = this; + + using (bc.Set (ResolveContext.Options.TryScope)) { + ok = stmt.Resolve (bc); + } + + bc.CurrentTryBlock = parent; + + // + // Finally block inside iterator is called from MoveNext and + // Dispose methods that means we need to lift the block into + // newly created host method to emit the body only once. The + // original block then simply calls the newly generated method. + // + if (bc.CurrentIterator != null && !bc.IsInProbingMode) { + var b = stmt as Block; + if (b != null && b.Explicit.HasYield) { + finally_host = bc.CurrentIterator.CreateFinallyHost (this); + } + } + + return base.Resolve (bc) && ok; + } + } + + // + // Base class for blocks using exception handling + // + public abstract class ExceptionStatement : ResumableStatement + { + protected List resume_points; + protected int first_resume_pc; + protected ExceptionStatement parent; + + protected ExceptionStatement (Location loc) + { + this.loc = loc; + } + + protected virtual void EmitTryBodyPrepare (EmitContext ec) + { + StateMachineInitializer state_machine = null; + if (resume_points != null) { + state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod; + + ec.EmitInt ((int) IteratorStorey.State.Running); + ec.Emit (OpCodes.Stloc, state_machine.CurrentPC); + } + + ec.BeginExceptionBlock (); + + if (resume_points != null) { + ec.MarkLabel (resume_point); + + // For normal control flow, we want to fall-through the Switch + // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above + ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC); + ec.EmitInt (first_resume_pc); + ec.Emit (OpCodes.Sub); + + Label[] labels = new Label[resume_points.Count]; + for (int i = 0; i < resume_points.Count; ++i) + labels[i] = resume_points[i].PrepareForEmit (ec); + ec.Emit (OpCodes.Switch, labels); + } + } + + public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine) + { + if (parent != null) { + // TODO: MOVE to virtual TryCatch + var tc = this as TryCatch; + var s = tc != null && tc.IsTryCatchFinally ? stmt : this; + + pc = parent.AddResumePoint (s, pc, stateMachine); + } else { + pc = stateMachine.AddResumePoint (this); + } + + if (resume_points == null) { + resume_points = new List (); + first_resume_pc = pc; + } + + if (pc != first_resume_pc + resume_points.Count) + throw new InternalErrorException ("missed an intervening AddResumePoint?"); + + resume_points.Add (stmt); + return pc; + } + } + + public class Lock : TryFinallyBlock + { + Expression expr; + TemporaryVariableReference expr_copy; + TemporaryVariableReference lock_taken; + + public Lock (Expression expr, Statement stmt, Location loc) + : base (stmt, loc) + { + this.expr = expr; + } + + public Expression Expr { + get { + return this.expr; + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + return base.DoFlowAnalysis (fc); + } + + public override bool Resolve (BlockContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return false; + + if (!TypeSpec.IsReferenceType (expr.Type)) { + ec.Report.Error (185, loc, + "`{0}' is not a reference type as required by the lock statement", + expr.Type.GetSignatureForError ()); + } + + if (expr.Type.IsGenericParameter) { + expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object); + } + + VariableReference lv = expr as VariableReference; + bool locked; + if (lv != null) { + locked = lv.IsLockedByStatement; + lv.IsLockedByStatement = true; + } else { + lv = null; + locked = false; + } + + // + // Have to keep original lock value around to unlock same location + // in the case of original value has changed or is null + // + expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc); + expr_copy.Resolve (ec); + + // + // Ensure Monitor methods are available + // + if (ResolvePredefinedMethods (ec) > 1) { + lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc); + lock_taken.Resolve (ec); + } + + using (ec.Set (ResolveContext.Options.LockScope)) { + base.Resolve (ec); + } + + if (lv != null) { + lv.IsLockedByStatement = locked; + } + + return true; + } + + protected override void EmitTryBodyPrepare (EmitContext ec) + { + expr_copy.EmitAssign (ec, expr); + + if (lock_taken != null) { + // + // Initialize ref variable + // + lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc)); + } else { + // + // Monitor.Enter (expr_copy) + // + expr_copy.Emit (ec); + ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ()); + } + + base.EmitTryBodyPrepare (ec); + } + + protected override void EmitTryBody (EmitContext ec) + { + // + // Monitor.Enter (expr_copy, ref lock_taken) + // + if (lock_taken != null) { + expr_copy.Emit (ec); + lock_taken.LocalInfo.CreateBuilder (ec); + lock_taken.AddressOf (ec, AddressOp.Load); + ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ()); + } + + Statement.Emit (ec); + } + + public override void EmitFinallyBody (EmitContext ec) + { + // + // if (lock_taken) Monitor.Exit (expr_copy) + // + Label skip = ec.DefineLabel (); + + if (lock_taken != null) { + lock_taken.Emit (ec); + ec.Emit (OpCodes.Brfalse_S, skip); + } + + expr_copy.Emit (ec); + var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc); + if (m != null) + ec.Emit (OpCodes.Call, m); + + ec.MarkLabel (skip); + } + + int ResolvePredefinedMethods (ResolveContext rc) + { + // Try 4.0 Monitor.Enter (object, ref bool) overload first + var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get (); + if (m != null) + return 4; + + m = rc.Module.PredefinedMembers.MonitorEnter.Get (); + if (m != null) + return 1; + + rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc); + return 0; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Lock target = (Lock) t; + + target.expr = expr.Clone (clonectx); + target.stmt = Statement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + public class Unchecked : Statement { + public Block Block; + + public Unchecked (Block b, Location loc) + { + Block = b; + b.Unchecked = true; + this.loc = loc; + } + + public override bool Resolve (BlockContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, false)) + return Block.Resolve (ec); + } + + protected override void DoEmit (EmitContext ec) + { + using (ec.With (EmitContext.Options.CheckedScope, false)) + Block.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return Block.FlowAnalysis (fc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Block.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Unchecked target = (Unchecked) t; + + target.Block = clonectx.LookupBlock (Block); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Checked : Statement { + public Block Block; + + public Checked (Block b, Location loc) + { + Block = b; + b.Unchecked = false; + this.loc = loc; + } + + public override bool Resolve (BlockContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, true)) + return Block.Resolve (ec); + } + + protected override void DoEmit (EmitContext ec) + { + using (ec.With (EmitContext.Options.CheckedScope, true)) + Block.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return Block.FlowAnalysis (fc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Block.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Checked target = (Checked) t; + + target.Block = clonectx.LookupBlock (Block); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Unsafe : Statement { + public Block Block; + + public Unsafe (Block b, Location loc) + { + Block = b; + Block.Unsafe = true; + this.loc = loc; + } + + public override bool Resolve (BlockContext ec) + { + if (ec.CurrentIterator != null) + ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators"); + + using (ec.Set (ResolveContext.Options.UnsafeScope)) + return Block.Resolve (ec); + } + + protected override void DoEmit (EmitContext ec) + { + Block.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return Block.FlowAnalysis (fc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Block.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Unsafe target = (Unsafe) t; + + target.Block = clonectx.LookupBlock (Block); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Fixed statement + // + public class Fixed : Statement + { + abstract class Emitter : ShimExpression + { + protected LocalVariable vi; + + protected Emitter (Expression expr, LocalVariable li) + : base (expr) + { + vi = li; + } + + public abstract void EmitExit (EmitContext ec); + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + } + + class ExpressionEmitter : Emitter { + public ExpressionEmitter (Expression converted, LocalVariable li) : + base (converted, li) + { + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotImplementedException (); + } + + public override void Emit (EmitContext ec) { + // + // Store pointer in pinned location + // + expr.Emit (ec); + vi.EmitAssign (ec); + } + + public override void EmitExit (EmitContext ec) + { + ec.EmitInt (0); + ec.Emit (OpCodes.Conv_U); + vi.EmitAssign (ec); + } + } + + class StringEmitter : Emitter + { + LocalVariable pinned_string; + + public StringEmitter (Expression expr, LocalVariable li) + : base (expr, li) + { + } + + protected override Expression DoResolve (ResolveContext rc) + { + pinned_string = new LocalVariable (vi.Block, "$pinned", + LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used, + vi.Location); + pinned_string.Type = rc.BuiltinTypes.String; + + eclass = ExprClass.Variable; + type = rc.BuiltinTypes.Int; + return this; + } + + public override void Emit (EmitContext ec) + { + pinned_string.CreateBuilder (ec); + + expr.Emit (ec); + pinned_string.EmitAssign (ec); + + // TODO: Should use Binary::Add + pinned_string.Emit (ec); + ec.Emit (OpCodes.Conv_I); + + var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc); + if (m == null) + return; + + PropertyExpr pe = new PropertyExpr (m, pinned_string.Location); + //pe.InstanceExpression = pinned_string; + pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec); + + ec.Emit (OpCodes.Add); + vi.EmitAssign (ec); + } + + public override void EmitExit (EmitContext ec) + { + ec.EmitNull (); + pinned_string.EmitAssign (ec); + } + } + + public class VariableDeclaration : BlockVariable + { + public VariableDeclaration (FullNamedExpression type, LocalVariable li) + : base (type, li) + { + } + + protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer) + { + if (!Variable.Type.IsPointer && li == Variable) { + bc.Report.Error (209, TypeExpression.Location, + "The type of locals declared in a fixed statement must be a pointer type"); + return null; + } + + // + // The rules for the possible declarators are pretty wise, + // but the production on the grammar is more concise. + // + // So we have to enforce these rules here. + // + // We do not resolve before doing the case 1 test, + // because the grammar is explicit in that the token & + // is present, so we need to test for this particular case. + // + + if (initializer is Cast) { + bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression"); + return null; + } + + initializer = initializer.Resolve (bc); + + if (initializer == null) + return null; + + // + // Case 1: Array + // + if (initializer.Type.IsArray) { + TypeSpec array_type = TypeManager.GetElementType (initializer.Type); + + // + // Provided that array_type is unmanaged, + // + if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc)) + return null; + + // + // and T* is implicitly convertible to the + // pointer type given in the fixed statement. + // + ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc); + + Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc); + if (converted == null) + return null; + + // + // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0]) + // + converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr, + new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)), + new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))), + new NullLiteral (loc), + converted, loc); + + converted = converted.Resolve (bc); + + return new ExpressionEmitter (converted, li); + } + + // + // Case 2: string + // + if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) { + return new StringEmitter (initializer, li).Resolve (bc); + } + + // Case 3: fixed buffer + if (initializer is FixedBufferPtr) { + return new ExpressionEmitter (initializer, li); + } + + // + // Case 4: & object. + // + bool already_fixed = true; + Unary u = initializer as Unary; + if (u != null && u.Oper == Unary.Operator.AddressOf) { + IVariableReference vr = u.Expr as IVariableReference; + if (vr == null || !vr.IsFixed) { + already_fixed = false; + } + } + + if (already_fixed) { + bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression"); + } + + initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc); + return new ExpressionEmitter (initializer, li); + } + } + + + VariableDeclaration decl; + Statement statement; + bool has_ret; + + public Fixed (VariableDeclaration decl, Statement stmt, Location l) + { + this.decl = decl; + statement = stmt; + loc = l; + } + + #region Properties + + public Statement Statement { + get { + return statement; + } + } + + public BlockVariable Variables { + get { + return decl; + } + } + + #endregion + + public override bool Resolve (BlockContext bc) + { + using (bc.Set (ResolveContext.Options.FixedInitializerScope)) { + if (!decl.Resolve (bc)) + return false; + } + + return statement.Resolve (bc); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + decl.FlowAnalysis (fc); + return statement.FlowAnalysis (fc); + } + + protected override void DoEmit (EmitContext ec) + { + decl.Variable.CreateBuilder (ec); + decl.Initializer.Emit (ec); + if (decl.Declarators != null) { + foreach (var d in decl.Declarators) { + d.Variable.CreateBuilder (ec); + d.Initializer.Emit (ec); + } + } + + statement.Emit (ec); + + if (has_ret) + return; + + // + // Clear the pinned variable + // + ((Emitter) decl.Initializer).EmitExit (ec); + if (decl.Declarators != null) { + foreach (var d in decl.Declarators) { + ((Emitter)d.Initializer).EmitExit (ec); + } + } + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + decl.MarkReachable (rc); + + rc = statement.MarkReachable (rc); + + // TODO: What if there is local exit? + has_ret = rc.IsUnreachable; + return rc; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Fixed target = (Fixed) t; + + target.decl = (VariableDeclaration) decl.Clone (clonectx); + target.statement = statement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Catch : Statement + { + class FilterStatement : Statement + { + readonly Catch ctch; + + public FilterStatement (Catch ctch) + { + this.ctch = ctch; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + } + + protected override void DoEmit (EmitContext ec) + { + if (ctch.li != null) { + if (ctch.hoisted_temp != null) + ctch.hoisted_temp.Emit (ec); + else + ctch.li.Emit (ec); + + if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter) + ec.Emit (OpCodes.Box, ctch.type); + } + + var expr_start = ec.DefineLabel (); + var end = ec.DefineLabel (); + + ec.Emit (OpCodes.Brtrue_S, expr_start); + ec.EmitInt (0); + ec.Emit (OpCodes.Br, end); + ec.MarkLabel (expr_start); + + ctch.Filter.Emit (ec); + + ec.MarkLabel (end); + ec.Emit (OpCodes.Endfilter); + ec.BeginFilterHandler (); + ec.Emit (OpCodes.Pop); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + ctch.Filter.FlowAnalysis (fc); + return true; + } + + public override bool Resolve (BlockContext bc) + { + ctch.Filter = ctch.Filter.Resolve (bc); + + if (ctch.Filter != null) { + if (ctch.Filter.ContainsEmitWithAwait ()) { + bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause"); + } + + var c = ctch.Filter as Constant; + if (c != null && !c.IsDefaultValue) { + bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant"); + } + } + + return true; + } + } + + ExplicitBlock block; + LocalVariable li; + FullNamedExpression type_expr; + CompilerAssign assign; + TypeSpec type; + LocalTemporary hoisted_temp; + + public Catch (ExplicitBlock block, Location loc) + { + this.block = block; + this.loc = loc; + } + + #region Properties + + public ExplicitBlock Block { + get { + return block; + } + } + + public TypeSpec CatchType { + get { + return type; + } + } + + public Expression Filter { + get; set; + } + + public bool IsGeneral { + get { + return type_expr == null; + } + } + + public FullNamedExpression TypeExpression { + get { + return type_expr; + } + set { + type_expr = value; + } + } + + public LocalVariable Variable { + get { + return li; + } + set { + li = value; + } + } + + #endregion + + protected override void DoEmit (EmitContext ec) + { + if (Filter != null) { + ec.BeginExceptionFilterBlock (); + ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType); + + if (li != null) + EmitCatchVariableStore (ec); + + if (Block.HasAwait) { + Block.EmitScopeInitialization (ec); + } else { + Block.Emit (ec); + } + + return; + } + + if (IsGeneral) + ec.BeginCatchBlock (ec.BuiltinTypes.Object); + else + ec.BeginCatchBlock (CatchType); + + if (li != null) { + EmitCatchVariableStore (ec); + } else { + ec.Emit (OpCodes.Pop); + } + + if (!Block.HasAwait) + Block.Emit (ec); + } + + void EmitCatchVariableStore (EmitContext ec) + { + li.CreateBuilder (ec); + + // + // Special case hoisted catch variable, we have to use a temporary variable + // to pass via anonymous storey initialization with the value still on top + // of the stack + // + if (li.HoistedVariant != null) { + hoisted_temp = new LocalTemporary (li.Type); + hoisted_temp.Store (ec); + + // switch to assignment from temporary variable and not from top of the stack + assign.UpdateSource (hoisted_temp); + } + } + + public override bool Resolve (BlockContext bc) + { + using (bc.Set (ResolveContext.Options.CatchScope)) { + if (type_expr == null) { + if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) { + Expression source = new EmptyExpression (li.Type); + assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null); + Block.AddScopeStatement (new StatementExpression (assign, Location.Null)); + } + } else { + type = type_expr.ResolveAsType (bc); + if (type == null) + return false; + + if (li == null) + CreateExceptionVariable (type); + + if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) { + bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception"); + } else if (li != null) { + li.Type = type; + li.PrepareAssignmentAnalysis (bc); + + // source variable is at the top of the stack + Expression source = new EmptyExpression (li.Type); + if (li.Type.IsGenericParameter) + source = new UnboxCast (source, li.Type); + + // + // Uses Location.Null to hide from symbol file + // + assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null); + Block.AddScopeStatement (new StatementExpression (assign, Location.Null)); + } + } + + if (Filter != null) { + Block.AddScopeStatement (new FilterStatement (this)); + } + + Block.SetCatchBlock (); + return Block.Resolve (bc); + } + } + + bool CreateExceptionVariable (TypeSpec type) + { + if (!Block.HasAwait) + return false; + + // TODO: Scan the block for rethrow expression + //if (!Block.HasRethrow) + // return; + + li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null); + return true; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (li != null && !li.IsCompilerGenerated) { + fc.SetVariableAssigned (li.VariableInfo, true); + } + + return block.FlowAnalysis (fc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + var c = Filter as Constant; + if (c != null && c.IsDefaultValue) + return Reachability.CreateUnreachable (); + + return block.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Catch target = (Catch) t; + + if (type_expr != null) + target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx); + + if (Filter != null) + target.Filter = Filter.Clone (clonectx); + + target.block = (ExplicitBlock) clonectx.LookupBlock (block); + } + } + + public class TryFinally : TryFinallyBlock + { + ExplicitBlock fini; + List try_exit_dat; + List