Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

#region preprocessor directives not converted #15

Closed
ViacheslavUKR opened this issue Jan 17, 2018 · 18 comments
Closed

#region preprocessor directives not converted #15

ViacheslavUKR opened this issue Jan 17, 2018 · 18 comments
Assignees
Labels
C# -> VB Specific to C# -> VB conversion help wanted output logic error A bug where the converted output behaves differently to the input code VB -> C# Specific to VB -> C# conversion

Comments

@ViacheslavUKR
Copy link

ViacheslavUKR commented Jan 17, 2018

Sorry, my friend, this is very interesting project to me, because I write in rare lang VB.NET, but any call your converter induce error below. This is input C# code for call this issue ( from wordwide C# example "contoso university").

#define Final // or Intro

#if Intro
#region snippet_Intro
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}
#endregion

#elif Final
#region snippet_Final
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Display(Name = "Number")]
        public int CourseID { get; set; }

        [StringLength(50, MinimumLength = 3)]
        public string Title { get; set; }

        [Range(0, 5)]
        public int Credits { get; set; }

        public int DepartmentID { get; set; }

        public Department Department { get; set; }
        public ICollection<Enrollment> Enrollments { get; set; }
        public ICollection<CourseAssignment> CourseAssignments { get; set; }
    }
}
#endregion
#endif


---------------------------
Microsoft Visual Studio
---------------------------
Convert C# to VB:

Selected C# code seems to have errors or to be incomplete:



----- Exception 1 of 1 -----

