20 changed files with 635 additions and 216 deletions
@ -0,0 +1,112 @@ |
|||||||
|
// 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<List<AstNode>> nodes = new Stack<List<AstNode>>(); |
||||||
|
List<AstNode> currentList; |
||||||
|
readonly ILocatable locationProvider; |
||||||
|
|
||||||
|
public InsertMissingTokensDecorator(TokenWriter writer, ILocatable locationProvider) |
||||||
|
: base(writer) |
||||||
|
{ |
||||||
|
this.locationProvider = locationProvider; |
||||||
|
currentList = new List<AstNode>(); |
||||||
|
} |
||||||
|
|
||||||
|
public override void StartNode(AstNode node) |
||||||
|
{ |
||||||
|
currentList.Add(node); |
||||||
|
nodes.Push(currentList); |
||||||
|
currentList = new List<AstNode>(); |
||||||
|
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); |
||||||
|
currentList.Add(t); |
||||||
|
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; |
||||||
|
} |
||||||
|
if (t != null) currentList.Add(t); |
||||||
|
base.WriteKeyword(role, keyword); |
||||||
|
} |
||||||
|
|
||||||
|
public override void WriteIdentifier(Identifier identifier) |
||||||
|
{ |
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Text; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
using NUnit.Framework; |
||||||
|
using ICSharpCode.NRefactory.CSharp.Parser; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Description of InsertMissingTokensDecoratorTests.
|
||||||
|
/// </summary>
|
||||||
|
[TestFixture] |
||||||
|
public class InsertMissingTokensDecoratorTests |
||||||
|
{ |
||||||
|
string[] fileNames; |
||||||
|
|
||||||
|
[TestFixtureSetUp] |
||||||
|
public void SetUp() |
||||||
|
{ |
||||||
|
string path = Path.GetFullPath (Path.Combine ("..", "..")); |
||||||
|
if (!File.Exists(Path.Combine(path, "NRefactory.sln"))) |
||||||
|
throw new InvalidOperationException("Test cannot find the NRefactory source code in " + path); |
||||||
|
fileNames = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories); |
||||||
|
} |
||||||
|
|
||||||
|
static void RemoveTokens(AstNode node) |
||||||
|
{ |
||||||
|
foreach (var child in node.Descendants) { |
||||||
|
if (child is CSharpTokenNode && !(child is CSharpModifierToken)) |
||||||
|
child.Remove(); |
||||||
|
else if (child.NodeType == NodeType.Whitespace) |
||||||
|
child.Remove(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void AssertOutput(AstNode node) |
||||||
|
{ |
||||||
|
RemoveTokens(node); |
||||||
|
StringWriter w = new StringWriter(); |
||||||
|
w.NewLine = "\n"; |
||||||
|
node.AcceptVisitor(new CSharpOutputVisitor(TokenWriter.CreateWriterThatSetsLocationsInAST(w), FormattingOptionsFactory.CreateSharpDevelop())); |
||||||
|
var doc = new ReadOnlyDocument(w.ToString()); |
||||||
|
ConsistencyChecker.CheckMissingTokens(node, "test.cs", doc); |
||||||
|
ConsistencyChecker.CheckPositionConsistency(node, "test.cs", doc); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void SimpleClass() |
||||||
|
{ |
||||||
|
var code = @"class Test
|
||||||
|
{ |
||||||
|
} |
||||||
|
";
|
||||||
|
var unit = SyntaxTree.Parse(code); |
||||||
|
AssertOutput(unit); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void SimpleMethod() |
||||||
|
{ |
||||||
|
var code = @"class Test
|
||||||
|
{ |
||||||
|
void A () |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
";
|
||||||
|
var unit = SyntaxTree.Parse(code); |
||||||
|
AssertOutput(unit); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void SelfTest() |
||||||
|
{ |
||||||
|
foreach (var file in fileNames) { |
||||||
|
Console.WriteLine("processing {0}...", file); |
||||||
|
var node = SyntaxTree.Parse(File.ReadAllText(file), file); |
||||||
|
AssertOutput(node); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.IO; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
using NUnit.Framework; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp.Parser |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Provides utilities to check whether positions and/or tokens in an AST are valid.
|
||||||
|
/// </summary>
|
||||||
|
public static class ConsistencyChecker |
||||||
|
{ |
||||||
|
static void PrintNode (AstNode node) |
||||||
|
{ |
||||||
|
Console.WriteLine ("Parent:" + node.GetType ()); |
||||||
|
Console.WriteLine ("Children:"); |
||||||
|
foreach (var c in node.Children) |
||||||
|
Console.WriteLine (c.GetType () +" at:"+ c.StartLocation +"-"+ c.EndLocation + " Role: "+ c.Role); |
||||||
|
Console.WriteLine ("----"); |
||||||
|
} |
||||||
|
|
||||||
|
public static void CheckPositionConsistency (AstNode node, string currentFileName, IDocument currentDocument = null) |
||||||
|
{ |
||||||
|
if (currentDocument == null) |
||||||
|
currentDocument = new ReadOnlyDocument(File.ReadAllText(currentFileName)); |
||||||
|
string comment = "(" + node.GetType ().Name + " at " + node.StartLocation + " in " + currentFileName + ")"; |
||||||
|
var pred = node.StartLocation <= node.EndLocation; |
||||||
|
if (!pred) |
||||||
|
PrintNode (node); |
||||||
|
Assert.IsTrue(pred, "StartLocation must be before EndLocation " + comment); |
||||||
|
var prevNodeEnd = node.StartLocation; |
||||||
|
var prevNode = node; |
||||||
|
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
||||||
|
bool assertion = child.StartLocation >= prevNodeEnd; |
||||||
|
if (!assertion) { |
||||||
|
PrintNode (prevNode); |
||||||
|
PrintNode (node); |
||||||
|
} |
||||||
|
Assert.IsTrue(assertion, currentFileName + ": Child " + child.GetType () +" (" + child.StartLocation + ")" +" must start after previous sibling " + prevNode.GetType () + "(" + prevNode.StartLocation + ")"); |
||||||
|
CheckPositionConsistency(child, currentFileName, currentDocument); |
||||||
|
prevNodeEnd = child.EndLocation; |
||||||
|
prevNode = child; |
||||||
|
} |
||||||
|
Assert.IsTrue(prevNodeEnd <= node.EndLocation, "Last child must end before parent node ends " + comment); |
||||||
|
} |
||||||
|
|
||||||
|
public static void CheckMissingTokens(AstNode node, string currentFileName, IDocument currentDocument = null) |
||||||
|
{ |
||||||
|
if (currentDocument == null) |
||||||
|
currentDocument = new ReadOnlyDocument(File.ReadAllText(currentFileName)); |
||||||
|
if (node is CSharpTokenNode) { |
||||||
|
Assert.IsNull(node.FirstChild, "Token nodes should not have children"); |
||||||
|
} else { |
||||||
|
var prevNodeEnd = node.StartLocation; |
||||||
|
var prevNode = node; |
||||||
|
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
||||||
|
CheckWhitespace(prevNode, prevNodeEnd, child, child.StartLocation, currentFileName, currentDocument); |
||||||
|
CheckMissingTokens(child, currentFileName, currentDocument); |
||||||
|
prevNode = child; |
||||||
|
prevNodeEnd = child.EndLocation; |
||||||
|
} |
||||||
|
CheckWhitespace(prevNode, prevNodeEnd, node, node.EndLocation, currentFileName, currentDocument); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void CheckWhitespace(AstNode startNode, TextLocation whitespaceStart, AstNode endNode, TextLocation whitespaceEnd, string currentFileName, IDocument currentDocument) |
||||||
|
{ |
||||||
|
Assert.Greater(whitespaceStart.Line, 0); |
||||||
|
Assert.Greater(whitespaceStart.Column, 0); |
||||||
|
Assert.Greater(whitespaceEnd.Line, 0); |
||||||
|
Assert.Greater(whitespaceEnd.Column, 0); |
||||||
|
if (whitespaceStart == whitespaceEnd || startNode == endNode) |
||||||
|
return; |
||||||
|
int start = currentDocument.GetOffset(whitespaceStart.Line, whitespaceStart.Column); |
||||||
|
int end = currentDocument.GetOffset(whitespaceEnd.Line, whitespaceEnd.Column); |
||||||
|
string text = currentDocument.GetText(start, end - start); |
||||||
|
bool assertion = string.IsNullOrWhiteSpace(text); |
||||||
|
if (!assertion) { |
||||||
|
if (startNode.Parent != endNode.Parent) |
||||||
|
PrintNode (startNode.Parent); |
||||||
|
PrintNode (endNode.Parent); |
||||||
|
} |
||||||
|
Assert.IsTrue(assertion, "Expected whitespace between " + startNode.GetType () +":" + whitespaceStart + " and " + endNode.GetType () + ":" + whitespaceEnd |
||||||
|
+ ", but got '" + text + "' (in " + currentFileName + " parent:" + startNode.Parent.GetType () +")"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue