diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx index 781c94f35366..f9f0ed9668e6 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx @@ -186,8 +186,8 @@ Cannot add virtual keyword to member '{0}'. - - Cannot remove virtual keyword from member '{0}'. + + Cannot remove '{0}' keyword from member '{1}'. API compatibility errors between '{0}' ({2}) and '{1}' ({3}): diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs index 7f7f3dec8a08..3194cc83941e 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs @@ -63,7 +63,7 @@ private void RunOnMemberSymbol(ISymbol? left, ISymbol? right, ITypeSymbol leftCo leftMetadata, rightMetadata, DiagnosticIds.CannotRemoveVirtualFromMember, - string.Format(Resources.CannotRemoveVirtualFromMember, left), + string.Format(Resources.CannotRemoveVirtualOrAbstractFromMember, "virtual", left), DifferenceType.Removed, right)); } @@ -86,6 +86,21 @@ private void RunOnMemberSymbol(ISymbol? left, ISymbol? right, ITypeSymbol leftCo right)); } } + + if (left.IsAbstract) + { + if (!right.IsAbstract && !right.IsVirtual) + { + // abstract can be made virtual but cannot remove abstract. + differences.Add(new CompatDifference( + leftMetadata, + rightMetadata, + DiagnosticIds.CannotRemoveVirtualFromMember, + string.Format(Resources.CannotRemoveVirtualOrAbstractFromMember, "abstract", left), + DifferenceType.Removed, + right)); + } + } } } } diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf index 5872df4520eb..7f39c5e79e0f 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf @@ -122,9 +122,9 @@ U parametru typu {1} z(e) {2} nelze odebrat omezení {0}. - - Cannot remove virtual keyword from member '{0}'. - Nelze odebrat virtuální klíčové slovo ze člena {0}. + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf index a1c7eb6bc0ff..75cf2421a55c 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf @@ -122,9 +122,9 @@ Die Einschränkung „{0}“ für den Typparameter „{1}“ von „{2}“ kann nicht entfernt werden. - - Cannot remove virtual keyword from member '{0}'. - Das virtuelle Schlüsselwort kann nicht aus dem Member „{0}“ entfernt werden. + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf index 4588e5e99dc8..b0b37efef4bf 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf @@ -122,9 +122,9 @@ No se pueden quitar la restricción '{0}' en el parámetro de tipo '{1}' de '{2}'. - - Cannot remove virtual keyword from member '{0}'. - No se puede quitar la palabra clave virtual del miembro "{0}". + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf index 2f1d893ea7a8..5a7563d9d51b 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf @@ -122,9 +122,9 @@ Impossible de supprimer la contrainte « {0} » sur le paramètre de type « {1} » de « {2} ». - - Cannot remove virtual keyword from member '{0}'. - Impossible de supprimer le mot-clé virtuel du membre '{0}'. + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf index dd4468415175..cd156bd23d14 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf @@ -122,9 +122,9 @@ Impossibile rimuovere il vincolo '{0}' nel parametro di tipo '{1}' di '{2}'. - - Cannot remove virtual keyword from member '{0}'. - Non è possibile rimuovere la parola chiave virtuale dal membro '{0}'. + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf index 41eec2342b28..00205f06ebee 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf @@ -122,9 +122,9 @@ '{2}' の型パラメーター '{1}' の制約 '{0}' を削除できません。 - - Cannot remove virtual keyword from member '{0}'. - メンバー '{0}' から仮想キーワードを削除できません。 + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf index b18330f14056..6e134b556e74 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf @@ -122,9 +122,9 @@ 형식 매개 변수 '{2}'의 '{1}'에서 제약 조건 '{0}'을(를) 제거할 수 없습니다. - - Cannot remove virtual keyword from member '{0}'. - 구성원 '{0}'에서 가상 키워드를 제거할 수 없습니다. + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf index 6aefc58b20a5..6f85a13f676a 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf @@ -122,9 +122,9 @@ Nie można usunąć ograniczenia „{0}” w parametrze typu „{1}” z „{2}”. - - Cannot remove virtual keyword from member '{0}'. - Nie można usunąć wirtualnego słowa kluczowego z składowej „{0}”. + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf index c52b5f528fb2..4db8cc3804c6 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf @@ -122,9 +122,9 @@ Não é possível remover a restrição '{0}' no parâmetro de tipo '{1}' de '{2}'. - - Cannot remove virtual keyword from member '{0}'. - Não é possível remover a palavra-chave virtual do membro '{0}'. + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf index 7254edc7e52a..be95d93acfbb 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf @@ -122,9 +122,9 @@ Не удалось удалить ограничение "{0}" для параметра типа "{1}" из "{2}". - - Cannot remove virtual keyword from member '{0}'. - Не удается удалить виртуальное ключевое слово из элемента "{0}". + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf index 8f5b2a486612..7d0d45789ce3 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf @@ -122,9 +122,9 @@ '{1}'/'{2}' tür parametresindeki '{0}' kısıtlaması kaldırılamıyor. - - Cannot remove virtual keyword from member '{0}'. - Sanal anahtar sözcük, '{0}' üyesinden kaldırılamıyor. + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf index cb556448476d..719984883329 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf @@ -122,9 +122,9 @@ 无法移除 "{2}" 的类型参数 "{1}" 上的约束 "{0}"。 - - Cannot remove virtual keyword from member '{0}'. - 无法从成员“{0}”中删除虚拟关键字。 + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf index bcd1eb5ac9e6..2785bcd3f8e3 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf @@ -122,9 +122,9 @@ 無法移除 '{2}' 的類型參數 '{1}' 上的限制式 '{0}'。 - - Cannot remove virtual keyword from member '{0}'. - 無法從成員 '{0}' 移除虛擬關鍵字。 + + Cannot remove '{0}' keyword from member '{1}'. + Cannot remove '{0}' keyword from member '{1}'. diff --git a/test/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs b/test/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs index f6eb2a8ba5e8..3a45ecba122b 100644 --- a/test/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs +++ b/test/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs @@ -43,6 +43,20 @@ public static IEnumerable RemovedCases() false, CreateDifferences((DifferenceType.Removed, "M:CompatTests.First.F")), }; + // remove abstract from member + yield return new object[] { + CreateType(" abstract class", " public abstract void F();"), + CreateType(" abstract class", " public void F() {}"), + false, + CreateDifferences((DifferenceType.Removed, "M:CompatTests.First.F")), + }; + // replace abstract with virtual (no diffs expected) + yield return new object[] { + CreateType(" abstract class", " public abstract void F();"), + CreateType(" abstract class", " public virtual void F() {}"), + false, + CreateDifferences() + }; // properties yield return new object[] { CreateType(" class", " public virtual int F { get; }"), @@ -125,6 +139,13 @@ public static IEnumerable AddedCasesStrictMode() true, CreateDifferences((DifferenceType.Added,"M:CompatTests.First.F" )), }; + // remove abstract from member + yield return new object[] { + CreateType(" abstract class", " public abstract void F();"), + CreateType(" abstract class", " public void F() {}"), + true, + CreateDifferences((DifferenceType.Removed, "M:CompatTests.First.F")), + }; // abstract -> virtual yield return new object[] { CreateType(" abstract class", " public abstract void F();"),