16 changed files with 699 additions and 209 deletions
@ -0,0 +1,151 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// C# compiler settings.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable] |
||||||
|
public class CompilerSettings : AbstractFreezable |
||||||
|
{ |
||||||
|
protected override void FreezeInternal() |
||||||
|
{ |
||||||
|
conditionalSymbols = FreezableHelper.FreezeList(conditionalSymbols); |
||||||
|
specificWarningsAsErrors = FreezableHelper.FreezeList(specificWarningsAsErrors); |
||||||
|
disabledWarnings = FreezableHelper.FreezeList(disabledWarnings); |
||||||
|
base.FreezeInternal(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new CompilerSettings instance.
|
||||||
|
/// </summary>
|
||||||
|
public CompilerSettings() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
bool allowUnsafeBlocks = true; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/Sets whether <c>unsafe</c> code is allowed.
|
||||||
|
/// The default is <c>true</c>. If set to false, parsing unsafe code will result in parser errors.
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowUnsafeBlocks { |
||||||
|
get { return allowUnsafeBlocks; } |
||||||
|
set { |
||||||
|
FreezableHelper.ThrowIfFrozen(this); |
||||||
|
allowUnsafeBlocks = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool checkForOverflow; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/Sets whether overflow checking is enabled.
|
||||||
|
/// The default is <c>false</c>. This setting effects semantic analysis.
|
||||||
|
/// </summary>
|
||||||
|
public bool CheckForOverflow { |
||||||
|
get { return checkForOverflow; } |
||||||
|
set { checkForOverflow = value; } |
||||||
|
} |
||||||
|
|
||||||
|
Version languageVersion = new Version((int)Mono.CSharp.LanguageVersion.Default, 0); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/Sets the language version used by the parser.
|
||||||
|
/// Using language constructs newer than the supplied version will result in parser errors.
|
||||||
|
/// </summary>
|
||||||
|
public Version LanguageVersion { |
||||||
|
get { return languageVersion; } |
||||||
|
set { |
||||||
|
FreezableHelper.ThrowIfFrozen(this); |
||||||
|
if (value == null) |
||||||
|
throw new ArgumentNullException(); |
||||||
|
languageVersion = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
IList<string> conditionalSymbols = new List<string>(); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/Sets the list of conditional symbols that are defined project-wide.
|
||||||
|
/// </summary>
|
||||||
|
public IList<string> ConditionalSymbols { |
||||||
|
get { return conditionalSymbols; } |
||||||
|
} |
||||||
|
|
||||||
|
bool treatWarningsAsErrors; |
||||||
|
|
||||||
|
public bool TreatWarningsAsErrors { |
||||||
|
get { return treatWarningsAsErrors; } |
||||||
|
set { |
||||||
|
FreezableHelper.ThrowIfFrozen(this); |
||||||
|
treatWarningsAsErrors = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
IList<int> specificWarningsAsErrors = new List<int>(); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows treating specific warnings as errors without setting <see cref="TreatWarningsAsErrors"/> to true.
|
||||||
|
/// </summary>
|
||||||
|
public IList<int> SpecificWarningsAsErrors { |
||||||
|
get { return specificWarningsAsErrors; } |
||||||
|
} |
||||||
|
|
||||||
|
int warningLevel = 4; |
||||||
|
|
||||||
|
public int WarningLevel { |
||||||
|
get { return warningLevel; } |
||||||
|
set { |
||||||
|
FreezableHelper.ThrowIfFrozen(this); |
||||||
|
warningLevel = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
IList<int> disabledWarnings = new List<int>(); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows treating specific warnings as errors without setting <see cref="TreatWarningsAsErrors"/> to true.
|
||||||
|
/// </summary>
|
||||||
|
public IList<int> 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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,80 @@ |
|||||||
|
/* |
||||||
|
* Created by SharpDevelop. |
||||||
|
* User: Daniel |
||||||
|
* Date: 6/22/2012 |
||||||
|
* Time: 17:08 |
||||||
|
* |
||||||
|
* To change this template use Tools | Options | Coding | Edit Standard Headers. |
||||||
|
*/ |
||||||
|
namespace ICSharpCode.NRefactory.Demo |
||||||
|
{ |
||||||
|
partial class SemanticTreeDialog |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Designer variable used to keep track of non-visual components.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes resources used by the form.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) |
||||||
|
{ |
||||||
|
if (disposing) { |
||||||
|
if (components != null) { |
||||||
|
components.Dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
base.Dispose(disposing); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method is required for Windows Forms designer support.
|
||||||
|
/// Do not change the method contents inside the source code editor. The Forms designer might
|
||||||
|
/// not be able to load this method if it was changed manually.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() |
||||||
|
{ |
||||||
|
this.cancelButton = new System.Windows.Forms.Button(); |
||||||
|
this.treeView = new System.Windows.Forms.TreeView(); |
||||||
|
this.SuspendLayout(); |
||||||
|
//
|
||||||
|
// cancelButton
|
||||||
|
//
|
||||||
|
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); |
||||||
|
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; |
||||||
|
this.cancelButton.Location = new System.Drawing.Point(197, 236); |
||||||
|
this.cancelButton.Name = "cancelButton"; |
||||||
|
this.cancelButton.Size = new System.Drawing.Size(75, 23); |
||||||
|
this.cancelButton.TabIndex = 0; |
||||||
|
this.cancelButton.Text = "Close"; |
||||||
|
this.cancelButton.UseVisualStyleBackColor = true; |
||||||
|
//
|
||||||
|
// treeView
|
||||||
|
//
|
||||||
|
this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) |
||||||
|
| System.Windows.Forms.AnchorStyles.Left) |
||||||
|
| System.Windows.Forms.AnchorStyles.Right))); |
||||||
|
this.treeView.Location = new System.Drawing.Point(12, 12); |
||||||
|
this.treeView.Name = "treeView"; |
||||||
|
this.treeView.ShowRootLines = false; |
||||||
|
this.treeView.Size = new System.Drawing.Size(260, 218); |
||||||
|
this.treeView.TabIndex = 1; |
||||||
|
//
|
||||||
|
// SemanticTreeDialog
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |
||||||
|
this.CancelButton = this.cancelButton; |
||||||
|
this.ClientSize = new System.Drawing.Size(284, 262); |
||||||
|
this.Controls.Add(this.treeView); |
||||||
|
this.Controls.Add(this.cancelButton); |
||||||
|
this.Name = "SemanticTreeDialog"; |
||||||
|
this.Text = "Semantic Tree"; |
||||||
|
this.ResumeLayout(false); |
||||||
|
} |
||||||
|
private System.Windows.Forms.TreeView treeView; |
||||||
|
private System.Windows.Forms.Button cancelButton; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,92 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections; |
||||||
|
using System.Drawing; |
||||||
|
using System.Linq; |
||||||
|
using System.Reflection; |
||||||
|
using System.Text; |
||||||
|
using System.Windows.Forms; |
||||||
|
using ICSharpCode.NRefactory.Semantics; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Demo |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Description of SemanticTreeDialog.
|
||||||
|
/// </summary>
|
||||||
|
public partial class SemanticTreeDialog : Form |
||||||
|
{ |
||||||
|
public SemanticTreeDialog(ResolveResult rr) |
||||||
|
{ |
||||||
|
//
|
||||||
|
// The InitializeComponent() call is required for Windows Forms designer support.
|
||||||
|
//
|
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
var rootNode = MakeObjectNode("Resolve() = ", rr); |
||||||
|
rootNode.Expand(); |
||||||
|
treeView.Nodes.Add(rootNode); |
||||||
|
} |
||||||
|
|
||||||
|
TreeNode MakeObjectNode(string prefix, object obj) |
||||||
|
{ |
||||||
|
if (obj == null) |
||||||
|
return new TreeNode(prefix + "null"); |
||||||
|
if (obj is ResolveResult) { |
||||||
|
TreeNode t = new TreeNode(prefix + obj.GetType().Name); |
||||||
|
t.Tag = obj; |
||||||
|
foreach (PropertyInfo p in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { |
||||||
|
if (p.Name == "ConstantValue" && !((ResolveResult)obj).IsCompileTimeConstant) |
||||||
|
continue; |
||||||
|
TreeNode child = MakePropertyNode(p.Name, p.PropertyType, p.GetValue(obj, null)); |
||||||
|
if (child != null) |
||||||
|
t.Nodes.Add(child); |
||||||
|
} |
||||||
|
foreach (FieldInfo p in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)) { |
||||||
|
TreeNode child = MakePropertyNode(p.Name, p.FieldType, p.GetValue(obj)); |
||||||
|
if (child != null) |
||||||
|
t.Nodes.Add(child); |
||||||
|
} |
||||||
|
return t; |
||||||
|
} else { |
||||||
|
return new TreeNode(prefix + obj.ToString()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TreeNode MakePropertyNode(string propertyName, Type propertyType, object propertyValue) |
||||||
|
{ |
||||||
|
if (propertyName == "IsError" && (propertyValue as bool?) == false) |
||||||
|
return null; |
||||||
|
if (propertyName == "IsCompileTimeConstant" && (propertyValue as bool?) == false) |
||||||
|
return null; |
||||||
|
if (propertyValue == null) { |
||||||
|
return new TreeNode(propertyName + " = null"); |
||||||
|
} |
||||||
|
if (propertyType.GetInterface("System.Collections.IEnumerable") != null && propertyType != typeof(string)) { |
||||||
|
var collection = ((IEnumerable)propertyValue).Cast<object>().ToList(); |
||||||
|
var node = new TreeNode(propertyName + " = Collection with " + collection.Count + " elements"); |
||||||
|
foreach (object element in collection) { |
||||||
|
node.Nodes.Add(MakeObjectNode("", element)); |
||||||
|
} |
||||||
|
return node; |
||||||
|
} |
||||||
|
return MakeObjectNode(propertyName + " = ", propertyValue); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,120 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<root> |
||||||
|
<!-- |
||||||
|
Microsoft ResX Schema |
||||||
|
|
||||||
|
Version 2.0 |
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format |
||||||
|
that is mostly human readable. The generation and parsing of the |
||||||
|
various data types are done through the TypeConverter classes |
||||||
|
associated with the data types. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
... ado.net/XML headers & schema ... |
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader> |
||||||
|
<resheader name="version">2.0</resheader> |
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> |
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> |
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> |
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> |
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> |
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value> |
||||||
|
</data> |
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> |
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> |
||||||
|
<comment>This is a comment</comment> |
||||||
|
</data> |
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple |
||||||
|
name/value pairs. |
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a |
||||||
|
type or mimetype. Type corresponds to a .NET class that support |
||||||
|
text/value conversion through the TypeConverter architecture. |
||||||
|
Classes that don't support this are serialized and stored with the |
||||||
|
mimetype set. |
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the |
||||||
|
ResXResourceReader how to depersist the object. This is currently not |
||||||
|
extensible. For a given mimetype the value must be set accordingly: |
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format |
||||||
|
that the ResXResourceWriter will generate, however the reader can |
||||||
|
read any of the formats listed below. |
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64 |
||||||
|
value : The object must be serialized with |
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter |
||||||
|
: and then encoded with base64 encoding. |
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64 |
||||||
|
value : The object must be serialized with |
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter |
||||||
|
: and then encoded with base64 encoding. |
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64 |
||||||
|
value : The object must be serialized into a byte array |
||||||
|
: using a System.ComponentModel.TypeConverter |
||||||
|
: and then encoded with base64 encoding. |
||||||
|
--> |
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> |
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> |
||||||
|
<xsd:element name="root" msdata:IsDataSet="true"> |
||||||
|
<xsd:complexType> |
||||||
|
<xsd:choice maxOccurs="unbounded"> |
||||||
|
<xsd:element name="metadata"> |
||||||
|
<xsd:complexType> |
||||||
|
<xsd:sequence> |
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" /> |
||||||
|
</xsd:sequence> |
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" /> |
||||||
|
<xsd:attribute name="type" type="xsd:string" /> |
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" /> |
||||||
|
<xsd:attribute ref="xml:space" /> |
||||||
|
</xsd:complexType> |
||||||
|
</xsd:element> |
||||||
|
<xsd:element name="assembly"> |
||||||
|
<xsd:complexType> |
||||||
|
<xsd:attribute name="alias" type="xsd:string" /> |
||||||
|
<xsd:attribute name="name" type="xsd:string" /> |
||||||
|
</xsd:complexType> |
||||||
|
</xsd:element> |
||||||
|
<xsd:element name="data"> |
||||||
|
<xsd:complexType> |
||||||
|
<xsd:sequence> |
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> |
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> |
||||||
|
</xsd:sequence> |
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> |
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> |
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> |
||||||
|
<xsd:attribute ref="xml:space" /> |
||||||
|
</xsd:complexType> |
||||||
|
</xsd:element> |
||||||
|
<xsd:element name="resheader"> |
||||||
|
<xsd:complexType> |
||||||
|
<xsd:sequence> |
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> |
||||||
|
</xsd:sequence> |
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" /> |
||||||
|
</xsd:complexType> |
||||||
|
</xsd:element> |
||||||
|
</xsd:choice> |
||||||
|
</xsd:complexType> |
||||||
|
</xsd:element> |
||||||
|
</xsd:schema> |
||||||
|
<resheader name="resmimetype"> |
||||||
|
<value>text/microsoft-resx</value> |
||||||
|
</resheader> |
||||||
|
<resheader name="version"> |
||||||
|
<value>2.0</value> |
||||||
|
</resheader> |
||||||
|
<resheader name="reader"> |
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
||||||
|
</resheader> |
||||||
|
<resheader name="writer"> |
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
||||||
|
</resheader> |
||||||
|
</root> |
||||||
Loading…
Reference in new issue