System.InvalidOperationException: Nullable object must have a value.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.ConvertAndSplitAttributes(SyntaxList`1 attributeLists, SyntaxList`1& attributes, SyntaxList`1& returnAttributes)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.VisitPropertyDeclaration(PropertyDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.<VisitClassDeclaration>b__22_0(MemberDeclarationSyntax m)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.VisitClassDeclaration(ClassDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.<VisitNamespaceDeclaration>b__20_0(MemberDeclarationSyntax m)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at Microsoft.CodeAnalysis.SyntaxList`1.CreateNode(IEnumerable`1 nodes)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.<VisitCompilationUnit>b__14_1(MemberDeclarationSyntax m)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at Microsoft.CodeAnalysis.SyntaxList`1.CreateNode(IEnumerable`1 nodes)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.VisitCompilationUnit(CompilationUnitSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.Convert(CSharpSyntaxNode input, SemanticModel semanticModel, Document targetDocument)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.ConvertText(String text, MetadataReference[] references)


---------------------------
OK   
---------------------------

Also I start your project in source code and see a code line number with error. Please see below.

Error message:
----- Exception 1 of 1 -----

System.InvalidOperationException: Nullable object must have a value.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Nullable`1.get_Value()
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.ConvertAndSplitAttributes(SyntaxList`1 attributeLists, SyntaxList`1& attributes, SyntaxList`1& returnAttributes) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\NodesVisitor.cs:line 523
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.VisitPropertyDeclaration(PropertyDeclarationSyntax node) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\NodesVisitor.cs:line 431
   at Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.<VisitClassDeclaration>b__22_0(MemberDeclarationSyntax m) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\NodesVisitor.cs:line 206
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.VisitClassDeclaration(ClassDeclarationSyntax node) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\NodesVisitor.cs:line 206
   at Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.<VisitNamespaceDeclaration>b__20_0(MemberDeclarationSyntax m) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\NodesVisitor.cs:line 180
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at Microsoft.CodeAnalysis.SyntaxList`1.CreateNode(IEnumerable`1 nodes)
   at Microsoft.CodeAnalysis.SyntaxList`1..ctor(IEnumerable`1 nodes)
   at Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.List[TNode](IEnumerable`1 nodes)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\NodesVisitor.cs:line 182
   at Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.<VisitCompilationUnit>b__14_1(MemberDeclarationSyntax m) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\NodesVisitor.cs:line 118
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at Microsoft.CodeAnalysis.SyntaxList`1.CreateNode(IEnumerable`1 nodes)
   at Microsoft.CodeAnalysis.SyntaxList`1..ctor(IEnumerable`1 nodes)
   at Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.List[TNode](IEnumerable`1 nodes)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.NodesVisitor.VisitCompilationUnit(CompilationUnitSyntax node) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\NodesVisitor.cs:line 118
   at Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax.Accept[TResult](CSharpSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.VB.CSharpConverter.Convert(CSharpSyntaxNode input, SemanticModel semanticModel, Document targetDocument) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\CSharpConverter.cs:line 33
   at ICSharpCode.CodeConverter.VB.CSharpConverter.ConvertText(String text, MetadataReference[] references) in F:\Projects\CodeConverter-master\ICSharpCode.CodeConverter\VB\CSharpConverter.cs:line 46
@ViacheslavUKR
Copy link
Author

ViacheslavUKR commented Jan 18, 2018

I add one string of code

if (attrList.Target != null) {
	if (SyntaxTokenExtensions.IsKind((SyntaxToken)attrList.Target?.Identifier, CS.SyntaxKind.ReturnKeyword) == true)
		retAttr.Add((AttributeListSyntax)attrList.Accept(this));
	else
		attr.Add((AttributeListSyntax)attrList.Accept(this));
}

But all this project has built in crazy and insecure style. I'm don't understand this style of programming

If (Obj1.Obj2.Ob3.Obj4.Property = bla-bla-bla) than ... bla-bla-bla

are you have understanding that in any moment Obj1 may equal null, or Obj2 in any moment may equal null ... hmmm .... VB-programmer usually use only one variable in one operator, and in top of each sub or function before doing sensitive function need to check all parameters to correct value. And if possible need don't use expression future. This is my style, and I usually working as freelancer and I see thousands of VB-project. I think this is main stream of style of VB-programmer - firstly check any variable on top of function and after that use less as possible variables in one code string. Sorry, my friend, for this description, but I really don't understand your style of programming.

and second problem in shallow view - its support not all C# and VB syntax - for example directive #define this project fully ignore.

Very sorry.

@GrahamTheCoder
Copy link
Member

Thank you for including example code and error message, that's very helpful!

You are correct that some language features like this are still unsupported. Over time (and with the help of the community) we hope to cover much more.

If you intend to work on implementing #define#, be aware of this issue where me and someone else are currently changing how Trivia is handled, so will touch similar areas: #8 (comment)

The code style makes frequent use of the reasonably new operator ?. which you may want to read about if you aren't familiar https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators
This allows a more concise version of the pattern you've described.

@GrahamTheCoder GrahamTheCoder changed the title Nullable object must have a value. Preprocessor directives not supported and some attributes cause an exception Jan 20, 2018
@GrahamTheCoder GrahamTheCoder mentioned this issue Jan 20, 2018
3 tasks
@GrahamTheCoder GrahamTheCoder added the output logic error A bug where the converted output behaves differently to the input code label Mar 11, 2018
@GrahamTheCoder GrahamTheCoder changed the title Preprocessor directives not supported and some attributes cause an exception Preprocessor directives not supported Mar 12, 2018
@GrahamTheCoder
Copy link
Member

GrahamTheCoder commented Mar 12, 2018

As of 5.6.1, the attribute issue is fixed. Preprocessor directives are still not supported so I'll leave this issue open to track that. It may be extremely difficult to correctly convert preprocessor directives in cases such as this: #8 (comment)
However, support for #if statments that only contain whole nodes should be reasonably easy and cater for a lot of real-world cases.

@GrahamTheCoder
Copy link
Member

GrahamTheCoder commented Mar 19, 2018

#if statments that only contain whole nodes should be reasonably easy and cater for a lot of real-world cases.

After looking more closely at what's available in Roslyn, even this looks to be a challenge.
Exactly because of the complex cases where individual tokens are added/removed, the preprocessor directives are resolved at parse time. This means that multiple passes will be needed in order to capture all the possible parsings - 1 pass for each combination of values of constants used in preprocessor directives. This could obviously get expensive very quickly...
All these passes will also need to be pieced together back into a single document afterwards which could take some significant restructuring of code.

If Roslyn will deal with a bit of ambiguity it might yield acceptable results for simple cases, to just discover all constants, and set them all to true, which I'll try out some time, but anything more is definitely going to be punted way down the line.

There are some other preprocessor directives such as enabling/disabling warnings that should be much easier to convert.

This was referenced Mar 1, 2019
@GrahamTheCoder GrahamTheCoder changed the title Preprocessor directives not supported Preprocessor directives (#region, #if, #pragma) not converted Mar 6, 2019
@GrahamTheCoder GrahamTheCoder self-assigned this Jan 25, 2020
@kathleenwest
Copy link

I am also having an issue converted from C# to VB with the #region and #endregion not working.

@GrahamTheCoder
Copy link
Member

@kathleenwest Thanks for reminder, I actually need to update some of the details of this issue.
After your comment I've just investigated and discovered an issue in C# -> VB where the region start is in the wrong place if it was the first statement in the file, e.g:

Doesn't convert correctly:

#region MyClass definition  
public class MyClass
{  
    static void Main()
    {  
    }  
}  
#endregion  

Does convert correctly:

using System; 
#region MyClass definition  
public class MyClass
{  
    static void Main()
    {  
    }  
}  
#endregion  

If you're seeing a similar issue, perhaps this will help you work around the issue until there's a fix. Otherwise, could you post a bit more detail on the input or issue with the output? Thanks!

@kathleenwest
Copy link

Also, I learned that VB does not allow the similar #region #endregion inside the methods.. so that probably needs to be ignored by the code converter and commented out if it exists in C#.

@GrahamTheCoder
Copy link
Member

Ah I wasn't aware of that difference, thanks!

@rlktradewright
Copy link

@GrahamTheCoder

Can you update us on where you are with this (VB -> C#), as I use #Region constructs frequently in my VB codebase, and putting them all back in manually would be a significant pain.

As an interim measure, I think it would be helpful for these 'trivia' (!) to output the original directive along with the error message in the target code. At least then it would be easier to reconstruct the desired directive manually, without having to do a side-by-side comparison with the original.

@kathleenwest
Copy link

@rlktradewright The best work around I discovered so far is to just comment out the #region directives in C# before converting to VB. I also learned that VB language rules does not allow #region directives inside methods, only outside... so that may be a reason why the conversion fails.

@rlktradewright
Copy link

@kathleenwest

Thanks for that, though it's the opposite direction I'm concerned with VB to C#.

But you're right: I should just comment out all the #Region and #End Region directives in the VB and then use a single regular expression search and replace to convert them in the C#. Since I can do that for an entire solution with one edit, it's really not a problem. Still, it would nice not to have to do this....

@chtenb
Copy link

chtenb commented Feb 11, 2021

Would it help to include some of these regex hacks in this tool?

Stuff like

replac "(?i)#if debug then" "'XXX#if DEBUG" -- \*.vb
replac "(?i)#end if" "'XXX#endif" -- \*.vb 
replac "(?i)#region" "'XXX#region" -- \*.vb 
replac "(?i)#end region" "'XXX#endregion" -- \*.vb 


# Do roslyn conversion

replac '//XXX#' '#'

@GrahamTheCoder
Copy link
Member

Yeah, good thinking, maybe a partial workaround would be a quick win. I'll see what I can do to move this forward in some way.

@GrahamTheCoder
Copy link
Member

@kathleenwest I've just tried region statements within a VB method block and they appear to be valid now. Some sources online suggest that this works from VS2015 onwards, though I couldn't find a language specification from those versions to confirm.

However, this doesn't mean the languages are equivalent. C# actually allows regions around basically any tokens.
Valid c# example:

        Console
        #region Just a dot
            .
        #endregion
            WriteLine(
        #region Spoilers abound
            "Hello World!")
        #endregion
            ;

^ I hope no-one ever writes code that looks like this!

@kathleenwest
Copy link

@GrahamTheCoder I wouldn't be surprised if that works in newer versions of VS and not in older versions. My employer (now former) was making me do their C# and VB code in an older licensed version of VS and an older compiler. 😩

@GrahamTheCoder
Copy link
Member

@kathleenwest oh dear, congratulations on the upgrade! ☺

@GrahamTheCoder GrahamTheCoder added VB -> C# Specific to VB -> C# conversion C# -> VB Specific to C# -> VB conversion labels Apr 4, 2022
@nabrezzelt
Copy link

Hey, anything new here? ☺

@GrahamTheCoder GrahamTheCoder changed the title Preprocessor directives (#region, #if, #pragma) not converted Preprocessor directives (#if, #pragma) not converted Oct 11, 2022
@GrahamTheCoder GrahamTheCoder changed the title Preprocessor directives (#if, #pragma) not converted #region preprocessor directives not converted Oct 11, 2022
@GrahamTheCoder
Copy link
Member

GrahamTheCoder commented Oct 11, 2022

Previously there was a general base piece of work this issue required to allow any of these to be worked on. That's done, but the specific cases still need work on top of that.

Region directives now work in many common cases. They were the main focus of chat in this issue, so I'll close it. They will likely always be slightly imperfect and need a bit of post conversion tidying but I think it's best to open bugs for specific cases with those now.

#Pragma directives have had no attention, I've added them to known issues since they're rarely mentioned and complex enough not to be worth the effort in general, but there may be a couple of common cases worth covering in a dedicated PR. Or a wiki page with a few regexes.

#if statements' contents still aren't converted. For C# to VB I've added this to known issues since it's very unlikely anyone will ever tackle that. For VB to C#, I've reopened #863 to track it

Please follow #863 or open an appropriate issue if there's a specific case you think is important. Thanks!

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
C# -> VB Specific to C# -> VB conversion help wanted output logic error A bug where the converted output behaves differently to the input code VB -> C# Specific to VB -> C# conversion
Projects
None yet
Development

No branches or pull requests

6 participants