Пример #1
0
        public static Task <Document> ReplaceWithInterpolatedStringAsync(
            Document document,
            LiteralExpressionSyntax literalExpression,
            int interpolationStartIndex = -1,
            int interpolationLength     = 0,
            bool addNameOf = false,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            string s = literalExpression.Token.Text;

            var sb = new StringBuilder();

            sb.Append('$');
            sb.Append(StringUtility.DoubleBraces(s.Substring(0, interpolationStartIndex)));
            sb.Append('{');

            if (addNameOf)
            {
                sb.Append(
                    NameOfExpression(
                        StringLiteralParser.Parse(
                            s,
                            interpolationStartIndex,
                            interpolationLength,
                            isVerbatim: s.StartsWith("@"),
                            isInterpolatedText: false)));
            }

            sb.Append('}');
            sb.Append(StringUtility.DoubleBraces(s.Substring(interpolationStartIndex + interpolationLength)));

            ExpressionSyntax newNode = ParseExpression(sb.ToString()).WithTriviaFrom(literalExpression);

            return(document.ReplaceNodeAsync(literalExpression, newNode, cancellationToken));
        }
Пример #2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        /// <remarks>
        ///
        /// See http://www.dmtf.org/sites/default/files/standards/documents/DSP0221_3.0.0a.pdf
        ///
        /// A.17.3 String values
        ///
        /// Unless explicitly specified via ABNF rule WS, no whitespace is allowed between the elements of the rules
        /// in this ABNF section.
        ///
        ///     stringValue   = DOUBLEQUOTE *stringChar DOUBLEQUOTE
        ///                     *( *WS DOUBLEQUOTE *stringChar DOUBLEQUOTE )
        ///     stringChar    = stringUCSchar / stringEscapeSequence
        ///
        /// </remarks>
        private static StringLiteralToken ReadStringLiteralToken(ILexerStream stream)
        {
            // BUGBUG - no support for *( *WS DOUBLEQUOTE *stringChar DOUBLEQUOTE )
            // BUGBUG - incomplete escape sequences
            // BUGBUG - no support for UCS characters
            var sourceChars = new List <SourceChar>();

            // read the first character
            sourceChars.Add(stream.ReadChar('"'));
            // read the remaining characters
            var parser = new StringLiteralParser();

            while (!stream.Eof)
            {
                var peek = stream.Peek();
                if (StringValidator.IsDoubleQuote(peek.Value) && !parser.IsEscaped)
                {
                    parser.ConsumeEos();
                    break;
                }
                else
                {
                    sourceChars.Add(peek);
                    parser.ConsumeChar(stream.Read());
                }
            }
            // read the last character
            sourceChars.Add(stream.ReadChar('"'));
            // process any escape sequences in the string
            var unescaped = parser.OutputString.ToString();
            // return the result
            var extent = new SourceExtent(sourceChars);

            return(new StringLiteralToken(extent, unescaped));
        }
