Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Prefix identifier with '@' if necessary #943

Merged
merged 3 commits into from
Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Do not simplify default expression if it would change semantics ([RCS1244](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1244.md)) ([#939](https://github.com/josefpihrt/roslynator/pull/939).
- Fix NullReferenceException in [RCS1198](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1198.md) ([#940](https://github.com/josefpihrt/roslynator/pull/940).
- Order named arguments even if optional arguments are not specified [RCS1205](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1205.md) ([#941](https://github.com/josefpihrt/roslynator/pull/941).
- Prefix identifier with `@` if necessary ([RCS1220](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1220.md)) ([#943](https://github.com/josefpihrt/roslynator/pull/943).

-----
<!-- Content below does not adhere to 'Keep a Changelog' format -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ private static (IsPatternExpressionSyntax isPatternExpression, TNode newNode) Ge

string name = NameGenerator.CreateName(typeSymbol, firstCharToLower: true) ?? DefaultNames.Variable;

name = NameGenerator.Default.EnsureUniqueLocalName(name, semanticModel, node.SpanStart, cancellationToken: cancellationToken) ?? DefaultNames.Variable;
name = CSharpNameGenerator.Default.EnsureUniqueLocalName(name, semanticModel, node.SpanStart, cancellationToken: cancellationToken) ?? DefaultNames.Variable;

IsPatternExpressionSyntax isPatternExpression = IsPatternExpression(
isInfo.Expression,
Expand Down
161 changes: 161 additions & 0 deletions src/CSharp/CSharp/CSharpNameGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace Roslynator.CSharp
{
//TODO: make public
/// <summary>
/// Provides methods to obtain an unique C# identifier.
/// </summary>
internal class CSharpNameGenerator
{
private static readonly NameGenerator _generator = new NumberSuffixCSharpNameGenerator();

/// <summary>
/// Default implementation of <see cref="CSharpNameGenerator"/> that adds number suffix to ensure uniqueness.
/// </summary>
public static CSharpNameGenerator Default { get; } = new();

/// <summary>
/// Returns a name that will be unique at the specified position.
/// </summary>
/// <param name="baseName"></param>
/// <param name="semanticModel"></param>
/// <param name="position"></param>
public string EnsureUniqueName(string baseName, SemanticModel semanticModel, int position)
{
return _generator.EnsureUniqueName(baseName, semanticModel, position);
}

/// <summary>
/// Returns unique enum member name for a specified enum type.
/// </summary>
/// <param name="baseName"></param>
/// <param name="enumType"></param>
public string EnsureUniqueEnumMemberName(string baseName, INamedTypeSymbol enumType)
{
return _generator.EnsureUniqueEnumMemberName(baseName, enumType, isCaseSensitive: true);
}

/// <summary>
/// Return a local name that will be unique at the specified position.
/// </summary>
/// <param name="baseName"></param>
/// <param name="semanticModel"></param>
/// <param name="position"></param>
/// <param name="cancellationToken"></param>
public string EnsureUniqueLocalName(
string baseName,
SemanticModel semanticModel,
int position,
CancellationToken cancellationToken = default)
{
return _generator.EnsureUniqueLocalName(baseName, semanticModel, position, isCaseSensitive: true, cancellationToken);
}

/// <summary>
/// Return a local names that will be unique at the specified position.
/// </summary>
/// <param name="baseName"></param>
/// <param name="semanticModel"></param>
/// <param name="position"></param>
/// <param name="count"></param>
/// <param name="cancellationToken"></param>
public ImmutableArray<string> EnsureUniqueLocalNames(
string baseName,
SemanticModel semanticModel,
int position,
int count,
CancellationToken cancellationToken = default)
{
return _generator.EnsureUniqueLocalNames(baseName, semanticModel, position, count, isCaseSensitive: true, cancellationToken);
}

/// <summary>
/// Return a parameter name that will be unique at the specified position.
/// </summary>
/// <param name="baseName"></param>
/// <param name="containingSymbol"></param>
/// <param name="semanticModel"></param>
/// <param name="cancellationToken"></param>
public string EnsureUniqueParameterName(
string baseName,
ISymbol containingSymbol,
SemanticModel semanticModel,
CancellationToken cancellationToken = default)
{
return _generator.EnsureUniqueParameterName(baseName, containingSymbol, semanticModel, isCaseSensitive: true, cancellationToken);
}

internal string CreateUniqueLocalName(
ITypeSymbol typeSymbol,
SemanticModel semanticModel,
int position,
CancellationToken cancellationToken = default)
{
return _generator.CreateUniqueLocalName(typeSymbol, semanticModel, position, isCaseSensitive: true, cancellationToken);
}

internal string CreateUniqueLocalName(
ITypeSymbol typeSymbol,
string oldName,
SemanticModel semanticModel,
int position,
CancellationToken cancellationToken = default)
{
return _generator.CreateUniqueLocalName(typeSymbol, oldName, semanticModel, position, isCaseSensitive: true, cancellationToken);
}

internal string CreateUniqueParameterName(
string oldName,
IParameterSymbol parameterSymbol,
SemanticModel semanticModel,
CancellationToken cancellationToken = default)
{
return _generator.CreateUniqueParameterName(oldName, parameterSymbol, semanticModel, isCaseSensitive: true, cancellationToken);
}

private class NumberSuffixCSharpNameGenerator : NameGenerator
{
public override string EnsureUniqueName(string baseName, IEnumerable<string> reservedNames, bool isCaseSensitive = true)
{
int suffix = 1;

string name = baseName;

while (!IsUniqueName(name, reservedNames, isCaseSensitive))
{
suffix++;
name = baseName + suffix.ToString();
}

return CheckKeyword(name);
}

public override string EnsureUniqueName(string baseName, ImmutableArray<ISymbol> symbols, bool isCaseSensitive = true)
{
int suffix = 1;

string name = baseName;

while (!IsUniqueName(name, symbols, isCaseSensitive))
{
suffix++;
name = baseName + suffix.ToString();
}

return CheckKeyword(name);
}

private string CheckKeyword(string name)
{
return (SyntaxFacts.GetKeywordKind(name) != SyntaxKind.None) ? "@" + name : name;
}
}
}
}
Loading