Browse Source

Fixed roundtripping of preprocessor directives.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
4db74d9da0
  1. 18
      ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs
  2. 7
      ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs
  3. 98
      ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs
  4. 5
      ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs
  5. 68
      ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/PreprocessorDirectiveTests.cs
  6. 9
      ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/TypeDeclarationTests.cs
  7. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

18
ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs

@ -26,10 +26,24 @@ @@ -26,10 +26,24 @@
namespace ICSharpCode.NRefactory.CSharp
{
public enum CommentType {
public enum CommentType
{
/// <summary>
/// "//" comment
/// </summary>
SingleLine,
/// <summary>
/// "/* */" comment
/// </summary>
MultiLine,
Documentation
/// <summary>
/// "///" comment
/// </summary>
Documentation,
/// <summary>
/// Inactive code (code in non-taken "#if")
/// </summary>
InactiveCode
}
public class Comment : AstNode, IRelocatable

7
ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
//
//
// PreProcessorDirective.cs
//
// Author:
@ -64,6 +64,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -64,6 +64,9 @@ namespace ICSharpCode.NRefactory.CSharp
set;
}
/// <summary>
/// For an '#if' directive, specifies whether the condition evaluated to true.
/// </summary>
public bool Take {
get;
set;
@ -107,7 +110,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -107,7 +110,7 @@ namespace ICSharpCode.NRefactory.CSharp
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
PreProcessorDirective o = other as PreProcessorDirective;
return o != null && Type == o.Type;
return o != null && Type == o.Type && MatchString(Argument, o.Argument);
}
}
}

98
ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -142,7 +142,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -142,7 +142,7 @@ namespace ICSharpCode.NRefactory.CSharp
void WriteSpecials (AstNode start, AstNode end)
{
for (AstNode pos = start; pos != end; pos = pos.NextSibling) {
if (pos.Role == AstNode.Roles.Comment) {
if (pos.Role == AstNode.Roles.Comment || pos.Role == AstNode.Roles.PreProcessorDirective) {
pos.AcceptVisitor (this, null);
}
}
@ -153,13 +153,22 @@ namespace ICSharpCode.NRefactory.CSharp @@ -153,13 +153,22 @@ namespace ICSharpCode.NRefactory.CSharp
/// node with the specified role. Advances the current position.
/// </summary>
void WriteSpecialsUpToRole (Role role)
{
WriteSpecialsUpToRole (role, null);
}
void WriteSpecialsUpToRole (Role role, AstNode nextNode)
{
if (positionStack.Count == 0)
return;
for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) {
// 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);
positionStack.Push (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;
}
}
@ -176,21 +185,10 @@ namespace ICSharpCode.NRefactory.CSharp @@ -176,21 +185,10 @@ namespace ICSharpCode.NRefactory.CSharp
for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) {
if (pos == node) {
WriteSpecials (positionStack.Pop (), pos);
positionStack.Push (pos);
break;
}
}
}
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 == AstNode.Roles.Comma) {
WriteSpecials (positionStack.Pop (), pos);
positionStack.Push (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;
}
}
@ -212,6 +210,23 @@ namespace ICSharpCode.NRefactory.CSharp @@ -212,6 +210,23 @@ namespace ICSharpCode.NRefactory.CSharp
Space (!noSpaceAfterComma && policy.SpaceAfterBracketComma); // TODO: Comma policy has changed.
}
/// <summary>
/// Writes an optional comma, e.g. at the end of an enum declaration
/// </summary>
void OptionalComma()
{
// Look for the role between the current position and the nextNode.
for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) {
if (pos.Role == AstNode.Roles.Comma) {
Comma(null, noSpaceAfterComma: true);
break;
} else if (pos.NodeType != NodeType.Whitespace) {
// only skip over whitespace and comma nodes
break;
}
}
}
void WriteCommaSeparatedList (IEnumerable<AstNode> list)
{
bool isFirst = true;
@ -1375,22 +1390,22 @@ namespace ICSharpCode.NRefactory.CSharp @@ -1375,22 +1390,22 @@ namespace ICSharpCode.NRefactory.CSharp
WriteModifiers (typeDeclaration.ModifierTokens);
BraceStyle braceStyle;
switch (typeDeclaration.ClassType) {
case ClassType.Enum:
WriteKeyword ("enum");
braceStyle = policy.EnumBraceStyle;
break;
case ClassType.Interface:
WriteKeyword ("interface");
braceStyle = policy.InterfaceBraceStyle;
break;
case ClassType.Struct:
WriteKeyword ("struct");
braceStyle = policy.StructBraceStyle;
break;
default:
WriteKeyword ("class");
braceStyle = policy.ClassBraceStyle;
break;
case ClassType.Enum:
WriteKeyword ("enum");
braceStyle = policy.EnumBraceStyle;
break;
case ClassType.Interface:
WriteKeyword ("interface");
braceStyle = policy.InterfaceBraceStyle;
break;
case ClassType.Struct:
WriteKeyword ("struct");
braceStyle = policy.StructBraceStyle;
break;
default:
WriteKeyword ("class");
braceStyle = policy.ClassBraceStyle;
break;
}
WriteIdentifier (typeDeclaration.Name);
WriteTypeParameters (typeDeclaration.TypeParameters);
@ -1406,9 +1421,6 @@ namespace ICSharpCode.NRefactory.CSharp @@ -1406,9 +1421,6 @@ namespace ICSharpCode.NRefactory.CSharp
OpenBrace (braceStyle);
if (typeDeclaration.ClassType == ClassType.Enum) {
bool first = true;
if (!typeDeclaration.LBraceToken.IsNull) {
}
foreach (var member in typeDeclaration.Members) {
if (first) {
first = false;
@ -1418,6 +1430,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -1418,6 +1430,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
member.AcceptVisitor (this, data);
}
OptionalComma();
NewLine ();
} else {
foreach (var member in typeDeclaration.Members) {
@ -2263,9 +2276,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -2263,9 +2276,9 @@ namespace ICSharpCode.NRefactory.CSharp
// "1.0 / /*comment*/a", then we need to insert a space in front of the comment.
formatter.Space ();
}
formatter.StartNode( comment);
formatter.StartNode(comment);
formatter.WriteComment (comment.CommentType, comment.Content);
formatter.EndNode( comment);
formatter.EndNode(comment);
lastWritten = LastWritten.Whitespace;
return null;
}
@ -2278,7 +2291,12 @@ namespace ICSharpCode.NRefactory.CSharp @@ -2278,7 +2291,12 @@ namespace ICSharpCode.NRefactory.CSharp
formatter.Space ();
}
formatter.StartNode (preProcessorDirective);
formatter.WriteIdentifier ("#" + preProcessorDirective.Type.ToString ().ToLower ());
formatter.WriteToken ("#" + preProcessorDirective.Type.ToString ().ToLower ());
if (!string.IsNullOrEmpty(preProcessorDirective.Argument)) {
formatter.Space();
formatter.WriteToken(preProcessorDirective.Argument);
}
formatter.NewLine();
formatter.EndNode (preProcessorDirective);
lastWritten = LastWritten.Whitespace;
return null;

5
ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs

@ -175,6 +175,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -175,6 +175,7 @@ namespace ICSharpCode.NRefactory.CSharp
case CommentType.SingleLine:
textWriter.Write("//");
textWriter.WriteLine(content);
needsIndent = true;
break;
case CommentType.MultiLine:
textWriter.Write("/*");
@ -184,6 +185,10 @@ namespace ICSharpCode.NRefactory.CSharp @@ -184,6 +185,10 @@ namespace ICSharpCode.NRefactory.CSharp
case CommentType.Documentation:
textWriter.Write("///");
textWriter.WriteLine(content);
needsIndent = true;
break;
default:
textWriter.Write(content);
break;
}
}