Пример #3
0
        public static Task <Document> ConvertToInterpolatedStringAsync(
            Document document,
            LiteralExpressionSyntax literalExpression,
            int interpolationStartIndex = -1,
            int interpolationLength     = 0,
            bool addNameOf = false,
            CancellationToken cancellationToken = default)
        {
            string s = literalExpression.Token.Text;

            StringBuilder sb = StringBuilderCache.GetInstance();

            sb.Append('$');

            int length = sb.Length;

            sb.Append(s, 0, interpolationStartIndex);
            sb.Replace("{", "{{", length);
            sb.Replace("}", "}}", length);

            sb.Append('{');

            if (addNameOf)
            {
                sb.Append(
                    NameOfExpression(
                        StringLiteralParser.Parse(
                            s,
                            interpolationStartIndex,
                            interpolationLength,
                            isVerbatim: s.StartsWith("@"),
                            isInterpolatedText: false)));
            }

            int closeBracePosition = sb.Length;

            sb.Append('}');

            length = sb.Length;

            int startIndex = interpolationStartIndex + interpolationLength;

            sb.Append(s, startIndex, s.Length - startIndex);

            sb.Replace("{", "{{", length);
            sb.Replace("}", "}}", length);

            ExpressionSyntax newNode = ParseExpression(StringBuilderCache.GetStringAndFree(sb));

            SyntaxToken closeBrace = newNode.FindToken(closeBracePosition);

            newNode = newNode
                      .ReplaceToken(closeBrace, closeBrace.WithNavigationAnnotation())
                      .WithTriviaFrom(literalExpression);

            return(document.ReplaceNodeAsync(literalExpression, newNode, cancellationToken));
        }
        public void cannot_parse_newlines_within_literal(char delimiter, string newLine)
        {
            var result = StringLiteralParser.Parser(new Input(delimiter + "hello" + newLine + "world" + delimiter));

            Assert.False(result.WasSuccessful);
            Assert.Equal("unexpected end of line", result.Message);
            Assert.Equal(1, result.Expectations.Count());
            Assert.Equal("continued string contents or " + delimiter, result.Expectations.ElementAt(0));
        }
        public void cannot_parse_non_terminated_string(char delimiter, string input)
        {
            var result = StringLiteralParser.Parser(new Input(delimiter + input));

            Assert.False(result.WasSuccessful);
            Assert.Equal("unexpected end of input", result.Message);
            Assert.Equal(1, result.Expectations.Count());
            Assert.Equal("continued string contents or " + delimiter, result.Expectations.ElementAt(0));
        }
        public void cannot_parse_non_delimitered_string(string input)
        {
            var result = StringLiteralParser.Parser(new Input(input));

            Assert.False(result.WasSuccessful);
            Assert.Equal("unexpected '" + input[0] + "'", result.Message);
            Assert.Equal(2, result.Expectations.Count());
            Assert.Equal("\"", result.Expectations.ElementAt(0));
            Assert.Equal("'", result.Expectations.ElementAt(1));
        }
        public void cannot_parse_empty_input()
        {
            var result = StringLiteralParser.Parser(new Input(""));

            Assert.False(result.WasSuccessful);
            Assert.Equal("unexpected end of input", result.Message);
            Assert.Equal(2, result.Expectations.Count());
            Assert.Equal("string delimited by \"", result.Expectations.ElementAt(0));
            Assert.Equal("string delimited by '", result.Expectations.ElementAt(1));
        }
Пример #8
0
        public static Task <Document> RefactorAsync(
            Document document,
            InterpolatedStringExpressionSyntax interpolatedString,
            TextSpan span,
            bool addNameOf = false,
            CancellationToken cancellationToken = default)
        {
            string s = interpolatedString.ToString();

            int startIndex = span.Start - interpolatedString.SpanStart;

            var sb = new StringBuilder();

            sb.Append(s, 0, startIndex);
            sb.Append('{');

            if (addNameOf)
            {
                string identifier = StringLiteralParser.Parse(
                    s,
                    startIndex,
                    span.Length,
                    isVerbatim: interpolatedString.IsVerbatim(),
                    isInterpolatedText: true);

                sb.Append("nameof(");
                sb.Append(identifier);
                sb.Append(")");
            }

            int closeBracePosition = sb.Length;

            sb.Append('}');

            startIndex += span.Length;
            sb.Append(s, startIndex, s.Length - startIndex);

            ExpressionSyntax newNode = ParseExpression(sb.ToString());

            SyntaxToken closeBrace = newNode.FindToken(closeBracePosition);

            newNode = newNode
                      .ReplaceToken(closeBrace, closeBrace.WithNavigationAnnotation())
                      .WithTriviaFrom(interpolatedString);

            return(document.ReplaceNodeAsync(interpolatedString, newNode, cancellationToken));
        }
Пример #9
0
        IEnumerable <Iterator> Parse(string expression)
        {
            var builder = new StringBuilder();

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(expression)))
            {
                using (var reader = new StreamReader(stream))
                {
                    while (!reader.EndOfStream)
                    {
                        var idx = (char)reader.Read();
                        if (idx == -1)
                        {
                            break;
                        }

                        if (idx == '/')
                        {
                            yield return(new Iterator(builder.ToString()));

                            builder.Clear();
                        }
                        else if (idx == '"' && builder.Length == 0)
                        {
                            // String literal iterator.
                            yield return(new Iterator(StringLiteralParser.ReadQuotedString(reader)));
                        }
                        else
                        {
                            builder.Append(idx);
                        }
                    }
                }
            }

            if (builder.Length == 0)
            {
                throw new ApplicationException("Syntax error at end of expression");
            }

            yield return(new Iterator(builder.ToString()));
        }
