roslyn icon indicating copy to clipboard operation
roslyn copied to clipboard

Inclusion of using is put inside unrelated preprocessor directive

Open jnyrup opened this issue 8 years ago • 6 comments

Version Used: VS 20017 Version 15.1 26403.7

Steps to Reproduce:

  1. Start with this program
#if DEBUG
using System.IO;
#endif

namespace ConsoleApplication2
{
    class Foo
    {
        public void Bar()
        {
            Console.WriteLine();
        }
    }
}

  1. Ctrl+. on Console.WriteLine();
  2. Select "Using System"
  3. Observe how using System; is put inside #if DEBUG, result:
#if DEBUG
using System;
using System.IO;
#endif

namespace ConsoleApplication2
{
    class Foo
    {
        public void Bar()
        {
            Console.WriteLine();
        }
    }
}

Expected Behavior: using System; is put outside preprocessor directive.

Actual Behavior: using System; is put inside preprocessor directive making it incompilable in Release Mode.

jnyrup avatar May 05 '17 13:05 jnyrup

💭 I would expect the feature to place the new directive inside a logically-equivalent guard. For example, if you were to start with the following code I would expect the result to behave as you observed:

#if DEBUG
using System.IO;
#endif

namespace ConsoleApplication2
{
    class Foo
    {
        public void Bar()
        {
#if DEBUG
            Console.WriteLine(); // Ctrl+. on this line
#endif
        }
    }
}

Conceptually this isn't unreasonable, but there are a few cases that make it challenging:

  1. Nested directives
  2. Boolean equivalence (e.g. #if !!DEBUG is equivalent to #if DEBUG, but has a different form)

sharwell avatar May 05 '17 15:05 sharwell

PRs welcome.

CyrusNajmabadi avatar May 05 '17 18:05 CyrusNajmabadi

I would expect the feature to place the new directive inside a logically-equivalent guard.

Currently we have no APIs to answer that question. From the perspective of everything consuming the SyntaxTree code there is only the code that active and the code that is disabled. We could certainly try to support this, but it would need a fair amount of plumbing/infrastructure to support. Given that we haven't heard about this issue in 15+ years, it would not be a high pri for me :)

CyrusNajmabadi avatar May 05 '17 19:05 CyrusNajmabadi

I would expect the feature to place the new directive inside a logically-equivalent guard.

Unfortunately there might not be a single guard that covers it.

#if FOO
using System.IO;
#elif BAR
using System.IO;
#endif

namespace ConsoleApplication2
{
    class Preprocessor2
    {
        public void Foo()
        {
#if FOO || BAR
            Console.WriteLine();
#endif
        }
    }
}

Given that we haven't heard about this issue in 15+ years...

I've had the issue two or three times recently in a class where I've used preprocessor directives to switch between alias.

#if DEBUG
    using Dev___DataMember = System.Runtime.Serialization.DataMemberAttribute;
#else
    using Dev___DataMember = System.Runtime.Serialization.IgnoreDataMemberAttribute;
#endif

There are properly cleaner ways to archive this, but it was a minor annoyance when my VS is in debug mode but the TFS server compiles in release mode and fails to build.

jnyrup avatar May 05 '17 20:05 jnyrup

Unfortunately there might not be a single guard that covers it.

True, but one could be added. Again in theory. 😄

sharwell avatar May 05 '17 21:05 sharwell

Would take a small community fix here.

CyrusNajmabadi avatar Oct 18 '24 21:10 CyrusNajmabadi

Not planning on doing this.

CyrusNajmabadi avatar Sep 22 '25 11:09 CyrusNajmabadi