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

Implement IdentifiersShouldNotContainUnderscoresFixer #3706

Merged
merged 4 commits into from
May 21, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;

namespace Microsoft.CodeQuality.CSharp.Analyzers.ApiDesignGuidelines
{
Expand All @@ -13,5 +14,25 @@ namespace Microsoft.CodeQuality.CSharp.Analyzers.ApiDesignGuidelines
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
public sealed class CSharpIdentifiersShouldNotContainUnderscoresFixer : IdentifiersShouldNotContainUnderscoresFixer
{
protected override string GetNewName(string name)
{
string result = RemoveUnderscores(name);
if (result.Length == 0)
{
return string.Empty;
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
}

if (!SyntaxFacts.IsValidIdentifier(result))
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
{
return $"@{result}";
}

return result;
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
}

protected override SyntaxNode GetDeclarationNode(SyntaxNode node)
=> node.IsKind(SyntaxKind.IdentifierName)
? node.Parent
: node;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Analyzer.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Rename;

namespace Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines
{
Expand All @@ -11,19 +18,78 @@ namespace Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines
/// </summary>
public abstract class IdentifiersShouldNotContainUnderscoresFixer : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray<string>.Empty;
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(IdentifiersShouldNotContainUnderscoresAnalyzer.RuleId);

protected abstract string GetNewName(string name);
protected abstract SyntaxNode GetDeclarationNode(SyntaxNode node);

public sealed override FixAllProvider GetFixAllProvider()
{
// See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
return WellKnownFixAllProviders.BatchFixer;
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
}

Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
// Fixer not yet implemented.
return Task.CompletedTask;
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);

var node = root.FindNode(context.Span);
if (node == null)
{
return;
}

var symbol = model.GetDeclaredSymbol(GetDeclarationNode(node), context.CancellationToken);
if (symbol == null)
{
return;
}

var newName = GetNewName(symbol.Name);
if (newName.Length == 0)
{
return;
}

// Make sure there is no symbol with the same name already exists.
if (!model.LookupSymbols(context.Span.Start, symbol.ContainingType, newName).IsEmpty)
{
return;
}

string title = MicrosoftCodeQualityAnalyzersResources.IdentifiersShouldNotContainUnderscoresCodeFixTitle;
context.RegisterCodeFix(new MyCodeAction(title,
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
ct => Renamer.RenameSymbolAsync(context.Document.Project.Solution, symbol, newName, null, ct),
equivalenceKey: title),
context.Diagnostics);
}

protected static string RemoveUnderscores(string name)
{
var builder = new StringBuilder();
bool isPreviousUnderscore = false;
foreach (char c in name)
{
if (c == '_')
{
isPreviousUnderscore = true;
continue;
}

builder.Append(isPreviousUnderscore ? char.ToUpper(c, CultureInfo.InvariantCulture) : c);
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
isPreviousUnderscore = false;
}

return builder.ToString();
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
}

private class MyCodeAction : SolutionChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Solution>> createChangedSolution, string equivalenceKey)
: base(title, createChangedSolution, equivalenceKey)
{
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,9 @@
<data name="DoNotIgnoreMethodResultsMessageUserDefinedMethod" xml:space="preserve">
<value>'{0}' calls '{1}' but does not use the value the method returns. This method is defined as a user-option. Use the result in a conditional statement, assign the result to a variable, or pass it as an argument to another method.</value>
</data>
<data name="IdentifiersShouldNotContainUnderscoresCodeFixTitle" xml:space="preserve">
<value>Remove underscores</value>
</data>
<data name="MarkAttributesWithAttributeUsageCodeFix" xml:space="preserve">
<value>Apply 'AttributeUsageAttribute'</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">Události by neměly mít předponu Before nebo After</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">Odeberte finalizační metodu z typu {0}, přepište Dispose(bool disposing) a vložte finalizační logiku do cesty kódu, kde je disposing hodnoty false. V opačném případě by to mohlo vést k duplicitním voláním Dispose, protože základní typ {1} také poskytuje finalizační metodu.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">Ereignisse dürfen kein Präfix "Before" oder "After" aufweisen.</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">Entfernen Sie den Finalizer vom Typ "{0}", setzen Sie "Dispose(bool disposing)" außer Kraft, und platzieren Sie die Finalisierungslogik im Codepfad, wenn "disposing" FALSE lautet. Andernfalls kann es zu doppelten Dispose-Aufrufen kommen, weil der Basistyp "{1}" ebenfalls einen Finalizer bereitstellt.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">Los eventos no deben tener el prefijo "Antes" ni "Después"</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">Elimine el finalizador del tipo "{0}", reemplace el valor de "Dispose(bool disposing)" y coloque la lógica de finalización en la ruta de código de manera que el valor de "disposing" se establezca como falso. De lo contrario, se duplicarían las invocaciones de "Dispose" ya que el tipo Base "{1}" también ofrece un finalizador.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">Les événements ne doivent pas avoir un préfixe 'Before' ou 'After'</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">Supprimez le finaliseur du type '{0}', remplacez Dispose(bool disposing) et placez la logique de finalisation dans le chemin de code où 'disposing' est false. Sinon, vous risquez d'avoir des invocations de Dispose en double, car le type de base '{1}' fournit aussi un finaliseur.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">Non usare il prefisso 'Before' o 'After' negli eventi</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">Rimuovere il finalizzatore dal tipo '{0}', eseguire l'override di Dispose(bool disposing) e inserire la logica di finalizzazione nel percorso del codice in cui 'disposing' è false. In caso contrario, potrebbero verificarsi chiamate duplicate a Dispose perché il tipo di base '{1}' fornisce anche un finalizzatore.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">イベントに 'Before' または 'After' プレフィックスを付けることはできない</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">型 '{0}' からファイナライザーを削除し、Dispose(bool disposing) をオーバーライドします。その後、'disposing' が false のコード パスに finalization 論理を配置します。その他の場合、基本型 '{1}' でもファイナライザーが提供されるため、Dispose 呼び出しが重複する可能性があります。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">이벤트에 'Before' 또는 'After' 접두사를 사용하지 마세요.</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">'{0}' 형식에서 종료자를 제거하고 Dispose(bool disposing)를 재정의한 후 종료 논리를 'disposing'이 false인 코드 경로에 추가하세요. 그렇지 않으면, 기본 형식 '{1}'도 종료자를 제공할 때 중복된 Dispose 호출이 발생할 수 있습니다.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">Zdarzenia nie powinny mieć prefiksu „Przed” ani „Po”</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">Usuń finalizator z typu „{0}”, przesłoń funkcję Dispose(bool disposing) i umieść logikę finalizacji w ścieżce kodu, gdzie element „disposing” ma wartość false. W przeciwnym razie mogą pojawić się zduplikowane wywołania funkcji Dispose, ponieważ typ bazowy „{1}” także udostępnia finalizator.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">Eventos não devem ter o prefixo 'Before' ou 'After'</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">Remova o finalizador do tipo '{0}', substitua Dispose(bool disposing) e coloque a lógica de finalização no caminho do código em que 'disposing' é false. Caso contrário, ele poderá duplicar invocações Dispose, já que o tipo Base '{1}' também fornece um finalizador.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">3 - ReadyForReview</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">Удалите метод завершения из типа '{0}', переопределите Dispose(bool disposing) и поместите логику завершения в путь кода, туда где 'disposing' имеет значение false. В противном случае это может привести к дублированию вызовов метода Dispose, так как базовый тип '{1}' также предоставляет метод завершения.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">Olaylar 'Before' ya da 'After' ön ekine sahip olmamalıdır</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">'{0}' türünden sonlandırıcıyı kaldırın, Dispose(bool disposing) metodunu geçersiz kılın ve sonlandırma mantığını 'disposing' değerinin false olduğu kod yoluna yerleştirin. Aksi takdirde, '{1}' Temel türü de bir sonlandırıcı sağladığından yinelenen Dispose çağrılarına yol açabilir.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">事件不应具有 "Before" 或 "After" 前缀</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">从类型“{0}”中删除终结器,替代 Dispose(bool disposing),并在 "disposing" 为 false 的代码路径中放置终结逻辑。否则,它可能导致出现重复的 Dispose 调用,因为基类型“{1}”也提供终结器。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@
<target state="translated">事件開頭不應出現 'Before' 或 'After'</target>
<note />
</trans-unit>
<trans-unit id="IdentifiersShouldNotContainUnderscoresCodeFixTitle">
<source>Remove underscores</source>
<target state="new">Remove underscores</target>
<note />
</trans-unit>
<trans-unit id="ImplementIDisposableCorrectlyMessageFinalizeOverride">
<source>Remove the finalizer from type '{0}', override Dispose(bool disposing), and put the finalization logic in the code path where 'disposing' is false. Otherwise, it might lead to duplicate Dispose invocations as the Base type '{1}' also provides a finalizer.</source>
<target state="translated">從類型 '{0}' 移除完成項,覆寫 Dispose(bool disposing),然後在程式碼路徑中放置完成項邏輯,其中 'disposing' 為 false。否則這可能導致 Dispose 引動過程重複,原因是基底類型 ‘{1}’ 也提供了完成項。</target>
Expand Down
Loading