Пример #10
0
        private static int GetStartIndex(LiteralExpressionSyntax literalExpression, TextSpan span)
        {
            int index = span.Start - literalExpression.Span.Start;

            string text = literalExpression.Token.Text;

            if (text.StartsWith("@", StringComparison.Ordinal))
            {
                if (index > 1 &&
                    StringLiteralParser.CanExtractSpan(text, 2, text.Length - 3, span.Offset(-literalExpression.Span.Start), isVerbatim: true, isInterpolatedText: false))
                {
                    return(index);
                }
            }
            else if (index > 0 &&
                     StringLiteralParser.CanExtractSpan(text, 1, text.Length - 2, span.Offset(-literalExpression.SpanStart), isVerbatim: false, isInterpolatedText: false))
            {
                return(index);
            }

            return(-1);
        }
        public static Task <Document> RefactorAsync(
            Document document,
            InterpolatedStringExpressionSyntax interpolatedString,
            TextSpan span,
            bool addNameOf = false,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            string s = interpolatedString.ToString();

            int startIndex = span.Start - interpolatedString.SpanStart;

            var sb = new StringBuilder();

            sb.Append(s, 0, startIndex);
            sb.Append('{');

            if (addNameOf)
            {
                sb.Append(
                    CSharpFactory.NameOfExpression(
                        StringLiteralParser.Parse(
                            s,
                            startIndex,
                            span.Length,
                            isVerbatim: interpolatedString.IsVerbatim(),
                            isInterpolatedText: true)));
            }

            sb.Append('}');

            startIndex += span.Length;
            sb.Append(s, startIndex, s.Length - startIndex);

            ExpressionSyntax newNode = ParseExpression(sb.ToString()).WithTriviaFrom(interpolatedString);

            return(document.ReplaceNodeAsync(interpolatedString, newNode, cancellationToken));
        }
Пример #12
0
        private static int GetStartIndex(StringLiteralExpressionInfo info, TextSpan span)
        {
            int spanStart = info.Expression.SpanStart;

            int index = span.Start - spanStart;

            string text = info.Text;

            if (info.IsVerbatim)
            {
                if (index > 1 &&
                    StringLiteralParser.CanExtractSpan(text, 2, text.Length - 3, span.Offset(-spanStart), isVerbatim: true, isInterpolatedText: false))
                {
                    return(index);
                }
            }
            else if (index > 0 &&
                     StringLiteralParser.CanExtractSpan(text, 1, text.Length - 2, span.Offset(-spanStart), isVerbatim: false, isInterpolatedText: false))
            {
                return(index);
            }

            return(-1);
        }
