diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestContextShouldBeValidFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestContextShouldBeValidFixer.cs index 14639ce961..ddbaf97659 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/TestContextShouldBeValidFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/TestContextShouldBeValidFixer.cs @@ -76,56 +76,29 @@ private static async Task FixMemberDeclarationAsync(Document document, modifiers.Where(modifier => !modifier.IsKind(SyntaxKind.PrivateKeyword) && !modifier.IsKind(SyntaxKind.InternalKeyword) && !modifier.IsKind(SyntaxKind.ProtectedKeyword))).Add(visibilityModifier); } - MemberDeclarationSyntax newMemberDeclaration = memberDeclaration.WithModifiers(modifiers); - - if (newMemberDeclaration is FieldDeclarationSyntax fieldDeclarationSyntax) + // The analyzer only reports on property declarations. + var propertyDeclaration = (PropertyDeclarationSyntax)memberDeclaration.WithModifiers(modifiers); + if (!propertyDeclaration.Identifier.ValueText.Equals(TestContextShouldBeValidAnalyzer.TestContextPropertyName, StringComparison.Ordinal)) { - newMemberDeclaration = ConvertFieldToProperty(fieldDeclarationSyntax); + propertyDeclaration = propertyDeclaration.WithIdentifier( + SyntaxFactory.Identifier(propertyDeclaration.Identifier.LeadingTrivia, TestContextShouldBeValidAnalyzer.TestContextPropertyName, propertyDeclaration.Identifier.TrailingTrivia)); } - else - { - // ensure that the property has setter and getter - var propertyDeclaration = (PropertyDeclarationSyntax)newMemberDeclaration; - if (!propertyDeclaration.Identifier.ValueText.Equals(TestContextShouldBeValidAnalyzer.TestContextPropertyName, StringComparison.Ordinal)) - { - propertyDeclaration = propertyDeclaration.WithIdentifier( - SyntaxFactory.Identifier(propertyDeclaration.Identifier.LeadingTrivia, TestContextShouldBeValidAnalyzer.TestContextPropertyName, propertyDeclaration.Identifier.TrailingTrivia)); - } - SyntaxList accessors = propertyDeclaration.AccessorList?.Accessors ?? default; + SyntaxList accessors = propertyDeclaration.AccessorList?.Accessors ?? default; - AccessorDeclarationSyntax getAccessor = accessors.FirstOrDefault(a => a.Kind() == SyntaxKind.GetAccessorDeclaration) - ?? SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + AccessorDeclarationSyntax getAccessor = accessors.FirstOrDefault(a => a.Kind() == SyntaxKind.GetAccessorDeclaration) + ?? SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); - AccessorDeclarationSyntax setAccessor = accessors.FirstOrDefault(a => a.Kind() == SyntaxKind.SetAccessorDeclaration) - ?? SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + AccessorDeclarationSyntax setAccessor = accessors.FirstOrDefault(a => a.Kind() == SyntaxKind.SetAccessorDeclaration) + ?? SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); - newMemberDeclaration = propertyDeclaration.WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.List([getAccessor, setAccessor]))); - } + PropertyDeclarationSyntax newPropertyDeclaration = propertyDeclaration.WithAccessorList( + SyntaxFactory.AccessorList(SyntaxFactory.List([getAccessor, setAccessor]))); // Create a new member declaration with the updated modifiers. - editor.ReplaceNode(memberDeclaration, newMemberDeclaration); + editor.ReplaceNode(memberDeclaration, newPropertyDeclaration); SyntaxNode newRoot = editor.GetChangedRoot(); return document.WithSyntaxRoot(newRoot); } - - private static PropertyDeclarationSyntax ConvertFieldToProperty(FieldDeclarationSyntax fieldDeclaration) - { - TypeSyntax type = fieldDeclaration.Declaration.Type; - - // Create the property declaration - PropertyDeclarationSyntax propertyDeclaration = SyntaxFactory.PropertyDeclaration(type, TestContextShouldBeValidAnalyzer.TestContextPropertyName) - .WithModifiers(SyntaxFactory.TokenList(fieldDeclaration.Modifiers)) - .WithAccessorList(SyntaxFactory.AccessorList( - SyntaxFactory.List( - [ - SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), - SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)) - ]))); - - return propertyDeclaration; - } } diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index 0b3097f38c..a343119cf7 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -355,7 +355,7 @@ The type declaring these methods should also respect the following rules: TestCleanup method should have valid layout - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. diff --git a/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs index 361668c8f5..19e572c14e 100644 --- a/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs @@ -186,108 +186,77 @@ public override void Initialize(AnalysisContext context) var namedType = (INamedTypeSymbol)context.Symbol; foreach (ISymbol member in namedType.GetMembers()) { + if (member is not IPropertySymbol propertySymbol) + { + continue; + } + + if (!SymbolEqualityComparer.Default.Equals(propertySymbol.Type, testContextSymbol) || + !propertySymbol.Name.Equals(TestContextPropertyName, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if (propertySymbol.IsStatic) + { + context.RegisterSymbolEndAction( + context => context.ReportDiagnostic(propertySymbol.CreateDiagnostic(TestContextShouldBeValidRule))); + continue; + } + + if (IsTestContextPropertyAutomaticallyAssigned(propertySymbol, testContextSymbol)) + { + return; + } + IFieldSymbol? fieldReturnedByProperty = null; - ConcurrentBag? fieldsAssignedInConstructor = null; - switch (member.Kind) + ConcurrentBag fieldsAssignedInConstructor = []; + + context.RegisterOperationBlockAction(context => { - case SymbolKind.Property: - case SymbolKind.Field: - if (member is IPropertySymbol propertySymbol) + if (context.OwningSymbol.Equals(propertySymbol.GetMethod, SymbolEqualityComparer.Default)) + { + fieldReturnedByProperty = TryGetReturnedField(context.OperationBlocks); + } + else if (TryGetTestContextParameterIfValidConstructor(context.OwningSymbol, testContextSymbol) is { } parameter) + { + CollectTestContextFieldsAssignedInConstructor(parameter, context.OperationBlocks, fieldsAssignedInConstructor); + } + }); + + // Initially, we consider the property as not assigned in the constructor. + // Then, we look for a constructor with a single TestContext parameter and look for assignment + // in the constructor. We simply iterate over the operation blocks (no DFA involved for now). + bool isAssigned = false; + + context.RegisterOperationBlockAction( + context => + { + if (TryGetTestContextParameterIfValidConstructor(context.OwningSymbol, testContextSymbol) is not { } parameter) { - if (!SymbolEqualityComparer.Default.Equals(propertySymbol.Type, testContextSymbol) || - !member.Name.Equals(TestContextPropertyName, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (propertySymbol.IsStatic) - { - context.RegisterSymbolEndAction( - context => context.ReportDiagnostic(member.CreateDiagnostic(TestContextShouldBeValidRule))); - continue; - } - - if (IsTestContextPropertyAutomaticallyAssigned(propertySymbol, testContextSymbol)) - { - return; - } - - fieldsAssignedInConstructor = []; - - context.RegisterOperationBlockAction(context => - { - if (context.OwningSymbol.Equals(propertySymbol.GetMethod, SymbolEqualityComparer.Default)) - { - fieldReturnedByProperty = TryGetReturnedField(context.OperationBlocks); - } - else if (TryGetTestContextParameterIfValidConstructor(context.OwningSymbol, testContextSymbol) is { } parameter) - { - CollectTestContextFieldsAssignedInConstructor(parameter, context.OperationBlocks, fieldsAssignedInConstructor); - } - }); + return; } - else if (member is IFieldSymbol fieldSymbol) + + if (AssignsParameterToMember(parameter, propertySymbol, context.OperationBlocks)) { - // AssociatedSymbol check is to not analyze compiler-generated backing field. - if (fieldSymbol.AssociatedSymbol is not null || - // Workaround https://github.com/dotnet/roslyn/issues/70208 - // https://github.com/dotnet/roslyn/blob/05e49aa98995349ffa26a19020333293ffe99670/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs#L47 - (fieldSymbol.Name.StartsWith("<", StringComparison.Ordinal) && fieldSymbol.Name.EndsWith(">P", StringComparison.Ordinal)) || - !SymbolEqualityComparer.Default.Equals(fieldSymbol.Type, testContextSymbol)) - { - continue; - } - - // For fields, we check the type but not the name to allow analyzing different conventions. - // The field could be named _testContext, testContext, or s_testContext. So we want to analyze all these. - if (fieldSymbol.IsStatic) - { - context.RegisterSymbolEndAction( - context => context.ReportDiagnostic(member.CreateDiagnostic(TestContextShouldBeValidRule))); - continue; - } + isAssigned = true; } - else + }); + + context.RegisterSymbolEndAction( + context => + { + if (!isAssigned) { - throw ApplicationStateGuard.Unreachable(); + isAssigned = fieldReturnedByProperty is not null && + fieldsAssignedInConstructor.Contains(fieldReturnedByProperty, SymbolEqualityComparer.Default); } - // Initially, we consider the field/property as not assigned in the constructor. - // Then, we look for a constructor with a single TestContext parameter and look for assignment - // in the constructor. We simply iterate over the operation blocks (no DFA involved for now). - bool isAssigned = false; - - context.RegisterOperationBlockAction( - context => - { - if (TryGetTestContextParameterIfValidConstructor(context.OwningSymbol, testContextSymbol) is not { } parameter) - { - return; - } - - if (AssignsParameterToMember(parameter, member, context.OperationBlocks)) - { - isAssigned = true; - } - }); - - context.RegisterSymbolEndAction( - context => - { - if (!isAssigned) - { - isAssigned = fieldReturnedByProperty is not null && - fieldsAssignedInConstructor?.Contains(fieldReturnedByProperty, SymbolEqualityComparer.Default) == true; - } - - if (!isAssigned) - { - context.ReportDiagnostic(member.CreateDiagnostic(TestContextShouldBeValidRule)); - } - }); - - break; - } + if (!isAssigned) + { + context.ReportDiagnostic(propertySymbol.CreateDiagnostic(TestContextShouldBeValidRule)); + } + }); } }, SymbolKind.NamedType); } diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index 062636e15c..69d02dc9e8 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -792,11 +792,11 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - TestContext musí být nestatické pole nebo vlastnost přiřazené v konstruktoru nebo pro vlastnost nastavenou přes MSTest. Musí se řídit následujícím rozložením: + TestContext musí být nestatické pole nebo vlastnost přiřazené v konstruktoru nebo pro vlastnost nastavenou přes MSTest. Musí se řídit následujícím rozložením: – Musí být public bez ohledu na to, jestli je nastavený atribut [assembly: DiscoverInternals]. – Nesmí být static. – Musí mít metodu setter. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index 3e68f26343..60d3a314c7 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -793,11 +793,11 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - „TestContext“ muss ein nicht statisches Feld oder eine nicht statische Eigenschaft sein, das im Konstruktor zugewiesen ist, oder für eine von MSTest festgelegte Eigenschaft sollte es dem Layout folgen: + „TestContext“ muss ein nicht statisches Feld oder eine nicht statische Eigenschaft sein, das im Konstruktor zugewiesen ist, oder für eine von MSTest festgelegte Eigenschaft sollte es dem Layout folgen: – es sollte „public“ sein, unabhängig davon, ob das Attribut „[assembly: DiscoverInternals]“ festgelegt ist oder nicht. – sie darf nicht „static“ sein – es sollte einen Setter aufweisen. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index 76d6db223e..b1662c2d57 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -792,11 +792,11 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - "TestContext" debe ser un campo no estático o una propiedad asignada en el constructor o para una propiedad establecida por MSTest, debe seguir el diseño: + "TestContext" debe ser un campo no estático o una propiedad asignada en el constructor o para una propiedad establecida por MSTest, debe seguir el diseño: - debe ser "public" independientemente de si el atributo "[assembly: DiscoverInternals]" está establecido o no. - No debe ser "static" - debe tener un establecedor. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index 395171adcb..78b25f56b7 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -792,11 +792,11 @@ Le type doit être une classe - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - « TestContext » doit être un champ ou une propriété non statique affecté dans le constructeur ou pour une propriété définie par MSTest, il doit suivre la disposition : + « TestContext » doit être un champ ou une propriété non statique affecté dans le constructeur ou pour une propriété définie par MSTest, il doit suivre la disposition : - il doit être « public », que l’attribut « [assembly: DiscoverInternals] » soit défini ou non. - il ne devrait pas être « statique » - il doit avoir un setter. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index 4fa6f4e443..c3eed4dfd5 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -792,11 +792,11 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - 'TestContext' deve essere una proprietà o un campo non statico assegnato nel costruttore o per un insieme di proprietà da MSTest. Deve seguire il layout: + 'TestContext' deve essere una proprietà o un campo non statico assegnato nel costruttore o per un insieme di proprietà da MSTest. Deve seguire il layout: - deve essere “pubblico” indipendentemente dal fatto che l'attributo “[assembly: DiscoverInternals]” sia impostato o meno. - non deve essere “statico” - deve avere un setter. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index 6df7b050ad..bd6504cd4a 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -792,11 +792,11 @@ The type declaring these methods should also respect the following rules: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - 'TestContext' は、コンストラクターまたは MSTest によって設定されたプロパティに割り当てられた非静的フィールドまたはプロパティである必要があります。レイアウトに従う必要があります: + 'TestContext' は、コンストラクターまたは MSTest によって設定されたプロパティに割り当てられた非静的フィールドまたはプロパティである必要があります。レイアウトに従う必要があります: - '[assembly: DiscoverInternals]' 属性が設定されているかどうかに関係なく、'public' である必要があります。 - 'static' にすることはできません - セッターが必要です。 diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index 1c7116dccb..3c336e303e 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -792,11 +792,11 @@ The type declaring these methods should also respect the following rules: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - 'TestContext'는 생성자 또는 MSTest에서 설정한 속성에 할당된 비정적 필드 또는 속성이어야 하며 레이아웃을 따라야 합니다. + 'TestContext'는 생성자 또는 MSTest에서 설정한 속성에 할당된 비정적 필드 또는 속성이어야 하며 레이아웃을 따라야 합니다. - '[assembly: DiscoverInternals]' 특성이 설정되었는지 여부에 관계없이 'public'이어야 합니다. - 'static'이 아니어야 합니다. - setter가 있어야 합니다. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index 5f07c58beb..1a8481a075 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -792,11 +792,11 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - Element „TestContext” powinien być niestatycznym polem lub właściwością przypisaną w konstruktorze lub dla właściwości ustawionej przez narzędzie MSTest, powinien być zgodny z układem: + Element „TestContext” powinien być niestatycznym polem lub właściwością przypisaną w konstruktorze lub dla właściwości ustawionej przez narzędzie MSTest, powinien być zgodny z układem: — powinien mieć wartość „public” niezależnie od tego, czy ustawiono atrybut „[assembly: DiscoverInternals]”. — nie powinien mieć wartości „static” — powinien mieć metodę ustawiającą. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index f3cb1d9749..9aad657f31 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -792,11 +792,11 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - 'TestContext' deve ser um campo ou propriedade não estática atribuída no construtor ou, para uma propriedade definida pelo MSTest, deve seguir o layout: + 'TestContext' deve ser um campo ou propriedade não estática atribuída no construtor ou, para uma propriedade definida pelo MSTest, deve seguir o layout: - deve ser 'public' independentemente de o atributo '[assembly: DiscoverInternals]' estar definido ou não. - não deve ser 'static' - deve ter um setter. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index f493ae6ace..3458543a25 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -801,11 +801,11 @@ The type declaring these methods should also respect the following rules: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - "TestContext" должно быть нестатическим полем или свойством, присваиваемым в конструкторе, или — для свойства, установленного MSTest — должно следовать набору правил: + "TestContext" должно быть нестатическим полем или свойством, присваиваемым в конструкторе, или — для свойства, установленного MSTest — должно следовать набору правил: — оно должно быть объявлено как "public", независимо от того, задан ли атрибут "[assembly: DiscoverInternals]"; — оно не может быть объявлено как "static"; — для него должен быть определен метод задания. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index e0ad0f449c..8500bfc680 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -793,11 +793,11 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - 'TestContext' statik olmayan bir alan veya oluşturucuda atanan bir özellik olmalıdır veya MSTest tarafından ayarlanan bir özellik kümesi için şu düzeni izlemelidir: + 'TestContext' statik olmayan bir alan veya oluşturucuda atanan bir özellik olmalıdır veya MSTest tarafından ayarlanan bir özellik kümesi için şu düzeni izlemelidir: - '[assembly: DiscoverInternals]' özniteliğinin ayarlanıp ayarlanmadığına bakılmaksızın 'public' olmalıdır. - 'static' olmamalıdır - bir ayarlayıcı içermelidir. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index f03143e464..5b29ac5e97 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -792,11 +792,11 @@ The type declaring these methods should also respect the following rules: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - "TestContext" 应该是构造函数中分配的非静态字段或属性,或者对于 MSTest 设置的属性,它应遵循以下布局: + "TestContext" 应该是构造函数中分配的非静态字段或属性,或者对于 MSTest 设置的属性,它应遵循以下布局: - 无论是否设置 "[assembly: DiscoverInternals]" 属性,它都应为 "public"。 - 它不应为 "static" - 它应具有一个资源库。 diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index 5cc4b4ed68..4b14e0c660 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -792,11 +792,11 @@ The type declaring these methods should also respect the following rules: - 'TestContext' should be a non-static field or property assigned in constructor or for a property set by MSTest, it should follow the layout: + 'TestContext' should be a non-static property assigned in constructor or set by MSTest. To be set by MSTest, it should follow the layout: - it should be 'public' regardless of whether '[assembly: DiscoverInternals]' attribute is set or not. - it should not be 'static' - it should have a setter. - 'TestContext' 應是在建構函式中指派的非靜態欄位或屬性,或是針對由 MSTest 所設定的屬性,其應遵循下列配置: + 'TestContext' 應是在建構函式中指派的非靜態欄位或屬性,或是針對由 MSTest 所設定的屬性,其應遵循下列配置: - 它應該是 'public' (無論是否有設定 '[assembly: DiscoverInternals]' 屬性)。 - 它不應該是 'static' - 它應該有 setter。 diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs index 7a04be3267..5dedea9e19 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs @@ -27,63 +27,72 @@ public sealed class TestContextShouldBeValidAnalyzerTests [DataRow("TeStCoNtExT", "internal")] [DataRow("TeStCoNtExT", "protected")] [TestMethod] - public async Task WhenTestContextCaseInsensitiveIsField_Diagnostic(string fieldName, string accessibility) + public async Task WhenTestContextCaseInsensitiveIsField_NoDiagnostic(string fieldName, string accessibility) { + // MSTEST0005 only validates the TestContext property layout. Fields of type TestContext + // are intentionally not flagged, because doing so produced too many false positives + // (see https://github.com/microsoft/testfx/issues/4590). The static-field case is + // covered by MSTEST0024 (DoNotStoreStaticTestContext). string code = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class MyTestClass { - {{accessibility}} TestContext [|{{fieldName}}|]; + {{accessibility}} TestContext {{fieldName}}; } """; - string fixedCode = - """ + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [DataRow("_testContext")] + [DataRow("s_testContext")] + [DataRow("testContext")] + [TestMethod] + public async Task WhenStaticFieldOfTypeTestContextAssignedInClassInitialize_NoDiagnostic(string fieldName) + { + // Regression test for https://github.com/microsoft/testfx/issues/4590: + // a static field of type TestContext that is assigned in a [ClassInitialize] method + // must not trigger MSTEST0005. MSTEST0024 already covers the "do not store TestContext + // in a static member" guidance. + string code = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class MyTestClass { - public TestContext TestContext { get; set; } + private static TestContext {{fieldName}}; + + [ClassInitialize] + public static void ClassInitialize(TestContext context) => + {{fieldName}} = context; + + [TestMethod] + public void TestMethod1() { } } """; - await VerifyCS.VerifyCodeFixAsync( - code, - fixedCode); + + await VerifyCS.VerifyCodeFixAsync(code, code); } - [DataRow("TestContext", "private")] - [DataRow("TestContext", "public")] - [DataRow("TestContext", "internal")] - [DataRow("TestContext", "protected")] - [DataRow("testcontext", "private")] - [DataRow("testcontext", "public")] - [DataRow("testcontext", "internal")] - [DataRow("testcontext", "protected")] - [DataRow("TESTCONTEXT", "private")] - [DataRow("TESTCONTEXT", "public")] - [DataRow("TESTCONTEXT", "internal")] - [DataRow("TESTCONTEXT", "protected")] - [DataRow("TeStCoNtExT", "private")] - [DataRow("TeStCoNtExT", "public")] - [DataRow("TeStCoNtExT", "internal")] - [DataRow("TeStCoNtExT", "protected")] [TestMethod] - public async Task WhenTestContextCaseInsensitiveIsField_AssignedInConstructor_NoDiagnostic(string fieldName, string accessibility) + public async Task WhenStaticFieldOfTypeTestContextIsNeverAssigned_NoDiagnostic() { - string code = $$""" + // Documents the deliberate behavior change tied to https://github.com/microsoft/testfx/issues/4590: + // MSTEST0005 no longer reports on fields of type TestContext, including unassigned static fields. + // Such a field is also not reported by MSTEST0024 (which only fires on assignment), so this is + // an accepted trade-off in favor of removing the false positives that previously bothered users. + string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class MyTestClass { - public MyTestClass(TestContext testContext) - { - this.{{fieldName}} = testContext; - } + private static TestContext _context; - {{accessibility}} TestContext {{fieldName}}; + [TestMethod] + public void TestMethod1() { } } """;