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

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

@ -1,4 +1,4 @@
// //
// PreProcessorDirective.cs // PreProcessorDirective.cs
// //
// Author: // Author:
@ -64,6 +64,9 @@ namespace ICSharpCode.NRefactory.CSharp
set; set;
} }
/// <summary>
/// For an '#if' directive, specifies whether the condition evaluated to true.
/// </summary>
public bool Take { public bool Take {
get; get;
set; set;
@ -107,7 +110,7 @@ namespace ICSharpCode.NRefactory.CSharp
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{ {
PreProcessorDirective o = other as PreProcessorDirective; 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
void WriteSpecials (AstNode start, AstNode end) void WriteSpecials (AstNode start, AstNode end)
{ {
for (AstNode pos = start; pos != end; pos = pos.NextSibling) { 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); pos.AcceptVisitor (this, null);
} }
} }
@ -153,13 +153,22 @@ namespace ICSharpCode.NRefactory.CSharp
/// node with the specified role. Advances the current position. /// node with the specified role. Advances the current position.
/// </summary> /// </summary>
void WriteSpecialsUpToRole (Role role) void WriteSpecialsUpToRole (Role role)
{
WriteSpecialsUpToRole (role, null);
}
void WriteSpecialsUpToRole (Role role, AstNode nextNode)
{ {
if (positionStack.Count == 0) if (positionStack.Count == 0)
return; 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) { if (pos.Role == role) {
WriteSpecials (positionStack.Pop (), pos); 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; break;
} }
} }
@ -176,21 +185,10 @@ namespace ICSharpCode.NRefactory.CSharp
for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) { for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) {
if (pos == node) { if (pos == node) {
WriteSpecials (positionStack.Pop (), pos); WriteSpecials (positionStack.Pop (), pos);
positionStack.Push (pos); // Push the next sibling because the node itself is not a special,
break; // and should be considered to be already handled.
} positionStack.Push (pos.NextSibling);
} // This is necessary for OptionalComma() to work correctly.
}
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);
break; break;
} }
} }
@ -212,6 +210,23 @@ namespace ICSharpCode.NRefactory.CSharp
Space (!noSpaceAfterComma && policy.SpaceAfterBracketComma); // TODO: Comma policy has changed. 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) void WriteCommaSeparatedList (IEnumerable<AstNode> list)
{ {
bool isFirst = true; bool isFirst = true;
@ -1375,22 +1390,22 @@ namespace ICSharpCode.NRefactory.CSharp
WriteModifiers (typeDeclaration.ModifierTokens); WriteModifiers (typeDeclaration.ModifierTokens);
BraceStyle braceStyle; BraceStyle braceStyle;
switch (typeDeclaration.ClassType) { switch (typeDeclaration.ClassType) {
case ClassType.Enum: case ClassType.Enum:
WriteKeyword ("enum"); WriteKeyword ("enum");
braceStyle = policy.EnumBraceStyle; braceStyle = policy.EnumBraceStyle;
break; break;
case ClassType.Interface: case ClassType.Interface:
WriteKeyword ("interface"); WriteKeyword ("interface");
braceStyle = policy.InterfaceBraceStyle; braceStyle = policy.InterfaceBraceStyle;
break; break;
case ClassType.Struct: case ClassType.Struct:
WriteKeyword ("struct"); WriteKeyword ("struct");
braceStyle = policy.StructBraceStyle; braceStyle = policy.StructBraceStyle;
break; break;
default: default:
WriteKeyword ("class"); WriteKeyword ("class");
braceStyle = policy.ClassBraceStyle; braceStyle = policy.ClassBraceStyle;
break; break;
} }
WriteIdentifier (typeDeclaration.Name); WriteIdentifier (typeDeclaration.Name);
WriteTypeParameters (typeDeclaration.TypeParameters); WriteTypeParameters (typeDeclaration.TypeParameters);
@ -1406,9 +1421,6 @@ namespace ICSharpCode.NRefactory.CSharp
OpenBrace (braceStyle); OpenBrace (braceStyle);
if (typeDeclaration.ClassType == ClassType.Enum) { if (typeDeclaration.ClassType == ClassType.Enum) {
bool first = true; bool first = true;
if (!typeDeclaration.LBraceToken.IsNull) {
}
foreach (var member in typeDeclaration.Members) { foreach (var member in typeDeclaration.Members) {
if (first) { if (first) {
first = false; first = false;
@ -1418,6 +1430,7 @@ namespace ICSharpCode.NRefactory.CSharp
} }
member.AcceptVisitor (this, data); member.AcceptVisitor (this, data);
} }
OptionalComma();
NewLine (); NewLine ();
} else { } else {
foreach (var member in typeDeclaration.Members) { foreach (var member in typeDeclaration.Members) {
@ -2263,9 +2276,9 @@ namespace ICSharpCode.NRefactory.CSharp
// "1.0 / /*comment*/a", then we need to insert a space in front of the comment. // "1.0 / /*comment*/a", then we need to insert a space in front of the comment.
formatter.Space (); formatter.Space ();
} }
formatter.StartNode( comment); formatter.StartNode(comment);
formatter.WriteComment (comment.CommentType, comment.Content); formatter.WriteComment (comment.CommentType, comment.Content);
formatter.EndNode( comment); formatter.EndNode(comment);
lastWritten = LastWritten.Whitespace; lastWritten = LastWritten.Whitespace;
return null; return null;
} }
@ -2278,7 +2291,12 @@ namespace ICSharpCode.NRefactory.CSharp
formatter.Space (); formatter.Space ();
} }
formatter.StartNode (preProcessorDirective); 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); formatter.EndNode (preProcessorDirective);
lastWritten = LastWritten.Whitespace; lastWritten = LastWritten.Whitespace;
return null; return null;

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

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

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

@ -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
} }
}}); }});
} }
[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 @@
<Compile Include="CSharp\Parser\Expression\ObjectCreateExpressionTests.cs" /> <Compile Include="CSharp\Parser\Expression\ObjectCreateExpressionTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\DelegateDeclarationTests.cs" /> <Compile Include="CSharp\Parser\GeneralScope\DelegateDeclarationTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\NamespaceDeclarationTests.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\TypeDeclarationTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\UsingDeclarationTests.cs" /> <Compile Include="CSharp\Parser\GeneralScope\UsingDeclarationTests.cs" />
<Compile Include="CSharp\Parser\ParseSelfTests.cs" /> <Compile Include="CSharp\Parser\ParseSelfTests.cs" />

Loading…
Cancel
Save