Пример #13
0
        public static async Task ComputeRefactoringsAsync(RefactoringContext context, LiteralExpressionSyntax literalExpression)
        {
            if (context.IsRefactoringEnabled(RefactoringIdentifiers.InsertStringInterpolation) &&
                context.SupportsCSharp6 &&
                context.Span.End < literalExpression.Span.End)
            {
                int startIndex = GetStartIndex(literalExpression, context.Span);

                if (startIndex != -1)
                {
                    context.RegisterRefactoring(
                        "Insert interpolation",
                        cancellationToken =>
                    {
                        return(ReplaceWithInterpolatedStringAsync(
                                   context.Document,
                                   literalExpression,
                                   startIndex,
                                   context.Span.Length,
                                   addNameOf: false,
                                   cancellationToken: cancellationToken));
                    });

                    if (!context.Span.IsEmpty)
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        string name = StringLiteralParser.Parse(literalExpression.Token.Text, startIndex, context.Span.Length, literalExpression.IsVerbatimStringLiteral(), isInterpolatedText: false);

                        foreach (ISymbol symbol in semanticModel.LookupSymbols(literalExpression.SpanStart))
                        {
                            if (string.Equals(name, symbol.MetadataName, StringComparison.Ordinal))
                            {
                                context.RegisterRefactoring(
                                    "Insert interpolation with nameof",
                                    cancellationToken =>
                                {
                                    return(ReplaceWithInterpolatedStringAsync(
                                               context.Document,
                                               literalExpression,
                                               startIndex,
                                               context.Span.Length,
                                               addNameOf: true,
                                               cancellationToken: cancellationToken));
                                });

                                break;
                            }
                        }
                    }
                }
            }

            if (context.Span.IsBetweenSpans(literalExpression))
            {
                string text = literalExpression.GetStringLiteralInnerText();

                if (literalExpression.IsVerbatimStringLiteral())
                {
                    if (text.Contains("\"\""))
                    {
                        if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiteral))
                        {
                            context.RegisterRefactoring(
                                "Replace verbatim string literal with regular string literal",
                                cancellationToken =>
                            {
                                return(ReplaceWithRegularStringLiteralAsync(
                                           context.Document,
                                           literalExpression,
                                           cancellationToken));
                            });
                        }

                        if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiterals) &&
                            text.Contains("\n"))
                        {
                            context.RegisterRefactoring(
                                "Replace verbatim string literal with regular string literals",
                                cancellationToken =>
                            {
                                return(ReplaceWithRegularStringLiteralsAsync(
                                           context.Document,
                                           literalExpression,
                                           cancellationToken));
                            });
                        }
                    }
                }
                else if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceRegularStringLiteralWithVerbatimStringLiteral) &&
                         text.Contains(@"\"))
                {
                    context.RegisterRefactoring(
                        "Replace regular string literal with verbatim string literal",
                        cancellationToken =>
                    {
                        return(ReplaceWithVerbatimStringLiteralAsync(
                                   context.Document,
                                   literalExpression,
                                   cancellationToken));
                    });
                }
            }

            if (context.IsRefactoringEnabled(RefactoringIdentifiers.UseStringEmptyInsteadOfEmptyStringLiteral) &&
                CanReplaceWithStringEmpty(literalExpression))
            {
                context.RegisterRefactoring(
                    "Replace \"\" with 'string.Empty'",
                    cancellationToken =>
                {
                    return(ReplaceWithStringEmptyAsync(
                               context.Document,
                               literalExpression,
                               cancellationToken));
                });
            }
        }
        public static async Task ComputeRefactoringsAsync(RefactoringContext context, InterpolatedStringTextSyntax interpolatedStringText)
        {
            if (context.IsRefactoringEnabled(RefactoringIdentifiers.InsertStringInterpolation) &&
                interpolatedStringText.IsParentKind(SyntaxKind.InterpolatedStringExpression))
            {
                TextSpan span = context.Span;
                var      interpolatedString = (InterpolatedStringExpressionSyntax)interpolatedStringText.Parent;
                string   text       = interpolatedStringText.TextToken.Text;
                bool     isVerbatim = interpolatedString.IsVerbatim();

                if (StringLiteralParser.CanExtractSpan(
                        text,
                        span.Offset(-interpolatedStringText.SpanStart),
                        isVerbatim,
                        isInterpolatedText: true))
                {
                    context.RegisterRefactoring(
                        "Insert interpolation",
                        cancellationToken =>
                    {
                        return(InsertInterpolationRefactoring.RefactorAsync(
                                   context.Document,
                                   interpolatedString,
                                   span,
                                   addNameOf: false,
                                   cancellationToken: cancellationToken));
                    });

                    if (!span.IsEmpty)
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        string name = StringLiteralParser.Parse(
                            text,
                            span.Start - interpolatedStringText.SpanStart,
                            span.Length,
                            isVerbatim: isVerbatim,
                            isInterpolatedText: true);

                        foreach (ISymbol symbol in semanticModel.LookupSymbols(interpolatedStringText.SpanStart))
                        {
                            if (string.Equals(name, symbol.MetadataName, StringComparison.Ordinal))
                            {
                                context.RegisterRefactoring(
                                    "Insert interpolation with nameof",
                                    cancellationToken =>
                                {
                                    return(InsertInterpolationRefactoring.RefactorAsync(
                                               context.Document,
                                               interpolatedString,
                                               span,
                                               addNameOf: true,
                                               cancellationToken: cancellationToken));
                                });

                                break;
                            }
                        }
                    }
                }
            }
        }