68
ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/PreprocessorDirectiveTests.cs

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
// 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.Linq;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Parser.GeneralScope
{
[TestFixture]
public class PreprocessorDirectiveTests
{
[Test]
public void InactiveIf()
{
string program = @"namespace NS {
#if SOMETHING
class A {}
#endif
}";
NamespaceDeclaration ns = ParseUtilCSharp.ParseGlobal<NamespaceDeclaration>(program);
Assert.AreEqual(0, ns.Members.Count);
Assert.AreEqual(7, ns.Children.Count());
Assert.AreEqual(AstNode.Roles.Keyword, ns.Children.ElementAt(0).Role);
Assert.AreEqual(AstNode.Roles.Identifier, ns.Children.ElementAt(1).Role);
Assert.AreEqual(AstNode.Roles.LBrace, ns.Children.ElementAt(2).Role);
Assert.AreEqual(AstNode.Roles.PreProcessorDirective, ns.Children.ElementAt(3).Role);
Assert.AreEqual(AstNode.Roles.Comment, ns.Children.ElementAt(4).Role);
Assert.AreEqual(AstNode.Roles.PreProcessorDirective, ns.Children.ElementAt(5).Role);
Assert.AreEqual(AstNode.Roles.RBrace, ns.Children.ElementAt(6).Role);
var pp = ns.GetChildrenByRole(AstNode.Roles.PreProcessorDirective);
Assert.AreEqual(PreProcessorDirectiveType.If, pp.First().Type);
Assert.IsFalse(pp.First().Take);
Assert.AreEqual("SOMETHING", pp.First().Argument);
Assert.AreEqual(new TextLocation(2, 2), pp.First().StartLocation);
Assert.AreEqual(new TextLocation(2, 15), pp.First().EndLocation);
var c = ns.GetChildByRole(AstNode.Roles.Comment);
Assert.AreEqual(CommentType.InactiveCode, c.CommentType);
Assert.AreEqual(new TextLocation(3, 1), c.StartLocation);
Assert.AreEqual(new TextLocation(4, 2), c.EndLocation);
Assert.AreEqual("\tclass A {}\n\t", c.Content.Replace("\r", ""));
Assert.AreEqual(PreProcessorDirectiveType.Endif, pp.Last().Type);
Assert.AreEqual(string.Empty, pp.Last().Argument);
Assert.AreEqual(new TextLocation(4, 2), pp.First().StartLocation);
Assert.AreEqual(new TextLocation(4, 8), pp.First().EndLocation);
}
}
}

9
ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/TypeDeclarationTests.cs

@ -329,5 +329,14 @@ public abstract class MyClass : MyBase, Interface1, My.Test.Interface2 @@ -329,5 +329,14 @@ public abstract class MyClass : MyBase, Interface1, My.Test.Interface2
}
}});
}
[Test]
public void EnumWithCommaAtEnd()
{
TypeDeclaration td = ParseUtilCSharp.ParseGlobal<TypeDeclaration>("enum MyEnum { A, B, }");
Assert.AreEqual(2, td.Members.Count);
Assert.AreEqual(AstNode.Roles.RBrace, td.LastChild.Role);
Assert.AreEqual(AstNode.Roles.Comma, td.LastChild.PrevSibling.Role);
}
}
}

1
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -86,6 +86,7 @@ @@ -86,6 +86,7 @@
<Compile Include="CSharp\Parser\Expression\ObjectCreateExpressionTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\DelegateDeclarationTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\NamespaceDeclarationTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\PreprocessorDirectiveTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\TypeDeclarationTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\UsingDeclarationTests.cs" />
<Compile Include="CSharp\Parser\ParseSelfTests.cs" />

Loading…
Cancel
Save