Bug Report T470377
Visible to All Users

Refactorings - The "Inline Method" refactoring produces non-compilable code or changes the behavior when a method to be inlined uses both a ternary operator and a return value in an expression

created 8 years ago (modified 8 years ago)

Given this initial code

C#
class Program { const string DEV_API_KEY = "Development Key", DEV_API_SECRET = "Development Secret"; const string PROD_API_KEY = "Production Key", PROD_API_SECRET = "Production Secret"; static string ApiKey(bool a_isDev) { return a_isDev ? DEV_API_KEY : PROD_API_KEY; } static string ApiSecret(bool a_isDev) { return a_isDev ? DEV_API_SECRET : PROD_API_SECRET; } static void GetOAuthToken(string a_hostname, bool a_isDev) { var request = WebRequest.Create(a_hostname + "/oauth/token"); request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String($"{ApiKey(a_isDev)}:{ApiSecret(a_isDev)}".Select(ch => (byte)ch).ToArray())); //... } }

Select the "Inline Method" or "Inline Method (and delete)" refactoring on the call to ApiKey in the second line of GetOAuthToken. That line is converted to:

C#
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String($"{a_isDev ? DEV_API_KEY : PROD_API_KEY}:{ApiSecret(a_isDev)}".Select(ch => (byte)ch).ToArray()));

This is not valid C# code; the ternary operator needs to be enclosed in parentheses when inside a string interpolation.
The same incorrect inlining appears when I trigger it from the method definition, or on the ApiSecret method.

UPDATE

A thematically similar issue but functionally much worse issue can occur when the caller uses the return value in an expression. In this case, the code may compile correctly, but have different behavior than the original. Given this initial code:

C#
class Program { static int Foo(bool a_useFive) { return a_useFive ? 5 : 10; } static void Main() { System.Console.WriteLine(Foo(true) + " items"); System.Console.WriteLine(Foo(true) * 2); } }

Use the "Inline Method (and delete)" code action on Foo, and the body of Main will get changed to:

C#
System.Console.WriteLine(true ? 5 : 10 + " items"); System.Console.WriteLine(true ? 5 : 10 * 2);

The first line no longer compiles, and the second line now outputs "5" instead of "10".

The same issue can be seen with any other expression where the precedence change from "function call" to "ternary" causes the operator in the caller to bind to the third argument to the ternary instead of to the result of the ternary.

Comments (3)
DevExpress Support Team 8 years ago

    Hi Dale,
    Thank you for informing us about this problem and providing us with such a detailed explanation.
    I was able reproduce this.

    Please give us some time. We will prepare corrections and inform you about that.

      There are also problems if the parameter in the caller is an suitably-crafted expression. Add

      C#
      static bool GetSomething(); static bool GetSomethingElse();

      and call:

      C#
      bool a; System.Console.WriteLine(Foo(a ? GetSomething() : GetSomethingElse()));

      The inlining operation yields this, which again doesn't compile:

      C#
      System.Console.WriteLine(a ? GetSomething() : GetSomethingElse() ? 5 : 10);
      DevExpress Support Team 8 years ago

        Hi Dale,

        Thank you for providing us with the additional test scenario.
        We'll examine it and prepare the correction.

        Answers approved by DevExpress Support

        created 8 years ago

        We have fixed the issue described in this ticket and will include the fix in our next maintenance update. To apply this solution before the official update, request a hotfix by clicking the corresponding link for product versions you require.

        Note: Hotfixes may be unavailable for beta versions and updates that are about to be released.

          Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

          Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.