Пример #15
0
        public IEnumerable <string> GetTokens()
        {
            var builder = new StringBuilder();

            while (!_reader.EndOfStream)
            {
                var current = (char)_reader.Peek();
                switch (current)
                {
                case ':':
                    if (builder.Length > 0)
                    {
                        yield return(builder.ToString());

                        builder.Clear();
                    }
                    _reader.Read();     // Discarding ':'.
                    yield return(":");

                    break;

                case '@':
                    if (builder.Length > 0)
                    {
                        builder.Append(current);
                        _reader.Read();
                    }
                    else
                    {
                        _reader.Read();     // Discarding '@'.
                        var next = (char)_reader.Read();
                        if (next == '"')
                        {
                            yield return(StringLiteralParser.ReadMultiLineString(_reader));

                            builder.Clear();
                        }
                        else
                        {
                            builder.Append('@');
                            builder.Append(next);
                        }
                    }
                    break;

                case '"':
                case '\'':
                    if (builder.Length > 0)
                    {
                        builder.Append(current);
                        _reader.Read();
                    }
                    else
                    {
                        yield return(StringLiteralParser.ReadQuotedString(_reader));

                        builder.Clear();
                    }
                    break;

                case '\r':
                    if (builder.Length > 0)
                    {
                        yield return(builder.ToString());

                        builder.Clear();
                    }
                    _reader.Read();     // Discarding '\r'.
                    if (_reader.EndOfStream)
                    {
                        throw new ApplicationException("CR/LF error close to EOF");
                    }
                    var lf = (char)_reader.Read();
                    if (lf != '\n')
                    {
                        throw new ApplicationException("CR/LF error in Hyperlambda");
                    }
                    yield return("\r\n");

                    break;

                case '\n':
                    if (builder.Length > 0)
                    {
                        yield return(builder.ToString());

                        builder.Clear();
                    }
                    _reader.Read();     // Discarding '\n'.
                    yield return("\r\n");

                    break;

                case '/':
                    if (builder.Length > 0)
                    {
                        _reader.Read();     // Discarding '/' character.
                        builder.Append('/');
                    }
                    else
                    {
                        _reader.Read();     // Discarding current '/'.
                        if (_reader.Peek() == '/')
                        {
                            // Eating the rest of the line.
                            while (!_reader.EndOfStream && (char)_reader.Peek() != '\n')
                            {
                                _reader.Read();
                            }
                        }
                        else if (_reader.Peek() == '*')
                        {
                            // Eating until "*/".
                            var seenEndOfComment = false;
                            while (!_reader.EndOfStream && !seenEndOfComment)
                            {
                                var idxComment = _reader.Read();
                                if (idxComment == '*')
                                {
                                    // Checking if we're at end of comment.
                                    if (_reader.Peek() == '/')
                                    {
                                        _reader.Read();
                                        seenEndOfComment = true;
                                    }
                                }
                            }
                            if (!seenEndOfComment && _reader.EndOfStream)
                            {
                                throw new ApplicationException("Syntax error in comment close to end of Hyperlambda");
                            }
                        }
                        else
                        {
                            builder.Append(current);     // Only a part of the current token.
                        }
                    }
                    break;

                case ' ':
                    _reader.Read();     // Discarding current ' '.
                    if (builder.Length > 0)
                    {
                        builder.Append(current);
                    }
                    else
                    {
                        builder.Append(' ');
                        while (!_reader.EndOfStream && (char)_reader.Peek() == ' ')
                        {
                            _reader.Read();
                            builder.Append(' ');
                        }
                        if (!_reader.EndOfStream && builder.Length % 3 != 0)
                        {
                            throw new ApplicationException("Odd number of spaces found in Hyperlambda file");
                        }
                        yield return(builder.ToString());

                        builder.Clear();
                    }
                    break;

                default:
                    builder.Append(current);
                    _reader.Read();
                    break;
                }
            }

            // Returning the last token, if any.
            if (builder.Length > 0)
            {
                yield return(builder.ToString());
            }
        }
Пример #16
0
        public static async Task ComputeRefactoringsAsync(RefactoringContext context, LiteralExpressionSyntax literalExpression)
        {
            StringLiteralExpressionInfo info = SyntaxInfo.StringLiteralExpressionInfo(literalExpression);

            Debug.Assert(info.Success);

            if (!info.Success)
            {
                return;
            }

            if (context.IsRefactoringEnabled(RefactoringIdentifiers.InsertStringInterpolation) &&
                context.SupportsCSharp6 &&
                context.Span.End < literalExpression.Span.End &&
                !CSharpUtility.IsPartOfExpressionThatMustBeConstant(literalExpression))
            {
                int startIndex = GetStartIndex(info, context.Span);

                if (startIndex != -1)
                {
                    context.RegisterRefactoring(
                        "Insert interpolation",
                        cancellationToken =>
                    {
                        return(ReplaceWithInterpolatedStringAsync(
                                   context.Document,
                                   literalExpression,
                                   startIndex,
                                   context.Span.Length,
                                   addNameOf: false,
                                   cancellationToken: cancellationToken));
                    },
                        RefactoringIdentifiers.InsertStringInterpolation);

                    if (!context.Span.IsEmpty)
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        string name = StringLiteralParser.Parse(literalExpression.Token.Text, startIndex, context.Span.Length, info.IsVerbatim, isInterpolatedText: false);

                        foreach (ISymbol symbol in semanticModel.LookupSymbols(literalExpression.SpanStart))
                        {
                            if (string.Equals(name, symbol.MetadataName, StringComparison.Ordinal))
                            {
                                context.RegisterRefactoring(
                                    "Insert interpolation with nameof",
                                    cancellationToken =>
                                {
                                    return(ReplaceWithInterpolatedStringAsync(
                                               context.Document,
                                               literalExpression,
                                               startIndex,
                                               context.Span.Length,
                                               addNameOf: true,
                                               cancellationToken: cancellationToken));
                                },
                                    EquivalenceKey.Join(RefactoringIdentifiers.InsertStringInterpolation, "WithNameOf"));

                                break;
                            }
                        }
                    }
                }
            }

            if (context.Span.IsBetweenSpans(literalExpression))
            {
                if (info.IsVerbatim)
                {
                    if (info.ContainsEscapeSequence)
                    {
                        if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiteral))
                        {
                            context.RegisterRefactoring(
                                "Replace verbatim string literal with regular string literal",
                                ct => ReplaceWithRegularStringLiteralAsync(context.Document, literalExpression, ct),
                                RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiteral);
                        }

                        if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiterals) &&
                            info.ContainsLinefeed)
                        {
                            context.RegisterRefactoring(
                                "Replace verbatim string literal with regular string literals",
                                ct => ReplaceWithRegularStringLiteralsAsync(context.Document, literalExpression, ct),
                                RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiterals);
                        }
                    }
                }
                else if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceRegularStringLiteralWithVerbatimStringLiteral) &&
                         info.ContainsEscapeSequence)
                {
                    context.RegisterRefactoring(
                        "Replace regular string literal with verbatim string literal",
                        ct => ReplaceWithVerbatimStringLiteralAsync(context.Document, literalExpression, ct),
                        RefactoringIdentifiers.ReplaceRegularStringLiteralWithVerbatimStringLiteral);
                }
            }

            if (context.IsRefactoringEnabled(RefactoringIdentifiers.UseStringEmptyInsteadOfEmptyStringLiteral) &&
                CanReplaceWithStringEmpty(literalExpression))
            {
                context.RegisterRefactoring(
                    "Replace \"\" with 'string.Empty'",
                    ct => ReplaceWithStringEmptyAsync(context.Document, literalExpression, ct),
                    RefactoringIdentifiers.UseStringEmptyInsteadOfEmptyStringLiteral);
            }
        }