Repository: xunit/xunit.analyzers Branch: main Commit: 8da2743cb286 Files: 373 Total size: 1.3 MB Directory structure: gitextract_bu8u1mqd/ ├── .config/ │ └── dotnet-tools.json ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── ci-signed.yaml │ ├── ci-unsigned.yaml │ └── pull-request.yaml ├── .gitignore ├── .gitmodules ├── .vscode/ │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── BUILDING.md ├── LICENSE ├── NuGet.Config ├── README.md ├── build ├── build.ps1 ├── global.json ├── src/ │ ├── Directory.Build.props │ ├── Directory.Build.targets │ ├── common/ │ │ ├── CallerArgumentExpressionAttribute.cs │ │ └── Guard.cs │ ├── compat/ │ │ ├── Directory.Build.props │ │ ├── xunit.analyzers.latest/ │ │ │ └── xunit.analyzers.latest.csproj │ │ ├── xunit.analyzers.latest.fixes/ │ │ │ └── xunit.analyzers.latest.fixes.csproj │ │ └── xunit.analyzers.latest.tests/ │ │ └── xunit.analyzers.latest.tests.csproj │ ├── signing.snk │ ├── xunit.analyzers/ │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Suppressors/ │ │ │ ├── ConsiderCallingConfigureAwaitSuppressor.cs │ │ │ ├── MakeTypesInternalSuppressor.cs │ │ │ ├── NonNullableFieldInitializationSuppressor.cs │ │ │ └── UseAsyncSuffixForAsyncMethodsSuppressor.cs │ │ ├── Utility/ │ │ │ ├── AssertUsageAnalyzerBase.cs │ │ │ ├── Category.cs │ │ │ ├── CodeAnalysisExtensions.cs │ │ │ ├── Constants.cs │ │ │ ├── ConversionChecker.cs │ │ │ ├── Descriptors.Suppressors.cs │ │ │ ├── Descriptors.cs │ │ │ ├── Descriptors.xUnit1xxx.cs │ │ │ ├── Descriptors.xUnit2xxx.cs │ │ │ ├── Descriptors.xUnit3xxx.cs │ │ │ ├── EmptyAssertContext.cs │ │ │ ├── EmptyCommonContext.cs │ │ │ ├── EmptyCoreContext.cs │ │ │ ├── EmptyRunnerUtilityContext.cs │ │ │ ├── EnumerableExtensions.cs │ │ │ ├── IAssertContext.cs │ │ │ ├── ICommonContext.cs │ │ │ ├── ICoreContext.cs │ │ │ ├── IRunnerUtilityContext.cs │ │ │ ├── Serializability.cs │ │ │ ├── SerializabilityAnalyzer.cs │ │ │ ├── SerializableTypeSymbols.cs │ │ │ ├── SymbolExtensions.cs │ │ │ ├── SyntaxExtensions.cs │ │ │ ├── TypeHierarchyComparer.cs │ │ │ ├── TypeSymbolFactory.cs │ │ │ ├── V2AbstractionsContext.cs │ │ │ ├── V2AssertContext.cs │ │ │ ├── V2CoreContext.cs │ │ │ ├── V2ExecutionContext.cs │ │ │ ├── V2RunnerUtilityContext.cs │ │ │ ├── V3AssertContext.cs │ │ │ ├── V3CommonContext.cs │ │ │ ├── V3CoreContext.cs │ │ │ ├── V3RunnerCommonContext.cs │ │ │ ├── V3RunnerUtilityContext.cs │ │ │ ├── XunitContext.cs │ │ │ ├── XunitDiagnosticAnalyzer.cs │ │ │ ├── XunitDiagnosticSuppressor.cs │ │ │ ├── XunitV2DiagnosticAnalyzer.cs │ │ │ ├── XunitV2DiagnosticSuppressor.cs │ │ │ ├── XunitV3DiagnosticAnalyzer.cs │ │ │ └── XunitV3DiagnosticSuppressor.cs │ │ ├── X1000/ │ │ │ ├── ClassDataAttributeMustPointAtValidClass.cs │ │ │ ├── CollectionDefinitionsMustBePublic.cs │ │ │ ├── ConstructorsOnFactAttributeSubclassShouldBePublic.cs │ │ │ ├── DataAttributeShouldBeUsedOnATheory.cs │ │ │ ├── DoNotUseAsyncVoidForTestMethods.cs │ │ │ ├── DoNotUseBlockingTaskOperations.cs │ │ │ ├── DoNotUseConfigureAwait.cs │ │ │ ├── EnsureFixturesHaveASource.cs │ │ │ ├── FactMethodMustNotHaveParameters.cs │ │ │ ├── FactMethodShouldNotHaveTestData.cs │ │ │ ├── InlineDataMustMatchTheoryParameters.cs │ │ │ ├── InlineDataShouldBeUniqueWithinTheory.cs │ │ │ ├── LocalFunctionsCannotBeTestFunctions.cs │ │ │ ├── MemberDataShouldReferenceValidMember.cs │ │ │ ├── PublicMethodShouldBeMarkedAsTest.cs │ │ │ ├── TestClassCannotBeNestedInGenericClass.cs │ │ │ ├── TestClassMustBePublic.cs │ │ │ ├── TestClassShouldHaveTFixtureArgument.cs │ │ │ ├── TestMethodCannotHaveOverloads.cs │ │ │ ├── TestMethodMustNotHaveMultipleFactAttributes.cs │ │ │ ├── TestMethodShouldNotBeSkipped.cs │ │ │ ├── TestMethodSupportedReturnType.cs │ │ │ ├── TheoryDataRowArgumentsShouldBeSerializable.cs │ │ │ ├── TheoryDataShouldNotUseTheoryDataRow.cs │ │ │ ├── TheoryDataTypeArgumentsShouldBeSerializable.cs │ │ │ ├── TheoryMethodCannotHaveDefaultParameter.cs │ │ │ ├── TheoryMethodCannotHaveParamsArray.cs │ │ │ ├── TheoryMethodMustHaveTestData.cs │ │ │ ├── TheoryMethodShouldHaveParameters.cs │ │ │ ├── TheoryMethodShouldUseAllParameters.cs │ │ │ └── UseCancellationToken.cs │ │ ├── X2000/ │ │ │ ├── AssertCollectionContainsShouldNotUseBoolCheck.cs │ │ │ ├── AssertEmptyCollectionCheckShouldNotBeUsed.cs │ │ │ ├── AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecks.cs │ │ │ ├── AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck.cs │ │ │ ├── AssertEqualGenericShouldNotBeUsedForStringValue.cs │ │ │ ├── AssertEqualLiteralValueShouldBeFirst.cs │ │ │ ├── AssertEqualPrecisionShoulBeInRange.cs │ │ │ ├── AssertEqualShouldNotBeUsedForBoolLiteralCheck.cs │ │ │ ├── AssertEqualShouldNotBeUsedForCollectionSizeCheck.cs │ │ │ ├── AssertEqualShouldNotBeUsedForNullCheck.cs │ │ │ ├── AssertEqualsShouldNotBeUsed.cs │ │ │ ├── AssertIsTypeShouldNotBeUsedForAbstractType.cs │ │ │ ├── AssertIsTypeShouldUseGenericOverloadType.cs │ │ │ ├── AssertNullShouldNotBeCalledOnValueTypes.cs │ │ │ ├── AssertRegexMatchShouldNotUseBoolLiteralCheck.cs │ │ │ ├── AssertSameShouldNotBeCalledOnValueTypes.cs │ │ │ ├── AssertSingleShouldBeUsedForSingleParameter.cs │ │ │ ├── AssertSingleShouldUseTwoArgumentCall.cs │ │ │ ├── AssertStringEqualityCheckShouldNotUseBoolCheck.cs │ │ │ ├── AssertSubstringCheckShouldNotUseBoolCheck.cs │ │ │ ├── AssertThrowsShouldNotBeUsedForAsyncThrowsCheck.cs │ │ │ ├── AssertThrowsShouldUseGenericOverloadCheck.cs │ │ │ ├── AssignableFromAssertionIsConfusinglyNamed.cs │ │ │ ├── AsyncAssertsShouldBeAwaited.cs │ │ │ ├── BooleanAssertsShouldNotBeNegated.cs │ │ │ ├── BooleanAssertsShouldNotBeUsedForSimpleEqualityCheck.cs │ │ │ ├── DoNotUseAssertEmptyWithProblematicTypes.cs │ │ │ ├── SetEqualityAnalyzer.cs │ │ │ └── UseAssertFailInsteadOfBooleanAssert.cs │ │ ├── X3000/ │ │ │ ├── CrossAppDomainClassesMustBeLongLivedMarshalByRefObject.cs │ │ │ ├── DoNotTestForConcreteTypeOfJsonSerializableTypes.cs │ │ │ ├── FactAttributeDerivedClassesShouldProvideSourceInformationConstructor.cs │ │ │ └── SerializableClassMustHaveParameterlessConstructor.cs │ │ ├── xunit.analyzers.csproj │ │ └── xunit.analyzers.nuspec │ ├── xunit.analyzers.fixes/ │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Utility/ │ │ │ ├── AsyncHelper.cs │ │ │ ├── CodeAnalysisExtensions.cs │ │ │ ├── ConvertAttributeCodeAction.cs │ │ │ ├── RemoveAttributesOfTypeCodeAction.cs │ │ │ ├── XunitCodeAction.cs │ │ │ ├── XunitCodeFixProvider.cs │ │ │ └── XunitMemberFixProvider.cs │ │ ├── X1000/ │ │ │ ├── ClassDataAttributeMustPointAtValidClassFixer.cs │ │ │ ├── CollectionDefinitionClassesMustBePublicFixer.cs │ │ │ ├── ConvertToFactFix.cs │ │ │ ├── ConvertToTheoryFix.cs │ │ │ ├── DataAttributeShouldBeUsedOnATheoryFixer.cs │ │ │ ├── DoNotUseAsyncVoidForTestMethodsFixer.cs │ │ │ ├── DoNotUseConfigureAwaitFixer.cs │ │ │ ├── FactMethodMustNotHaveParametersFixer.cs │ │ │ ├── FactMethodShouldNotHaveTestDataFixer.cs │ │ │ ├── InlineDataMustMatchTheoryParameters_ExtraValueFixer.cs │ │ │ ├── InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameterFixer.cs │ │ │ ├── InlineDataMustMatchTheoryParameters_TooFewValuesFixer.cs │ │ │ ├── InlineDataShouldBeUniqueWithinTheoryFixer.cs │ │ │ ├── LocalFunctionsCannotBeTestFunctionsFixer.cs │ │ │ ├── MemberDataShouldReferenceValidMember_ExtraValueFixer.cs │ │ │ ├── MemberDataShouldReferenceValidMember_NameOfFixer.cs │ │ │ ├── MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixer.cs │ │ │ ├── MemberDataShouldReferenceValidMember_ParamsForNonMethodFixer.cs │ │ │ ├── MemberDataShouldReferenceValidMember_ReturnTypeFixer.cs │ │ │ ├── MemberDataShouldReferenceValidMember_StaticFixer.cs │ │ │ ├── MemberDataShouldReferenceValidMember_VisibilityFixer.cs │ │ │ ├── PublicMethodShouldBeMarkedAsTestFixer.cs │ │ │ ├── RemoveMethodParameterFix.cs │ │ │ ├── TestClassCannotBeNestedInGenericClassFixer.cs │ │ │ ├── TestClassMustBePublicFixer.cs │ │ │ ├── TestClassShouldHaveTFixtureArgumentFixer.cs │ │ │ ├── TestMethodMustNotHaveMultipleFactAttributesFixer.cs │ │ │ ├── TestMethodShouldNotBeSkippedFixer.cs │ │ │ ├── TheoryDataShouldNotUseTheoryDataRowFixer.cs │ │ │ ├── TheoryMethodCannotHaveDefaultParameterFixer.cs │ │ │ └── UseCancellationTokenFixer.cs │ │ ├── X2000/ │ │ │ ├── AssertCollectionContainsShouldNotUseBoolCheckFixer.cs │ │ │ ├── AssertEmptyCollectionCheckShouldNotBeUsedFixer.cs │ │ │ ├── AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixer.cs │ │ │ ├── AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer.cs │ │ │ ├── AssertEqualGenericShouldNotBeUsedForStringValueFixer.cs │ │ │ ├── AssertEqualLiteralValueShouldBeFirstFixer.cs │ │ │ ├── AssertEqualPrecisionShouldBeInRangeFixer.cs │ │ │ ├── AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer.cs │ │ │ ├── AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer.cs │ │ │ ├── AssertEqualShouldNotBeUsedForNullCheckFixer.cs │ │ │ ├── AssertEqualsShouldNotBeUsedFixer.cs │ │ │ ├── AssertIsTypeShouldNotBeUsedForAbstractTypeFixer.cs │ │ │ ├── AssertNullShouldNotBeCalledOnValueTypesFixer.cs │ │ │ ├── AssertRegexMatchShouldNotUseBoolLiteralCheckFixer.cs │ │ │ ├── AssertSameShouldNotBeCalledOnValueTypesFixer.cs │ │ │ ├── AssertSingleShouldBeUsedForSingleParameterFixer.cs │ │ │ ├── AssertSingleShouldUseTwoArgumentCallFixer.cs │ │ │ ├── AssertStringEqualityCheckShouldNotUseBoolCheckFixer.cs │ │ │ ├── AssertSubstringCheckShouldNotUseBoolCheckFixer.cs │ │ │ ├── AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer.cs │ │ │ ├── AssignableFromAssertionIsConfusinglyNamedFixer.cs │ │ │ ├── AsyncAssertsShouldBeAwaitedFixer.cs │ │ │ ├── BooleanAssertsShouldNotBeNegatedFixer.cs │ │ │ ├── BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixer.cs │ │ │ ├── BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer.cs │ │ │ ├── UseAssertFailInsteadOfBooleanAssertFixer.cs │ │ │ └── UseGenericOverloadFix.cs │ │ ├── X3000/ │ │ │ ├── CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.cs │ │ │ └── SerializableClassMustHaveParameterlessConstructorFixer.cs │ │ ├── tools/ │ │ │ ├── install.ps1 │ │ │ └── uninstall.ps1 │ │ └── xunit.analyzers.fixes.csproj │ └── xunit.analyzers.tests/ │ ├── Analyzers/ │ │ ├── X1000/ │ │ │ ├── ClassDataAttributeMustPointAtValidClassTests.cs │ │ │ ├── CollectionDefinitionClassesMustBePublicTests.cs │ │ │ ├── ConstructorsOnFactAttributeSubclassShouldBePublicTests.cs │ │ │ ├── DataAttributeShouldBeUsedOnATheoryTests.cs │ │ │ ├── DoNotUseAsyncVoidForTestMethodsTests.cs │ │ │ ├── DoNotUseBlockingTaskOperationsTests.cs │ │ │ ├── DoNotUseConfigureAwaitTests.cs │ │ │ ├── EnsureFixturesHaveASourceTests.cs │ │ │ ├── FactMethodMustNotHaveParametersTests.cs │ │ │ ├── FactMethodShouldNotHaveTestDataTests.cs │ │ │ ├── InlineDataMustMatchTheoryParametersTests.cs │ │ │ ├── InlineDataShouldBeUniqueWithinTheoryTests.cs │ │ │ ├── LocalFunctionsCannotBeTestFunctionsTests.cs │ │ │ ├── MemberDataShouldReferenceValidMemberTests.cs │ │ │ ├── PublicMethodShouldBeMarkedAsTestTests.cs │ │ │ ├── TestClassCannotBeNestedInGenericClassTests.cs │ │ │ ├── TestClassMustBePublicTests.cs │ │ │ ├── TestClassShouldHaveTFixtureArgumentTests.cs │ │ │ ├── TestMethodCannotHaveOverloadsTests.cs │ │ │ ├── TestMethodMustNotHaveMultipleFactAttributesTests.cs │ │ │ ├── TestMethodShouldNotBeSkippedTests.cs │ │ │ ├── TestMethodSupportedReturnTypeTests.cs │ │ │ ├── TheoryDataRowArgumentsShouldBeSerializableTests.cs │ │ │ ├── TheoryDataShouldNotUseTheoryDataRowTests.cs │ │ │ ├── TheoryDataTypeArgumentsShouldBeSerializableTests.cs │ │ │ ├── TheoryMethodCannotHaveDefaultParameterTests.cs │ │ │ ├── TheoryMethodCannotHaveParamsArrayTests.cs │ │ │ ├── TheoryMethodMustHaveTestDataTests.cs │ │ │ ├── TheoryMethodShouldHaveParametersTests.cs │ │ │ ├── TheoryMethodShouldUseAllParametersTests.cs │ │ │ └── UseCancellationTokenTests.cs │ │ ├── X2000/ │ │ │ ├── AssertCollectionContainsShouldNotUseBoolCheckTests.cs │ │ │ ├── AssertEmptyCollectionCheckShouldNotBeUsedTests.cs │ │ │ ├── AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksTests.cs │ │ │ ├── AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckTests.cs │ │ │ ├── AssertEqualGenericShouldNotBeUsedForStringValueTests.cs │ │ │ ├── AssertEqualLiteralValueShouldBeFirstTests.cs │ │ │ ├── AssertEqualPrecisionShouldBeInRangeTest.cs │ │ │ ├── AssertEqualShouldNotBeUsedForBoolLiteralCheckTests.cs │ │ │ ├── AssertEqualShouldNotBeUsedForCollectionSizeCheckTests.cs │ │ │ ├── AssertEqualShouldNotBeUsedForNullCheckTests.cs │ │ │ ├── AssertEqualsShouldNotBeUsedTests.cs │ │ │ ├── AssertIsTypeShouldNotBeUsedForAbstractTypeTests.cs │ │ │ ├── AssertIsTypeShouldUseGenericOverloadTypeTests.cs │ │ │ ├── AssertNullShouldNotBeCalledOnValueTypesTests.cs │ │ │ ├── AssertRegexMatchShouldNotUseBoolLiteralCheckTests.cs │ │ │ ├── AssertSameShouldNotBeCalledOnValueTypesTests.cs │ │ │ ├── AssertSingleShouldBeUsedForSingleParameterTests.cs │ │ │ ├── AssertSingleShouldUseTwoArgumentCallTests.cs │ │ │ ├── AssertStringEqualityCheckShouldNotUseBoolCheckTest.cs │ │ │ ├── AssertSubstringCheckShouldNotUseBoolCheckTests.cs │ │ │ ├── AssertThrowsShouldNotBeUsedForAsyncThrowsCheckTests.cs │ │ │ ├── AssertThrowsShouldUseGenericOverloadCheckTests.cs │ │ │ ├── AssignableFromAssertionIsConfusinglyNamedTests.cs │ │ │ ├── AsyncAssertsShouldBeAwaitedTests.cs │ │ │ ├── BooleanAssertsShouldNotBeNegatedTests.cs │ │ │ ├── BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckTests.cs │ │ │ ├── DoNotUseAssertEmptyWithProblematicTypesTests.cs │ │ │ ├── SetEqualityAnalyzerTests.cs │ │ │ └── UseAssertFailInsteadOfBooleanAssertTests.cs │ │ └── X3000/ │ │ ├── CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.cs │ │ ├── DoNotTestForConcreteTypeOfJsonSerializableTypesTests.cs │ │ ├── FactAttributeDerivedClassesShouldProvideSourceInformationConstructorTests.cs │ │ └── SerializableClassMustHaveParameterlessConstructorTests.cs │ ├── Fixes/ │ │ ├── X1000/ │ │ │ ├── ClassDataAttributeMustPointAtValidClassFixerTests.cs │ │ │ ├── CollectionDefinitionClassesMustBePublicFixerTests.cs │ │ │ ├── ConvertToFactFixTests.cs │ │ │ ├── ConvertToTheoryFixTests.cs │ │ │ ├── DataAttributeShouldBeUsedOnATheoryFixerTests.cs │ │ │ ├── DoNotUseAsyncVoidForTestMethodsFixerTests.cs │ │ │ ├── DoNotUseConfigureAwaitFixerTests.cs │ │ │ ├── FactMethodMustNotHaveParametersFixerTests.cs │ │ │ ├── FactMethodShouldNotHaveTestDataFixerTests.cs │ │ │ ├── InlineDataMustMatchTheoryParameters_ExtraValueFixerTests.cs │ │ │ ├── InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameterFixerTests.cs │ │ │ ├── InlineDataMustMatchTheoryParameters_TooFewValuesFixerTests.cs │ │ │ ├── InlineDataShouldBeUniqueWithinTheoryFixerTests.cs │ │ │ ├── LocalFunctionsCannotBeTestFunctionsFixerTests.cs │ │ │ ├── MemberDataShouldReferenceValidMember_ExtraValueFixerTests.cs │ │ │ ├── MemberDataShouldReferenceValidMember_NameOfFixerTests.cs │ │ │ ├── MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixerTests.cs │ │ │ ├── MemberDataShouldReferenceValidMember_ParamsForNonMethodFixerTests.cs │ │ │ ├── MemberDataShouldReferenceValidMember_ReturnTypeFixerTests.cs │ │ │ ├── MemberDataShouldReferenceValidMember_StaticFixerTests.cs │ │ │ ├── MemberDataShouldReferenceValidMember_VisibilityFixerTests.cs │ │ │ ├── PublicMethodShouldBeMarkedAsTestFixerTests.cs │ │ │ ├── RemoveMethodParameterFixTests.cs │ │ │ ├── TestClassCannotBeNestedInGenericClassFixerTests.cs │ │ │ ├── TestClassMustBePublicFixerTests.cs │ │ │ ├── TestClassShouldHaveTFixtureArgumentFixerTests.cs │ │ │ ├── TestMethodMustNotHaveMultipleFactAttributesFixerTests.cs │ │ │ ├── TestMethodShouldNotBeSkippedFixerTests.cs │ │ │ ├── TheoryDataShouldNotUseTheoryDataRowFixerTests.cs │ │ │ ├── TheoryMethodCannotHaveDefaultParameterFixerTests.cs │ │ │ └── UseCancellationTokenFixerTests.cs │ │ ├── X2000/ │ │ │ ├── AssertCollectionContainsShouldNotUseBoolCheckFixerTests.cs │ │ │ ├── AssertEmptyCollectionCheckShouldNotBeUsedFixerTests.cs │ │ │ ├── AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixerTests.cs │ │ │ ├── AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixerTests.cs │ │ │ ├── AssertEqualGenericShouldNotBeUsedForStringValueFixerTests.cs │ │ │ ├── AssertEqualLiteralValueShouldBeFirstFixerTests.cs │ │ │ ├── AssertEqualPrecisionShouldBeInRangeFixerTests.cs │ │ │ ├── AssertEqualShouldNotBeUsedForBoolLiteralCheckFixerTests.cs │ │ │ ├── AssertEqualShouldNotBeUsedForCollectionSizeCheckFixerTests.cs │ │ │ ├── AssertEqualShouldNotBeUsedForNullCheckFixerTests.cs │ │ │ ├── AssertEqualsShouldNotBeUsedFixerTests.cs │ │ │ ├── AssertIsTypeShouldNotBeUsedForAbstractTypeFixerTests.cs │ │ │ ├── AssertNullShouldNotBeCalledOnValueTypesFixerTests.cs │ │ │ ├── AssertRegexMatchShouldNotUseBoolLiteralCheckFixerTests.cs │ │ │ ├── AssertSameShouldNotBeCalledOnValueTypesFixerTests.cs │ │ │ ├── AssertSingleShouldBeUsedForSingleParameterFixerTests.cs │ │ │ ├── AssertSingleShouldUseTwoArgumentCallFixerTests.cs │ │ │ ├── AssertStringEqualityCheckShouldNotUseBoolCheckFixerTests.cs │ │ │ ├── AssertSubstringCheckShouldNotUseBoolCheckFixerTests.cs │ │ │ ├── AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixerTests.cs │ │ │ ├── AssignableFromAssertionIsConfusinglyNamedFixerTests.cs │ │ │ ├── AsyncAssertsShouldBeAwaitedFixerTests.cs │ │ │ ├── BooleanAssertsShouldNotBeNegatedFixerTests.cs │ │ │ ├── BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixerTests.cs │ │ │ ├── BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixerTests.cs │ │ │ ├── UseAssertFailInsteadOfBooleanAssertFixerTests.cs │ │ │ └── UseGenericOverloadFixTests.cs │ │ └── X3000/ │ │ ├── CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixerTests.cs │ │ └── SerializableClassMustHaveParameterlessConstructorFixerTests.cs │ ├── Suppressors/ │ │ ├── ConsiderCallingConfigureAwaitSuppressorTests.cs │ │ ├── MakeTypesInternalSuppressorTests.cs │ │ ├── NonNullableFieldInitializationSuppressorTests.cs │ │ └── UseAsyncSuffixForAsyncMethodsSuppressorTests.cs │ ├── Utility/ │ │ ├── 3rdPartyAnalyzers/ │ │ │ ├── AnalyzerLoaderBase.cs │ │ │ ├── CodeAnalysisNetAnalyzers.cs │ │ │ └── VsThreadingAnalyzers.cs │ │ ├── AssertsExtensions/ │ │ │ ├── EmptyException.cs │ │ │ ├── EqualException.cs │ │ │ └── NotEmptyException.cs │ │ ├── CSharpVerifier.Analyzers.RunnerUtility.cs │ │ ├── CSharpVerifier.Analyzers.cs │ │ ├── CSharpVerifier.CodeFixes.RunnerUtility.cs │ │ ├── CSharpVerifier.CodeFixes.cs │ │ ├── CSharpVerifier.Suppressors.cs │ │ ├── CSharpVerifier.cs │ │ ├── CodeAnalyzerHelper.cs │ │ ├── CodeFixProviderDiscovery.cs │ │ ├── XunitVerifier.cs │ │ ├── XunitVerifierV2.cs │ │ └── XunitVerifierV3.cs │ ├── xunit.analyzers.tests.csproj │ └── xunit.runner.json ├── tools/ │ ├── SignClient/ │ │ └── appsettings.json │ └── builder/ │ ├── .vscode/ │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── Program.cs │ ├── build.csproj │ ├── models/ │ │ └── BuildContext.cs │ └── targets/ │ ├── Build.cs │ ├── Packages.cs │ ├── SignAssemblies.cs │ └── Test.cs ├── version.json └── xunit.analyzers.sln ================================================ FILE CONTENTS ================================================ ================================================ FILE: .config/dotnet-tools.json ================================================ { "version": 1, "isRoot": true, "tools": { "sign": { "version": "0.9.1-beta.25330.2", "commands": [ "sign" ], "rollForward": false } } } ================================================ FILE: .editorconfig ================================================ # top-most EditorConfig file root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true indent_style = tab [*.{ps1,sln}] end_of_line = crlf # Visual Studio demands 2-spaced project files # Tabs are not legal whitespace for YAML files [*.{csproj,json,props,targets,xslt,yaml,yml}] indent_style = space indent_size = 2 [*.cs] # Organize usings dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = true file_header_template = unset # this. and Me. preferences dotnet_style_qualification_for_event = false dotnet_style_qualification_for_field = false dotnet_style_qualification_for_method = false dotnet_style_qualification_for_property = false # Language keywords vs BCL types preferences dotnet_style_predefined_type_for_locals_parameters_members = true dotnet_style_predefined_type_for_member_access = true # Parentheses preferences dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity dotnet_style_parentheses_in_other_binary_operators = always_for_clarity dotnet_style_parentheses_in_other_operators = never_if_unnecessary dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity # Modifier preferences dotnet_style_require_accessibility_modifiers = for_non_interface_members # Expression-level preferences dotnet_style_coalesce_expression = true dotnet_style_collection_initializer = true dotnet_style_explicit_tuple_names = true dotnet_style_namespace_match_folder = false dotnet_style_null_propagation = true dotnet_style_object_initializer = true dotnet_style_operator_placement_when_wrapping = beginning_of_line dotnet_style_prefer_auto_properties = true dotnet_style_prefer_compound_assignment = true dotnet_style_prefer_conditional_expression_over_assignment = true dotnet_style_prefer_conditional_expression_over_return = false dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed dotnet_style_prefer_inferred_anonymous_type_member_names = true dotnet_style_prefer_inferred_tuple_names = true dotnet_style_prefer_is_null_check_over_reference_equality_method = true dotnet_style_prefer_simplified_boolean_expressions = true dotnet_style_prefer_simplified_interpolation = true # Field preferences dotnet_style_readonly_field = true # Parameter preferences dotnet_code_quality_unused_parameters = all # Suppression preferences dotnet_remove_unnecessary_suppression_exclusions = none # New line preferences dotnet_style_allow_multiple_blank_lines_experimental = true dotnet_style_allow_statement_immediately_after_block_experimental = true #### C# Coding Conventions #### # var preferences csharp_style_var_elsewhere = true csharp_style_var_for_built_in_types = true csharp_style_var_when_type_is_apparent = true # Expression-bodied members csharp_style_expression_bodied_accessors = true csharp_style_expression_bodied_constructors = false csharp_style_expression_bodied_indexers = true csharp_style_expression_bodied_lambdas = true csharp_style_expression_bodied_local_functions = false csharp_style_expression_bodied_methods = false csharp_style_expression_bodied_operators = false csharp_style_expression_bodied_properties = true # Pattern matching preferences csharp_style_pattern_matching_over_as_with_null_check = true csharp_style_pattern_matching_over_is_with_cast_check = true csharp_style_prefer_extended_property_pattern = true csharp_style_prefer_not_pattern = true csharp_style_prefer_pattern_matching = true csharp_style_prefer_switch_expression = true # Null-checking preferences csharp_style_conditional_delegate_call = true # Modifier preferences csharp_prefer_static_local_function = true csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async # Code-block preferences csharp_prefer_braces = false csharp_prefer_simple_using_statement = true csharp_style_namespace_declarations = file_scoped # Expression-level preferences csharp_prefer_simple_default_expression = true csharp_style_deconstructed_variable_declaration = true csharp_style_implicit_object_creation_when_type_is_apparent = true csharp_style_inlined_variable_declaration = true csharp_style_pattern_local_over_anonymous_function = true csharp_style_prefer_index_operator = true csharp_style_prefer_null_check_over_type_check = true csharp_style_prefer_range_operator = true csharp_style_throw_expression = true csharp_style_unused_value_assignment_preference = discard_variable csharp_style_unused_value_expression_statement_preference = discard_variable # 'using' directive preferences csharp_using_directive_placement = outside_namespace:warning # New line preferences csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true csharp_style_allow_embedded_statements_on_same_line_experimental = true #### C# Formatting Rules #### # New line preferences csharp_new_line_before_catch = true csharp_new_line_before_else = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_open_brace = all csharp_new_line_between_query_expression_clauses = true # Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_case_contents_when_block = true csharp_indent_labels = one_less_than_current csharp_indent_switch_labels = true # Space preferences csharp_space_after_cast = false csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_comma = true csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after csharp_space_around_declaration_statements = false csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false csharp_space_before_open_square_brackets = false csharp_space_before_semicolon_in_for_statement = false csharp_space_between_empty_square_brackets = false csharp_space_between_method_call_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false # Wrapping preferences csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true #### Naming styles #### # Naming rules dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion dotnet_naming_rule.types_should_be_pascal_case.symbols = types dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case # Symbol specifications dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.non_field_members.required_modifiers = # Naming styles dotnet_naming_style.pascal_case.required_prefix = dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.begins_with_i.required_prefix = I dotnet_naming_style.begins_with_i.required_suffix = dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case #### Roslyn diagnostics #### dotnet_diagnostic.CA1000.severity = none # Do not declare static members on generic types dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists dotnet_diagnostic.CA1014.severity = none # Mark assemblies with CLSCompliantAttribute dotnet_diagnostic.CA1034.severity = none # Do not nest types dotnet_diagnostic.CA1050.severity = none # Declare types in namespaces dotnet_diagnostic.CA1200.severity = none # Avoid using cref tags with a prefix dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters dotnet_diagnostic.CA1707.severity = none # Remove the underscores from type name dotnet_diagnostic.CA1720.severity = none # Identifier contains type name dotnet_diagnostic.CA1724.severity = none # Type names should not match namespaces dotnet_diagnostic.CA1859.severity = none # Use concrete types when possible for improved performance dotnet_diagnostic.CA1861.severity = none # Avoid constant arrays as arguments dotnet_diagnostic.CA2211.severity = none # Non-constant fields should not be visible dotnet_diagnostic.CA2241.severity = error # Provide correct arguments to formatting methods/The format argument is not a valid string dotnet_diagnostic.CS1591.severity = none # Missing XML comment dotnet_diagnostic.IDE0010.severity = none # Populate switch dotnet_diagnostic.IDE0021.severity = none # Use block body for method dotnet_diagnostic.IDE0022.severity = none # Use block body for method dotnet_diagnostic.IDE0040.severity = none # Add accessibility modifiers dotnet_diagnostic.IDE0058.severity = none # Remove unnecessary expression value dotnet_diagnostic.IDE0072.severity = none # Populate switch dotnet_diagnostic.IDE1006.severity = none # Naming rule violation ================================================ FILE: .gitattributes ================================================ * text=auto eol=lf *.cs text diff=csharp *.csproj text merge=union *.ico binary *.resx text merge=union *.ps1 eol=crlf *.sln text eol=crlf merge=union *.snk binary *.vbproj text merge=union *.xls binary ================================================ FILE: .github/FUNDING.yml ================================================ github: xunit ================================================ FILE: .github/workflows/ci-signed.yaml ================================================ name: xUnit.net Analyzers CI Build (signed) on: push: branches: - main - 'rel/**' workflow_dispatch: jobs: deployment: name: "Build" runs-on: windows-latest environment: signing permissions: id-token: write # Required for Azure CLI Login env: DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE: true DOTNET_NOLOGO: true steps: - name: Clone source uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Install .NET SDK uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 10.0.x - name: Get .NET information run: dotnet --info - name: "Build target: BuildAll" run: dotnet run --project tools/builder --no-launch-profile -- BuildAll --timing - name: Login to Azure CLI uses: azure/login@v2 with: client-id: ${{ vars.KEYVAULT_APP_ID }} tenant-id: ${{ vars.KEYVAULT_TENANT_ID }} subscription-id: ${{ vars.KEYVAULT_SUBSCRIPTION_ID }} - name: "Build target: PublishPackages" env: PUSH_APIKEY: ${{ secrets.FEEDZ_PUSH_KEY }} PUSH_URI: ${{ vars.FEEDZ_PUSH_URL }} SIGN_APP_ID: ${{ vars.KEYVAULT_APP_ID }} SIGN_CERT_NAME: ${{ vars.KEYVAULT_CERT_NAME }} SIGN_TIMESTAMP_URI: ${{ vars.KEYVAULT_TIMESTAMP_URL }} SIGN_VAULT_URI: ${{ vars.KEYVAULT_URL }} run: dotnet run --project tools/builder --no-launch-profile -- PublishPackages --timing - name: "Upload artifact: build" uses: actions/upload-artifact@v4 with: name: build path: artifacts/build compression-level: 9 if: always() - name: "Upload artifact: test" uses: actions/upload-artifact@v4 with: name: test path: artifacts/test compression-level: 9 if: always() - name: "Upload artifact: packages" uses: actions/upload-artifact@v4 with: name: packages path: artifacts/packages compression-level: 0 if: always() - name: Publish Test Report uses: ctrf-io/github-test-reporter@v1 with: report-path: './artifacts/test/*.ctrf' github-report: true if: always() ================================================ FILE: .github/workflows/ci-unsigned.yaml ================================================ name: xUnit.net Analyzers CI Build (unsigned) on: push: branches-ignore: - main - 'rel/**' workflow_dispatch: jobs: build: name: "Build" runs-on: windows-latest env: DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE: true DOTNET_NOLOGO: true steps: - name: Clone source uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Install .NET SDK uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 10.0.x - name: Get .NET information run: dotnet --info - name: "Build target: BuildAll" run: dotnet run --project tools/builder --no-launch-profile -- BuildAll --timing - name: "Upload artifact: test" uses: actions/upload-artifact@v4 with: name: test path: artifacts/test compression-level: 9 if: always() - name: "Upload artifact: packages" uses: actions/upload-artifact@v4 with: name: packages path: artifacts/packages compression-level: 0 if: always() - name: Publish Test Report uses: ctrf-io/github-test-reporter@v1 with: report-path: './artifacts/test/*.ctrf' github-report: true if: always() ================================================ FILE: .github/workflows/pull-request.yaml ================================================ name: xUnit.net Analyzers PR Build on: - pull_request - workflow_dispatch jobs: build: name: "Build" runs-on: ${{ matrix.os }} env: DOTNET_CLI_WORKLOAD_UPDATE_NOTIFY_DISABLE: true DOTNET_NOLOGO: true strategy: fail-fast: false matrix: os: [windows-latest, ubuntu-latest, macOS-latest] steps: - name: Clone source uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Install .NET SDK uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x 10.0.x - name: Get .NET information run: dotnet --info - name: Install Mono (Ubuntu) run: > sudo apt-get -y install apt-transport-https dirmngr gnupg ca-certificates software-properties-common && curl 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3fa7e0328081bff6a14da29aa6a19b38d3d831ef' | sudo tee /etc/apt/trusted.gpg.d/mono-official-stable.asc && sudo apt-add-repository -y --no-update 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' && sudo apt-get update && sudo apt-get -y install mono-complete mono-vbnc if: ${{ matrix.os == 'ubuntu-latest' }} - name: Install Mono (macOS) run: brew install mono if: ${{ matrix.os == 'macOS-latest' }} - name: Get Mono information run: mono --version if: ${{ matrix.os != 'windows-latest' }} - name: "Build target: BuildAll" run: dotnet run --project tools/builder --no-launch-profile -- BuildAll - name: "Upload artifact: test-${{ matrix.os }}" uses: actions/upload-artifact@v4 with: name: test-${{ matrix.os }} path: artifacts/test compression-level: 9 if: always() - name: Publish Test Report uses: ctrf-io/github-test-reporter@v1 with: report-path: './artifacts/test/*.ctrf' github-report: true pull-request: true update-comment: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} if: always() ================================================ FILE: .gitignore ================================================ # Visual Studio launchSettings.json # NCrunch *.ncrunchsolution *.ncrunchsolution.user *.ncrunchproject # Mono's local registry .mono ##### Copied from https://github.com/github/gitignore/blob/7b22f8ab6c85b4ef1469d72a8ba96462e2a44853/VisualStudio.gitignore ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Mono auto generated files mono_crash.* # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUnit *.VisualState.xml TestResult.xml nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.tlog *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool coverage*.json coverage*.xml coverage*.info # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # NuGet Symbol Packages *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio 6 auto-generated project file (contains which files were open etc.) *.vbp # Visual Studio 6 workspace and project file (working project files containing files to include in project) *.dsw *.dsp # Visual Studio 6 technical files *.ncb *.aps # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # Visual Studio History (VSHistory) files .vshistory/ # BeatPulse healthcheck temp database healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd # VS Code files for those working on multiple tools .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json *.code-workspace # Local History for Visual Studio Code .history/ # Windows Installer files from build outputs *.cab *.msi *.msix *.msm *.msp # JetBrains Rider *.sln.iml .idea/ ================================================ FILE: .gitmodules ================================================ [submodule "tools/media"] path = tools/media url = https://github.com/xunit/media [submodule "tools/builder/common"] path = tools/builder/common url = https://github.com/xunit/build-tools-v3 ================================================ FILE: .vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [] } ================================================ FILE: .vscode/settings.json ================================================ { "cSpell.words": [ "LLMBRO", "MBRO", "app", "nuget", "nupkg", "parallelization", "veyor" ], "files.exclude": { "**/.git": true, "**/.DS_Store": true, "**/bin": true, "**/obj": true, "artifacts": true, "packages": true } } ================================================ FILE: .vscode/tasks.json ================================================ { "version": "2.0.0", "tasks": [ { "label": "Build", "type": "process", "command": "dotnet", "args": [ "run", "--project", "tools/builder", "--no-launch-profile", "--", "Build" ], "options": { "cwd": "${workspaceRoot}" }, "group": "build", "presentation": { "focus": true }, "problemMatcher": [] }, { "label": "Pre-PR Validation", "type": "process", "command": "dotnet", "args": [ "run", "--project", "tools/builder", "--no-launch-profile", "--", "Packages" ], "options": { "cwd": "${workspaceRoot}" }, "group": "build", "presentation": { "focus": true }, "problemMatcher": [] }, { "label": "Unit Tests (.NET Core)", "type": "process", "command": "dotnet", "args": [ "run", "--project", "tools/builder", "--no-launch-profile", "--", "TestCore" ], "options": { "cwd": "${workspaceRoot}" }, "group": "build", "presentation": { "focus": true }, "problemMatcher": [] }, { "label": "Unit Tests (.NET Framework)", "type": "process", "command": "dotnet", "args": [ "run", "--project", "tools/builder", "--no-launch-profile", "--", "TestFx" ], "options": { "cwd": "${workspaceRoot}" }, "group": "build", "presentation": { "focus": true }, "problemMatcher": [] } ] } ================================================ FILE: BUILDING.md ================================================ # Building xUnit.net Analyzers The primary build system for xUnit.net Analyzers is done via command line, and officially supports Linux and Windows. Users running macOS can generally follow the Linux instructions (while installing the macOS equivalents of the dependencies). # Pre-Requisites You will need the following software installed (regardless of OS): * [.NET SDK 8.0](https://dotnet.microsoft.com/download/dotnet/8.0) * [git](https://git-scm.com/downloads) ## Linux Pre-Requisites Linux users will additionally need: * [Mono](https://www.mono-project.com/download/stable/) 6.12+ * [bash](https://www.gnu.org/software/bash/) Note: Linux users cannot run the .NET Framework tests, as they are incompatible. For this reason, we recommend that users either work primarily in Windows, or verify their tests work as expected in a Windows VM, before submitting PRs. ## Windows Pre-Requisites Windows users will additionally need: * .NET Framework 4.7.2 or later (part of the Windows OS) * [PowerShell 7+](https://learn.microsoft.com/powershell/scripting/install/installing-powershell-on-windows) Ensure that you have configured PowerShell to be able to run local unsigned scripts (either by running `Set-ExecutionPolicy RemoteSigned` from within PowerShell, or by launching PowerShell with the `-ExecutionPolicy RemoteSigned` command line switch). _Note that the built-in version of PowerShell may work, but is unsupported by us. If you have PowerShell-related issues, please make sure you have installed PowerShell 7+ and the command prompt you opened is for PowerShell 7+, and not the built-in version of PowerShell._ # Command-Line Build 1. **Linux users:** Open a terminal to your favorite shell. **Windows users:** Open PowerShell 7+. 1. From the root folder of the source repo, this command will build the code & run all tests: `./build` To build a specific target (or multiple targets): `./build [target [target...]]` The common targets (case-insensitive) include: * `Restore`: Perform package restore * `Build`: Build the source * `Test`: Run all unit tests You can get a list of options: `./build --help` # Editing source The primary projects for editing are: * `xunit.analyzers` (for code analysis) * `xunit.analyzers.fixes` (for automated fixes for issues raised in code analysis) * `xunit.analyzers.tests` (for unit tests of both above projects) These are targeting our lowest common denominator for Roslyn (current version 3.11, the version that's supported in Visual Studio 2019 16.11). There are also three projects which build against the latest version of Roslyn: * `xunit.analyzers.latest` * `xunit.analyzers.latest.fixes` * `xunit.analyzers.latest.tests` When running a command line build, we run a matrix of 4 test projects: Roslyn 3.11 vs. latest, and .NET Framework vs. .NET. It's important that you run `./build` (or `./build test`) from Windows before submitting PRs, because some bugs are often found only in one of the four combinations (and Mono cannot run the .NET Framework tests). You will also occasionally see tests which only run in specific environments. Common `#if` statements you may see (or may need to use) include: * `#if NETFRAMEWORK` (only runs for .NET Framework) * `#if NETCOREAPP` (only runs for .NET) * `#if ROSLYN_LATEST` (only runs with latest Roslyn, which includes getting analysis test support to C# language > version 9) In production code, we try to minimize these when possible, and prefer to fall back to use dynamic runtime environment detection when we can (as we'd like to light up features in newer versions of Roslyn when available). While this isn't always possible, it is generally a goal we try to achieve. In test code, we tend to use these to more frequently to ensure we have complete coverage of features that should be available dynamically (whether they are lit up based on `#if` or by runtime environment detection). ================================================ FILE: LICENSE ================================================ Unless otherwise noted, the source code here is covered by the following license: Copyright (c) .NET Foundation and Contributors All Rights Reserved Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. The source in src/xunit.analyzers.tests/Utilities/XunitVerifier.cs was adapted from code that is covered the following license (copied from https://github.com/dotnet/roslyn-sdk/blob/35d5e46fd5c403194692c645d912a17d36ed74f5/LICENSE.txt): The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: NuGet.Config ================================================ ================================================ FILE: README.md ================================================ # About This Project This project contains source code analysis and cleanup rules for xUnit.net. Analysis and fixes are only supported with C#. **Requirements**: xUnit.net v2 or v3. Supported in Visual Studio 2019, 2022, and 2026 (as well as via command line builds with Roslyn 3.11+). Other environments (such as Mono or JetBrains Rider) may be able to use these analyzers as well; support and issue resolution will be provided by those third parties and not by xUnit.net itself. **Documentation**: a list of supported rules is available at https://xunit.net/xunit.analyzers/rules/ **Bugs and issues**: please visit the [core xUnit.net project issue tracker](https://github.com/xunit/xunit/issues). **Building**: see [BUILDING.md](https://github.com/xunit/xunit.analyzers/blob/main/BUILDING.md). > _**Note:** Our minimum supported versions of Visual Studio 2019, 2022, and 2026 align with the "Baseline" column of the "Support for Older Versions" table of [Visual Studio Product Lifecycle and Servicing](https://learn.microsoft.com/visualstudio/releases/2026/servicing-vs#support-for-older-versions), as these are the minimum versions supported by Microsoft. If you are experiencing issues with an older version of Visual Studio, please upgrade and verify the issue still exists before opening issues._ ## How to install - xUnit.net v3: the analyzer package is referenced by the main [`xunit.v3` NuGet package](https://www.nuget.org/packages/xunit.v3) out of the box. If you choose to reference [`xunit.v3.core`](https://www.nuget.org/packages/xunit.v3.core) instead, you can reference [`xunit.analyzers`](https://www.nuget.org/packages/xunit.analyzers) explicitly. - xUnit.net v2 2.3.0 and higher: the analyzer package is referenced by the main [`xunit` NuGet package](https://www.nuget.org/packages/xunit) out of the box. If you choose to reference [`xunit.core`](https://www.nuget.org/packages/xunit.core) instead, you can reference [`xunit.analyzers`](https://www.nuget.org/packages/xunit.analyzers) explicitly. - xUnit.net v2 2.2.0 and earlier: you have to install the [`xunit.analyzers` NuGet package](https://www.nuget.org/packages/xunit.analyzers) explicitly. ## How to uninstall - If you are using xUnit.net v3 and do not wish to use the analyzers package, replace the package reference to [`xunit.v3`](https://www.nuget.org/packages/xunit.v3) with the corresponding versions of [`xunit.v3.core`](https://www.nuget.org/packages/xunit.v3.core) and [`xunit.v3.assert`](https://www.nuget.org/packages/xunit.v3.assert). - If you are using xUnit.net v2 2.3.0 or higher and do not wish to use the analyzers package, replace the package reference to [`xunit`](https://www.nuget.org/packages/xunit) with the corresponding versions of [`xunit.core`](https://www.nuget.org/packages/xunit.core) and [`xunit.assert`](https://www.nuget.org/packages/xunit.assert). - If you are using xUnit.net v2 v2.2.0 or earlier: remove the reference to the [`xunit.analyzers` NuGet package](https://www.nuget.org/packages/xunit.analyzers). ## Analysis and Code Fix in Action ![Analyzer in action animation](https://cloud.githubusercontent.com/assets/607223/25752060/fb4af444-316b-11e7-9e7c-fc69ade132fb.gif) # About xUnit.net xUnit.net is a free, open source, community-focused unit testing tool for C#, F#, and Visual Basic. xUnit.net works with the [.NET SDK](https://dotnet.microsoft.com/download) command line tools, [Visual Studio](https://visualstudio.microsoft.com/), [Visual Studio Code](https://code.visualstudio.com/), [JetBrains Rider](https://www.jetbrains.com/rider/), [NCrunch](https://www.ncrunch.net/), and any development environment compatible with [Microsoft Testing Platform](https://learn.microsoft.com/dotnet/core/testing/microsoft-testing-platform-intro) (xUnit.net v3) or [VSTest](https://github.com/microsoft/vstest) (all versions of xUnit.net). xUnit.net is part of the [.NET Foundation](https://www.dotnetfoundation.org/) and operates under their [code of conduct](https://www.dotnetfoundation.org/code-of-conduct). It is licensed under [Apache 2](https://opensource.org/licenses/Apache-2.0) (an OSI approved license). The project is [governed](https://xunit.net/governance) by a Project Lead. For project documentation, please visit the [xUnit.net project home](https://xunit.net/). * _New to xUnit.net? Get started with the [.NET SDK](https://xunit.net/docs/getting-started/v3/getting-started)._ * _Need some help building the source? See [BUILDING.md](https://github.com/xunit/xunit/tree/main/BUILDING.md)._ * _Want to contribute to the project? See [CONTRIBUTING.md](https://github.com/xunit/.github/tree/main/CONTRIBUTING.md)._ * _Want to contribute to the assertion library? See the [suggested contribution workflow](https://github.com/xunit/assert.xunit/tree/main/README.md#suggested-contribution-workflow) in the assertion library project, as it is slightly more complex due to code being spread across two GitHub repositories._ ## Latest Builds | | Latest stable | Latest CI ([how to use](https://xunit.net/docs/using-ci-builds)) | Build status | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | `xunit.v3` | [![](https://img.shields.io/nuget/v/xunit.v3.svg?logo=nuget)](https://www.nuget.org/packages/xunit.v3) | [![](https://img.shields.io/endpoint.svg?url=https://f.feedz.io/xunit/xunit/shield/xunit.v3/latest&logo=nuget&color=f58142)](https://feedz.io/org/xunit/repository/xunit/packages/xunit.v3) | [![](https://img.shields.io/endpoint.svg?url=https://actions-badge.atrox.dev/xunit/xunit/badge%3Fref%3Dmain&label=build)](https://actions-badge.atrox.dev/xunit/xunit/goto?ref=main) | `xunit` | [![](https://img.shields.io/nuget/v/xunit.svg?logo=nuget)](https://www.nuget.org/packages/xunit) | [![](https://img.shields.io/endpoint.svg?url=https://f.feedz.io/xunit/xunit/shield/xunit/latest&logo=nuget&color=f58142)](https://feedz.io/org/xunit/repository/xunit/packages/xunit) | [![](https://img.shields.io/endpoint.svg?url=https://actions-badge.atrox.dev/xunit/xunit/badge%3Fref%3Dv2&label=build)](https://actions-badge.atrox.dev/xunit/xunit/goto?ref=v2) | `xunit.analyzers` | [![](https://img.shields.io/nuget/v/xunit.analyzers.svg?logo=nuget)](https://www.nuget.org/packages/xunit.analyzers) | [![](https://img.shields.io/endpoint.svg?url=https://f.feedz.io/xunit/xunit/shield/xunit.analyzers/latest&logo=nuget&color=f58142)](https://feedz.io/org/xunit/repository/xunit/packages/xunit.analyzers) | [![](https://img.shields.io/endpoint.svg?url=https://actions-badge.atrox.dev/xunit/xunit.analyzers/badge%3Fref%3Dmain&label=build)](https://actions-badge.atrox.dev/xunit/xunit.analyzers/goto?ref=main) | `xunit.runner.visualstudio` | [![](https://img.shields.io/nuget/v/xunit.runner.visualstudio.svg?logo=nuget)](https://www.nuget.org/packages/xunit.runner.visualstudio) | [![](https://img.shields.io/endpoint.svg?url=https://f.feedz.io/xunit/xunit/shield/xunit.runner.visualstudio/latest&logo=nuget&color=f58142)](https://feedz.io/org/xunit/repository/xunit/packages/xunit.runner.visualstudio) | [![](https://img.shields.io/endpoint.svg?url=https://actions-badge.atrox.dev/xunit/visualstudio.xunit/badge%3Fref%3Dmain&label=build)](https://actions-badge.atrox.dev/xunit/visualstudio.xunit/goto?ref=main) *For complete CI package lists, please visit the [feedz.io package search](https://feedz.io/org/xunit/repository/xunit/search). A free login is required.* ## Sponsors Help support this project by becoming a sponsor through [GitHub Sponsors](https://github.com/sponsors/xunit). ================================================ FILE: build ================================================ #!/usr/bin/env bash set -euo pipefail PUSHED=0 cleanup () { if [[ $PUSHED == 1 ]]; then popd >/dev/null PUSHED=0 fi } trap cleanup EXIT ERR INT TERM if which git > /dev/null; then git submodule status | while read line; do if [ "$(echo $line | cut -b1)" == "-" ]; then pieces=( $line ) git submodule update --init ${pieces[1]} echo "" fi done else echo "error(1): Could not find 'git'; please install the Git CLI" 2>&1 exit 1 fi if which dotnet > /dev/null; then if [ $(dotnet --version | cut -d. -f1) -lt 8 ]; then echo "error(1): .NET SDK version $(dotnet --version) is too low; please install 8.0 or later" exit 1 fi if [ `uname -o` = Msys ] || which mono > /dev/null ; then pushd $( cd "$(dirname "$0")" ; pwd -P ) >/dev/null PUSHED=1 dotnet run --project tools/builder --no-launch-profile -- "$@" else echo "error(1): Could not find 'mono'; please install Mono" 2>&1 exit 1 fi else echo "error(1): Could not find 'dotnet'; please install the .NET Core SDK" 2>&1 exit 1 fi ================================================ FILE: build.ps1 ================================================ #!/usr/bin/env pwsh #Requires -Version 5.1 Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" if ($null -eq (Get-Command "git" -ErrorAction Ignore)) { throw "Could not find 'git'; please install the Git command line tooling" } & git submodule status | ForEach-Object { if ($_[0] -eq '-') { $pieces = $_.Split(' ') & git submodule update --init "$($pieces[1])" Write-Host "" } } if ($null -eq (Get-Command "dotnet" -ErrorAction Ignore)) { throw "Could not find 'dotnet'; please install the .NET Core SDK" } $version = [Version]$([regex]::matches((&dotnet --version), '^(\d+\.)?(\d+\.)?(\*|\d+)').value) if ($version.Major -lt 8) { throw ".NET SDK version ($version) is too low; please install version 8.0 or later" } Push-Location (Split-Path $MyInvocation.MyCommand.Definition) try { & dotnet run --project tools/builder --no-launch-profile -- $args } finally { Pop-Location } ================================================ FILE: global.json ================================================ { "sdk": { "version": "10.0.100", "rollForward": "latestMinor" } } ================================================ FILE: src/Directory.Build.props ================================================ 8.0.10 true embedded true false false false false false false false false true 14.0 3.11 false enable true runtime; build; native; contentfiles; analyzers; buildtransitive all latest-All $(MSBuildThisFileDirectory)signing.snk true true true true true true true true $(DefineConstants);XUNIT_NULLABLE;XUNIT_POINTERS;XUNIT_VISIBILITY_INTERNAL true true true ================================================ FILE: src/Directory.Build.targets ================================================ runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive all -dev $(BuildVersionSimple) $(BuildVersionSimple) $(BuildVersionSimple)$(PrereleaseVersion)$(PrereleaseSuffix)+$(GitCommitIdShort) signed\ -dev $(BuildVersionSimple)$(PrereleaseVersion)$(PrereleaseSuffix) https://xunit.net/releases/analyzers/$(PackageVersion) Configuration=$(Configuration); GitCommitId=$(GitCommitId); PackageVersion=$(PackageVersion); SignedPath=$(SignedPath); ================================================ FILE: src/common/CallerArgumentExpressionAttribute.cs ================================================ #if !NETCOREAPP namespace System.Runtime.CompilerServices; [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] internal sealed class CallerArgumentExpressionAttribute(string parameterName) : Attribute { public string ParameterName { get; } = parameterName; } #endif ================================================ FILE: src/common/Guard.cs ================================================ using System; using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace Xunit; /// /// Helper class for guarding value arguments and valid state. /// static class Guard { /// /// Ensures that a nullable reference type argument is not null. /// /// The argument type /// The value of the argument /// The name of the argument /// The argument value as a non-null value /// Thrown when the argument is null public static T ArgumentNotNull( [NotNull] T? argValue, [CallerArgumentExpression(nameof(argValue))] string? argName = null) where T : class { if (argValue is null) throw new ArgumentNullException(argName?.TrimStart('@')); return argValue; } /// /// Ensures that a nullable enumerable type argument is not null or empty. /// /// The argument type /// The value of the argument /// The name of the argument /// The argument value as a non-null, non-empty value /// Thrown when the argument is null or empty public static T ArgumentNotNullOrEmpty( [NotNull] T? argValue, [CallerArgumentExpression(nameof(argValue))] string? argName = null) where T : class, IEnumerable { ArgumentNotNull(argValue, argName); if (!argValue.GetEnumerator().MoveNext()) throw new ArgumentException("Argument was empty", argName?.TrimStart('@')); return argValue; } /// /// Ensures that an argument is valid. /// /// The exception message to use when the argument is not valid /// The validity test value /// The name of the argument /// The argument value as a non-null value /// Thrown when the argument is not valid public static void ArgumentValid( string message, bool test, string? argName = null) { if (!test) throw new ArgumentException(message, argName); } /// /// Ensures that an argument is valid. /// /// The creator for an exception message to use when the argument is not valid /// The validity test value /// The name of the argument /// The argument value as a non-null value /// Thrown when the argument is not valid public static void ArgumentValid( Func messageFunc, bool test, string? argName = null) { if (!test) throw new ArgumentException(messageFunc?.Invoke(), argName); } } ================================================ FILE: src/compat/Directory.Build.props ================================================ $(DefineConstants);ROSLYN_LATEST 4.14.0 ================================================ FILE: src/compat/xunit.analyzers.latest/xunit.analyzers.latest.csproj ================================================ Xunit.Analyzers netstandard2.0 ================================================ FILE: src/compat/xunit.analyzers.latest.fixes/xunit.analyzers.latest.fixes.csproj ================================================ Xunit.Analyzers.Fixes netstandard2.0 ================================================ FILE: src/compat/xunit.analyzers.latest.tests/xunit.analyzers.latest.tests.csproj ================================================ xunit.analyzers.latest.tests.$(TargetFramework) Exe xunit.analyzers.latest.tests Xunit.Analyzers net8.0;net472 ================================================ FILE: src/xunit.analyzers/Properties/AssemblyInfo.cs ================================================ using System.Reflection; [assembly: AssemblyCompany(".NET Foundation")] [assembly: AssemblyProduct("xUnit.net Testing Framework")] [assembly: AssemblyCopyright("Copyright (C) .NET Foundation")] [assembly: AssemblyTitle("xUnit.net Code Analysis (Analyzers)")] ================================================ FILE: src/xunit.analyzers/Suppressors/ConsiderCallingConfigureAwaitSuppressor.cs ================================================ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Xunit.Analyzers; namespace Xunit.Suppressors; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class ConsiderCallingConfigureAwaitSuppressor : XunitDiagnosticSuppressor { public ConsiderCallingConfigureAwaitSuppressor() : base(Descriptors.CA2007_Suppression) { } protected override bool ShouldSuppress( Diagnostic diagnostic, SuppressionAnalysisContext context, XunitContext xunitContext) { if (diagnostic.Location.SourceTree is null) return false; var factAttributeType = xunitContext.Core.FactAttributeType; var theoryAttributeType = xunitContext.Core.TheoryAttributeType; if (factAttributeType is null || theoryAttributeType is null) return false; var root = diagnostic.Location.SourceTree.GetRoot(context.CancellationToken); if (root?.FindNode(diagnostic.Location.SourceSpan) is not InvocationExpressionSyntax invocationSyntax) return false; var current = invocationSyntax.Parent; while (true) { if (current is null or LocalFunctionStatementSyntax or LambdaExpressionSyntax) return false; if (current is MethodDeclarationSyntax) break; current = current.Parent; } var semanticModel = context.GetSemanticModel(diagnostic.Location.SourceTree); var methodSymbol = semanticModel.GetDeclaredSymbol(current); if (methodSymbol is null) return false; var attributes = ImmutableHashSet.Create(SymbolEqualityComparer.Default, factAttributeType, theoryAttributeType); return methodSymbol .GetAttributes() .Any(a => attributes.Contains(a.AttributeClass)); } } ================================================ FILE: src/xunit.analyzers/Suppressors/MakeTypesInternalSuppressor.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Xunit.Analyzers; namespace Xunit.Suppressors; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class MakeTypesInternalSuppressor : XunitDiagnosticSuppressor { public MakeTypesInternalSuppressor() : base(Descriptors.CA1515_Suppression) { } protected override bool ShouldSuppress( Diagnostic diagnostic, SuppressionAnalysisContext context, XunitContext xunitContext) { if (diagnostic.Location.SourceTree is null) return false; var root = diagnostic.Location.SourceTree.GetRoot(context.CancellationToken); if (root?.FindNode(diagnostic.Location.SourceSpan) is not ClassDeclarationSyntax classDeclaration) return false; var semanticModel = context.GetSemanticModel(diagnostic.Location.SourceTree); var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration) as ITypeSymbol; return classSymbol.IsTestClass(xunitContext, strict: false); } } ================================================ FILE: src/xunit.analyzers/Suppressors/NonNullableFieldInitializationSuppressor.cs ================================================ using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Xunit.Analyzers; namespace Xunit.Suppressors; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class NonNullableFieldInitializationSuppressor : XunitDiagnosticSuppressor { public NonNullableFieldInitializationSuppressor() : base(Descriptors.CS8618_Suppression) { } protected override bool ShouldSuppress( Diagnostic diagnostic, SuppressionAnalysisContext context, XunitContext xunitContext) { if (diagnostic.Location.SourceTree is null) return false; var asyncLifetimeType = TypeSymbolFactory.IAsyncLifetime(context.Compilation); if (asyncLifetimeType is null) return false; var root = diagnostic.Location.SourceTree.GetRoot(context.CancellationToken); var node = root?.FindNode(diagnostic.Location.SourceSpan); if (node is null) return false; var semanticModel = context.GetSemanticModel(diagnostic.Location.SourceTree); var memberSymbol = ResolveMemberSymbol(diagnostic, node, semanticModel, context); if (memberSymbol is null) return false; var containingType = memberSymbol.ContainingType; if (containingType is null) return false; if (!containingType.AllInterfaces.Contains(asyncLifetimeType, SymbolEqualityComparer.Default)) return false; // Find the InitializeAsync method implementation var initializeAsyncInterfaceMethod = asyncLifetimeType.GetMembers("InitializeAsync").FirstOrDefault(); if (initializeAsyncInterfaceMethod is null) return false; var initializeAsyncImpl = containingType.FindImplementationForInterfaceMember(initializeAsyncInterfaceMethod); if (initializeAsyncImpl is null) return false; return IsMemberAssignedInMethod(initializeAsyncImpl, memberSymbol, context); } static ISymbol? ResolveMemberSymbol( Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, SuppressionAnalysisContext context) { // CS8618 can target field variable declarators or property declarations directly ISymbol? memberSymbol = node switch { VariableDeclaratorSyntax variableDeclarator => semanticModel.GetDeclaredSymbol(variableDeclarator), PropertyDeclarationSyntax propertyDeclaration => semanticModel.GetDeclaredSymbol(propertyDeclaration), _ => null, }; if (memberSymbol is not null) return memberSymbol; // Check AdditionalLocations (some compiler versions include member location here) foreach (var additionalLocation in diagnostic.AdditionalLocations) { if (additionalLocation.SourceTree is null) continue; var addRoot = additionalLocation.SourceTree.GetRoot(context.CancellationToken); var addNode = addRoot.FindNode(additionalLocation.SourceSpan); var addModel = context.GetSemanticModel(additionalLocation.SourceTree); var symbol = addModel.GetDeclaredSymbol(addNode, context.CancellationToken); if (symbol is IFieldSymbol or IPropertySymbol) return symbol; } // Fallback: CS8618 on a constructor — extract member name from diagnostic message var declaredSymbol = semanticModel.GetDeclaredSymbol(node, context.CancellationToken); if (declaredSymbol is IMethodSymbol { MethodKind: MethodKind.Constructor } constructorSymbol) { var message = diagnostic.GetMessage(CultureInfo.InvariantCulture); var startQuote = message.IndexOf('\''); if (startQuote >= 0) { var endQuote = message.IndexOf('\'', startQuote + 1); if (endQuote > startQuote) { var memberName = message.Substring(startQuote + 1, endQuote - startQuote - 1); return constructorSymbol.ContainingType .GetMembers(memberName) .FirstOrDefault(m => m is IFieldSymbol or IPropertySymbol); } } } return null; } static bool IsMemberAssignedInMethod( ISymbol methodSymbol, ISymbol targetMember, SuppressionAnalysisContext context) { foreach (var syntaxRef in methodSymbol.DeclaringSyntaxReferences) { var methodSyntax = syntaxRef.GetSyntax(context.CancellationToken); if (methodSyntax is not MethodDeclarationSyntax methodDecl) continue; var methodSemanticModel = context.GetSemanticModel(methodSyntax.SyntaxTree); foreach (var assignment in methodDecl.DescendantNodes().OfType()) { var assignedSymbol = methodSemanticModel.GetSymbolInfo(assignment.Left).Symbol; if (assignedSymbol is not null && SymbolEqualityComparer.Default.Equals(assignedSymbol, targetMember)) return true; } } return false; } } ================================================ FILE: src/xunit.analyzers/Suppressors/UseAsyncSuffixForAsyncMethodsSuppressor.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Xunit.Analyzers; namespace Xunit.Suppressors; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class UseAsyncSuffixForAsyncMethodsSuppressor : XunitDiagnosticSuppressor { public UseAsyncSuffixForAsyncMethodsSuppressor() : base(Descriptors.VSTHRD200_Suppression) { } protected override bool ShouldSuppress( Diagnostic diagnostic, SuppressionAnalysisContext context, XunitContext xunitContext) { var attributeUsageType = TypeSymbolFactory.AttributeUsageAttribute(context.Compilation); if (attributeUsageType is null) return false; if (diagnostic?.Location.SourceTree is null) return false; if (diagnostic.Location.SourceTree.GetRoot().FindNode(diagnostic.Location.SourceSpan) is not MethodDeclarationSyntax methodDeclaration) return false; var semanticModel = context.GetSemanticModel(diagnostic.Location.SourceTree); var methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration) as IMethodSymbol; return methodSymbol.IsTestMethod(xunitContext, attributeUsageType, strict: false); } } ================================================ FILE: src/xunit.analyzers/Utility/AssertUsageAnalyzerBase.cs ================================================ using System; using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; public abstract class AssertUsageAnalyzerBase( DiagnosticDescriptor[] descriptors, IEnumerable methods) : XunitDiagnosticAnalyzer(descriptors) { readonly HashSet targetMethods = new(methods, StringComparer.Ordinal); protected AssertUsageAnalyzerBase( DiagnosticDescriptor descriptor, IEnumerable methods) : this([descriptor], methods) { } public sealed override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var assertType = TypeSymbolFactory.Assert(context.Compilation); if (assertType is null) return; context.RegisterOperationAction(context => { if (context.Operation is IInvocationOperation invocationOperation) { var methodSymbol = invocationOperation.TargetMethod; if (methodSymbol.MethodKind != MethodKind.Ordinary || !SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingType, assertType) || !targetMethods.Contains(methodSymbol.Name)) return; AnalyzeInvocation(context, xunitContext, invocationOperation, methodSymbol); } }, OperationKind.Invocation); } protected abstract void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method); } ================================================ FILE: src/xunit.analyzers/Utility/Category.cs ================================================ namespace Xunit.Analyzers; public enum Category { // 1xxx Usage, // 2xxx Assertions, // 3xxx Extensibility, } ================================================ FILE: src/xunit.analyzers/Utility/CodeAnalysisExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; static class CodeAnalysisExtensions { public static INamedTypeSymbol? FindNamedType( this IAssemblySymbol assembly, Func selector) { Guard.ArgumentNotNull(assembly); Guard.ArgumentNotNull(selector); var visitor = new NamedTypeVisitor(selector); visitor.Visit(assembly); return visitor.MatchingType; } public static ImmutableArray GetAttributesWithInheritance( this IMethodSymbol method, ITypeSymbol? attributeUsageType) { var result = new Dictionary>(SymbolEqualityComparer.Default); foreach (var attribute in method.GetAttributes()) if (attribute.AttributeClass is not null) result.Add(attribute.AttributeClass, attribute); if (method.IsOverride && attributeUsageType is not null) for (var baseMethod = method.OverriddenMethod; baseMethod != null; baseMethod = baseMethod.OverriddenMethod) foreach (var attribute in baseMethod.GetAttributes()) { if (attribute.AttributeClass is null || result.ContainsKey(attribute.AttributeClass)) continue; var inherited = true; var allowMultiple = false; var usageAttribute = attribute.AttributeClass.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeUsageType)); if (usageAttribute is not null) { var inheritedNamedArgument = usageAttribute .NamedArguments .FirstOrDefault(n => n.Key == nameof(AttributeUsageAttribute.Inherited)); if (inheritedNamedArgument.Value.Value is not null) inherited = (bool)inheritedNamedArgument.Value.Value; var allowMultipleNamedArgument = usageAttribute .NamedArguments .FirstOrDefault(n => n.Key == nameof(AttributeUsageAttribute.AllowMultiple)); if (allowMultipleNamedArgument.Value.Value is not null) allowMultiple = (bool)allowMultipleNamedArgument.Value.Value; } if ((allowMultiple || !result.ContainsKey(attribute.AttributeClass)) && inherited) result.Add(attribute.AttributeClass, attribute); } return result.Values.SelectMany(x => x).ToImmutableArray(); } public static (bool isInTestMethod, IOperation? lambdaOwner) IsInTestMethod( this IOperation operation, XunitContext xunitContext) { Guard.ArgumentNotNull(operation); Guard.ArgumentNotNull(xunitContext); if (xunitContext.Core.FactAttributeType is null || xunitContext.Core.TheoryAttributeType is null) return (false, null); var semanticModel = operation.SemanticModel; if (semanticModel is null) return (false, null); IOperation? lambdaOwner = null; for (var parent = operation.Parent; parent is not null; parent = parent.Parent) { if (parent is IAnonymousFunctionOperation) { if (lambdaOwner is null) { lambdaOwner = parent; if (parent.Parent is IDelegateCreationOperation) for (var target = parent.Parent.Parent; target is not null; target = target.Parent) if (target is IArgumentOperation && target.Parent is IInvocationOperation invocationOperation) { lambdaOwner = invocationOperation; break; } } continue; } if (parent is ILocalFunctionOperation) { lambdaOwner = parent; continue; } if (parent is not IMethodBodyOperation methodBodyOperation) continue; if (methodBodyOperation.Syntax is not MethodDeclarationSyntax methodSyntax) continue; var insideTestMethod = methodSyntax.AttributeLists.SelectMany(list => list.Attributes).Any(attr => { var typeInfo = semanticModel.GetTypeInfo(attr); if (typeInfo.Type is null) return false; return SymbolEqualityComparer.Default.Equals(typeInfo.Type, xunitContext.Core.FactAttributeType) || SymbolEqualityComparer.Default.Equals(typeInfo.Type, xunitContext.Core.TheoryAttributeType); }); if (!insideTestMethod) return (false, null); return (true, lambdaOwner); } return (false, null); } public static bool IsPointer( this ExpressionSyntax expression, SemanticModel? semanticModel) => semanticModel?.GetTypeInfo(expression).Type?.TypeKind == TypeKind.Pointer; public static bool IsTestClass( this ITypeSymbol? type, XunitContext xunitContext, bool strict) { Guard.ArgumentNotNull(xunitContext); if (type is null) return false; if (strict) return IsTestClassStrict(type, xunitContext); else return IsTestClassNonStrict(type, xunitContext); } static bool IsTestClassNonStrict( ITypeSymbol type, XunitContext xunitContext) { var factAttributeType = xunitContext.Core.FactAttributeType; if (factAttributeType is null) return false; return type .GetMembers() .OfType() .Any(method => method .GetAttributes() .Select(a => a.AttributeClass) .Any(t => factAttributeType.IsAssignableFrom(t)) ); } static bool IsTestClassStrict( ITypeSymbol type, XunitContext xunitContext) { var factAttributeType = xunitContext.Core.FactAttributeType; var theoryAttributeType = xunitContext.Core.TheoryAttributeType; if (factAttributeType is null || theoryAttributeType is null) return false; var testMethodAttributes = new[] { factAttributeType, theoryAttributeType } .ToImmutableHashSet(SymbolEqualityComparer.Default); return type .GetMembers() .OfType() .Any(method => method .GetAttributes() .Select(a => a.AttributeClass) .Any(t => testMethodAttributes.Contains(t, SymbolEqualityComparer.Default)) ); } public static bool IsTestMethod( this IMethodSymbol? method, XunitContext xunitContext, ITypeSymbol attributeUsageType, bool strict) { Guard.ArgumentNotNull(xunitContext); if (method is null) return false; var factAttributeType = xunitContext.Core.FactAttributeType; var theoryAttributeType = xunitContext.Core.TheoryAttributeType; if (factAttributeType is null || theoryAttributeType is null) return false; var attributes = method.GetAttributesWithInheritance(attributeUsageType); var comparer = SymbolEqualityComparer.Default; return strict ? attributes.Any(a => comparer.Equals(a.AttributeClass, factAttributeType) || comparer.Equals(a.AttributeClass, theoryAttributeType)) : attributes.Any(a => factAttributeType.IsAssignableFrom(a.AttributeClass)); } public static IOperation WalkDownImplicitConversions(this IOperation operation) { Guard.ArgumentNotNull(operation); var current = operation; while (current is IConversionOperation conversion && conversion.Conversion.IsImplicit) current = conversion.Operand; return current; } sealed class NamedTypeVisitor(Func selector) : SymbolVisitor { readonly Func selector = Guard.ArgumentNotNull(selector); public INamedTypeSymbol? MatchingType { get; private set; } public override void VisitAssembly(IAssemblySymbol symbol) => Guard.ArgumentNotNull(symbol).GlobalNamespace.Accept(this); public override void VisitNamespace(INamespaceSymbol symbol) { Guard.ArgumentNotNull(symbol); if (MatchingType is not null) return; foreach (var member in symbol.GetMembers()) member.Accept(this); } public override void VisitNamedType(INamedTypeSymbol symbol) { Guard.ArgumentNotNull(symbol); if (MatchingType is not null) return; if (selector(symbol)) { MatchingType = symbol; return; } foreach (var nestedType in symbol.GetTypeMembers()) nestedType.Accept(this); } } } ================================================ FILE: src/xunit.analyzers/Utility/Constants.cs ================================================ namespace Xunit.Analyzers; public static class Constants { /// /// Argument names for Assert methods /// public static class AssertArguments { public const string Actual = "actual"; public const string Expected = "expected"; public const string IgnoreCase = "ignoreCase"; } /// /// Method names from Assert /// public static class Asserts { public const string All = nameof(All); public const string AllAsync = nameof(AllAsync); public const string Collection = nameof(Collection); public const string CollectionAsync = nameof(CollectionAsync); public const string Contains = nameof(Contains); public const string Distinct = nameof(Distinct); public const string DoesNotContain = nameof(DoesNotContain); public const string DoesNotMatch = nameof(DoesNotMatch); public const string Empty = nameof(Empty); public const string EndsWith = nameof(EndsWith); public const string Equal = nameof(Equal); public const string Equivalent = nameof(Equivalent); public const string Fail = nameof(Fail); public const string False = nameof(False); public const string InRange = nameof(InRange); public const string IsAssignableFrom = nameof(IsAssignableFrom); public const string IsNotAssignableFrom = nameof(IsNotAssignableFrom); public const string IsNotType = nameof(IsNotType); public const string IsType = nameof(IsType); public const string Matches = nameof(Matches); public const string Multiple = nameof(Multiple); public const string NotEmpty = nameof(NotEmpty); public const string NotEqual = nameof(NotEqual); public const string NotInRange = nameof(NotInRange); public const string NotNull = nameof(NotNull); public const string NotSame = nameof(NotSame); public const string NotStrictEqual = nameof(NotStrictEqual); public const string Null = nameof(Null); public const string ProperSubset = nameof(ProperSubset); public const string ProperSuperset = nameof(ProperSuperset); public const string PropertyChanged = nameof(PropertyChanged); public const string PropertyChangedAsync = nameof(PropertyChangedAsync); public const string Raises = nameof(Raises); public const string RaisesAny = nameof(RaisesAny); public const string RaisesAnyAsync = nameof(RaisesAnyAsync); public const string RaisesAsync = nameof(RaisesAsync); public const string Same = nameof(Same); public const string Single = nameof(Single); public const string StartsWith = nameof(StartsWith); public const string StrictEqual = nameof(StrictEqual); public const string Subset = nameof(Subset); public const string Superset = nameof(Superset); public const string Throws = nameof(Throws); public const string ThrowsAny = nameof(ThrowsAny); public const string ThrowsAnyAsync = nameof(ThrowsAnyAsync); public const string ThrowsAsync = nameof(ThrowsAsync); public const string True = nameof(True); } /// /// Attribute names (without the Attribute suffix unless otherwise noted) /// public static class Attributes { public const string Fact = nameof(Fact); public const string Theory = nameof(Theory); } /// /// Property names from xUnit.net attributes /// public static class AttributeProperties { public const string DeclaringType = nameof(DeclaringType); public const string MemberName = nameof(MemberName); public const string MemberType = nameof(MemberType); } /// /// Properties placed into diagnostics to be picked up by fixes /// public static class Properties { public const string ArgumentValue = nameof(ArgumentValue); public const string AssertMethodName = nameof(AssertMethodName); public const string DataAttributeTypeName = nameof(DataAttributeTypeName); public const string DeclaringType = nameof(DeclaringType); public const string IgnoreCase = nameof(IgnoreCase); public const string IsCtorObsolete = nameof(IsCtorObsolete); public const string IsStatic = nameof(IsStatic); public const string IsStaticMethodCall = nameof(IsStaticMethodCall); public const string LiteralValue = nameof(LiteralValue); public const string MemberName = nameof(MemberName); public const string MethodName = nameof(MethodName); public const string NewBaseType = nameof(NewBaseType); public const string ParameterArrayStyle = nameof(ParameterArrayStyle); public const string ParameterIndex = nameof(ParameterIndex); public const string ParameterName = nameof(ParameterName); public const string ParameterSpecialType = nameof(ParameterSpecialType); public const string Replacement = nameof(Replacement); public const string SizeValue = nameof(SizeValue); public const string SubstringMethodName = nameof(SubstringMethodName); public const string TestClassName = nameof(TestClassName); public const string TFixtureDisplayName = nameof(TFixtureDisplayName); public const string TFixtureName = nameof(TFixtureName); public const string TypeName = nameof(TypeName); public const string UseExactMatch = nameof(UseExactMatch); } /// /// Type names as strings for runtime lookup /// public static class Types { public static class System { public const string ObsoleteAttribute = "System.ObsoleteAttribute"; } public static class Xunit { public const string AssemblyFixtureAttribute_V3 = "Xunit.AssemblyFixtureAttribute"; public const string Assert = "Xunit.Assert"; public const string ClassDataAttribute = "Xunit.ClassDataAttribute"; public const string ClassDataAttributeOfT_V3 = "Xunit.ClassDataAttribute`1"; public const string CollectionAttribute = "Xunit.CollectionAttribute"; public const string CollectionAttributeOfT_V3 = "Xunit.CollectionAttribute`1"; public const string CollectionDefinitionAttribute = "Xunit.CollectionDefinitionAttribute"; public const string DataAttribute_V2 = "Xunit.Sdk.DataAttribute"; public const string DataAttribute_V3 = "Xunit.v3.DataAttribute"; public const string FactAttribute = "Xunit.FactAttribute"; public const string IAssemblyInfo_V2 = "Xunit.Abstractions.IAssemblyInfo"; public const string IAsyncLifetime = "Xunit.IAsyncLifetime"; public const string IAttributeInfo_V2 = "Xunit.Abstractions.IAttributeInfo"; public const string IClassFixtureOfT = "Xunit.IClassFixture`1"; public const string ICollectionFixtureOfT = "Xunit.ICollectionFixture`1"; public const string IDataAttribute_V3 = "Xunit.v3.IDataAttribute"; public const string IMessageSink_V2 = "Xunit.Abstractions.IMessageSink"; public const string IMessageSink_V3 = "Xunit.Sdk.IMessageSink"; public const string IMessageSinkMessage_V2 = "Xunit.Abstractions.IMessageSinkMessage"; public const string IMethodInfo_V2 = "Xunit.Abstractions.IMethodInfo"; public const string IParameterInfo_V2 = "Xunit.Abstractions.IParameterInfo"; public const string InlineDataAttribute = "Xunit.InlineDataAttribute"; public const string IRunnerReporter_V3 = "Xunit.Runner.Common.IRunnerReporter"; public const string ISourceInformation_V2 = "Xunit.Abstractions.ISourceInformation"; public const string ISourceInformationProvider_V2 = "Xunit.Abstractions.ISourceInformationProvider"; public const string ISourceInformationProvider_V3 = "Xunit.Runner.Common.ISourceInformationProvider"; public const string ITest_V2 = "Xunit.Abstractions.ITest"; public const string ITest_V3 = "Xunit.Sdk.ITest"; public const string ITestAssembly_V2 = "Xunit.Abstractions.ITestAssembly"; public const string ITestAssembly_V3 = "Xunit.Sdk.ITestAssembly"; public const string ITestCase_V2 = "Xunit.Abstractions.ITestCase"; public const string ITestCase_V3 = "Xunit.Sdk.ITestCase"; public const string ITestClass_V2 = "Xunit.Abstractions.ITestClass"; public const string ITestClass_V3 = "Xunit.Sdk.ITestClass"; public const string ITestCollection_V2 = "Xunit.Abstractions.ITestCollection"; public const string ITestCollection_V3 = "Xunit.Sdk.ITestCollection"; public const string ITestContextAccessor_V3 = "Xunit.ITestContextAccessor"; public const string ITestFramework_V2 = "Xunit.Abstractions.ITestFramework"; public const string ITestFramework_V3 = "Xunit.v3.ITestFramework"; public const string ITestFrameworkDiscoverer_V2 = "Xunit.Abstractions.ITestFrameworkDiscoverer"; public const string ITestFrameworkDiscoverer_V3 = "Xunit.v3.ITestFrameworkDiscoverer"; public const string ITestFrameworkExecutor_V2 = "Xunit.Abstractions.ITestFrameworkExecutor"; public const string ITestFrameworkExecutor_V3 = "Xunit.v3.ITestFrameworkExecutor"; public const string ITestMethod_V2 = "Xunit.Abstractions.ITestMethod"; public const string ITestMethod_V3 = "Xunit.Sdk.ITestMethod"; public const string ITestOutputHelper_V2 = "Xunit.Abstractions.ITestOutputHelper"; public const string ITestOutputHelper_V3 = "Xunit.ITestOutputHelper"; public const string ITheoryDataRow_V3 = "Xunit.ITheoryDataRow"; public const string ITypeInfo_V2 = "Xunit.Abstractions.ITypeInfo"; public const string IXunitSerializable_V2 = "Xunit.Abstractions.IXunitSerializable"; public const string IXunitSerializable_V3 = "Xunit.Sdk.IXunitSerializable"; public const string IXunitSerializer_V3 = "Xunit.Sdk.IXunitSerializer"; public const string JsonTypeIDAttribute_V3 = "Xunit.Sdk.JsonTypeIDAttribute"; public const string LongLivedMarshalByRefObject_Execution_V2 = "Xunit.LongLivedMarshalByRefObject"; public const string LongLivedMarshalByRefObject_RunnerUtility = "Xunit.Sdk.LongLivedMarshalByRefObject"; public const string MemberDataAttribute = "Xunit.MemberDataAttribute"; public const string Record = "Xunit.Record"; public const string RegisterXunitSerializerAttribute_V3 = "Xunit.Sdk.RegisterXunitSerializerAttribute"; public const string TestContext_V3 = "Xunit.TestContext"; public const string TheoryAttribute = "Xunit.TheoryAttribute"; public const string TheoryData = "Xunit.TheoryData"; public const string TheoryDataRow_V3 = "Xunit.TheoryDataRow"; } } } ================================================ FILE: src/xunit.analyzers/Utility/ConversionChecker.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; namespace Xunit.Analyzers; static class ConversionChecker { static readonly HashSet SignedIntegralTypes = [ SpecialType.System_SByte, SpecialType.System_Int16, SpecialType.System_Int32, SpecialType.System_Int64, ]; static readonly HashSet UnsignedIntegralTypes = [ SpecialType.System_Byte, SpecialType.System_UInt16, SpecialType.System_UInt32, SpecialType.System_UInt64, ]; public static bool IsConvertible( Compilation compilation, ITypeSymbol source, ITypeSymbol destination, XunitContext xunitContext, object? valueSource = null) { Guard.ArgumentNotNull(compilation); Guard.ArgumentNotNull(source); Guard.ArgumentNotNull(destination); Guard.ArgumentNotNull(xunitContext); if (destination.TypeKind == TypeKind.Array) { var destinationElementType = ((IArrayTypeSymbol)destination).ElementType; if (destinationElementType.TypeKind == TypeKind.TypeParameter) return IsConvertibleTypeParameter(source, (ITypeParameterSymbol)destinationElementType); } if (destination.TypeKind == TypeKind.TypeParameter) return IsConvertibleTypeParameter(source, (ITypeParameterSymbol)destination); var conversion = compilation.ClassifyConversion(source, destination); if (conversion.IsNumeric) return IsConvertibleNumeric(source, destination, valueSource); if (destination.SpecialType == SpecialType.System_DateTime || (xunitContext.Core.TheorySupportsConversionFromStringToDateTimeOffsetAndGuid && IsDateTimeOffsetOrGuid(destination))) { // Allow all conversions from strings. All parsing issues will be reported at runtime. return source.SpecialType == SpecialType.System_String; } // Rules of last resort return conversion.IsImplicit || conversion.IsUnboxing || (conversion.IsExplicit && conversion.IsUserDefined) || (conversion.IsExplicit && conversion.IsNullable); } static bool IsConvertibleTypeParameter( ITypeSymbol source, ITypeParameterSymbol destination) { if (destination.HasValueTypeConstraint && !source.IsValueType) return false; if (destination.HasReferenceTypeConstraint && source.IsValueType) return false; return destination.ConstraintTypes.All(c => c.IsAssignableFrom(source)); } static bool IsConvertibleNumeric( ITypeSymbol source, ITypeSymbol destination, object? valueSource = null) { var isIntegral = long.TryParse(valueSource?.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var integralValue); if (isIntegral && integralValue < 0 && IsSigned(source) && IsUnsigned(destination)) return false; if (destination.SpecialType == SpecialType.System_Char && (source.SpecialType == SpecialType.System_Double || source.SpecialType == SpecialType.System_Single)) { // Conversions from float to char (though numeric) do not actually work at runtime, so report them return false; } return true; // Allow all numeric conversions. Narrowing conversion issues will be reported at runtime. } static bool IsDateTimeOffsetOrGuid(ITypeSymbol destination) { if (destination.ContainingNamespace?.Name != nameof(System)) return false; return destination.MetadataName is (nameof(DateTimeOffset)) or (nameof(Guid)); } static bool IsSigned(ITypeSymbol typeSymbol) => SignedIntegralTypes.Contains(typeSymbol.SpecialType); static bool IsUnsigned(ITypeSymbol typeSymbol) => UnsignedIntegralTypes.Contains(typeSymbol.SpecialType); } ================================================ FILE: src/xunit.analyzers/Utility/Descriptors.Suppressors.cs ================================================ using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public static partial class Descriptors { public static SuppressionDescriptor CA1515_Suppression { get; } = Suppression("CA1515", "xUnit.net's test classes must be public."); public static SuppressionDescriptor CA2007_Suppression { get; } = Suppression("CA2007", "xUnit.net test methods should not call ConfigureAwait"); public static SuppressionDescriptor CS8618_Suppression { get; } = Suppression("CS8618", "Non-nullable member is initialized in IAsyncLifetime.InitializeAsync"); public static SuppressionDescriptor VSTHRD200_Suppression { get; } = Suppression("VSTHRD200", "xUnit.net test methods are not directly callable and do not benefit from this naming rule"); } ================================================ FILE: src/xunit.analyzers/Utility/Descriptors.cs ================================================ using System.Collections.Concurrent; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public static partial class Descriptors { static readonly ConcurrentDictionary categoryMapping = new(); static DiagnosticDescriptor Diagnostic( string id, string title, Category category, DiagnosticSeverity defaultSeverity, string messageFormat) { var helpLink = $"https://xunit.net/xunit.analyzers/rules/{id}"; var categoryString = categoryMapping.GetOrAdd(category, c => c.ToString()); return new DiagnosticDescriptor(id, title, messageFormat, categoryString, defaultSeverity, isEnabledByDefault: true, helpLinkUri: helpLink); } static SuppressionDescriptor Suppression( string suppressedDiagnosticId, string justification) => new("xUnitSuppress-" + suppressedDiagnosticId, suppressedDiagnosticId, justification); } ================================================ FILE: src/xunit.analyzers/Utility/Descriptors.xUnit1xxx.cs ================================================ using Microsoft.CodeAnalysis; using static Microsoft.CodeAnalysis.DiagnosticSeverity; using static Xunit.Analyzers.Category; namespace Xunit.Analyzers; public static partial class Descriptors { public static DiagnosticDescriptor X1000_TestClassMustBePublic { get; } = Diagnostic( "xUnit1000", "Test classes must be public", Usage, Error, "Test classes must be public. Add or change the visibility modifier of the test class to public." ); public static DiagnosticDescriptor X1001_FactMethodMustNotHaveParameters { get; } = Diagnostic( "xUnit1001", "Fact methods cannot have parameters", Usage, Error, "Fact methods cannot have parameters. Remove the parameters from the method or convert it into a Theory." ); public static DiagnosticDescriptor X1002_TestMethodMustNotHaveMultipleFactAttributes { get; } = Diagnostic( "xUnit1002", "Test methods cannot have multiple Fact or Theory attributes", Usage, Error, "Test methods cannot have multiple Fact or Theory attributes. Remove all but one of the attributes." ); public static DiagnosticDescriptor X1003_TheoryMethodMustHaveTestData { get; } = Diagnostic( "xUnit1003", "Theory methods must have test data", Usage, Error, "Theory methods must have test data. Use InlineData, MemberData, or ClassData to provide test data for the Theory." ); public static DiagnosticDescriptor X1004_TestMethodShouldNotBeSkipped { get; } = Diagnostic( "xUnit1004", "Test methods should not be skipped", Usage, Info, "Test methods should not be skipped. Remove the Skip property to start running the test again." ); public static DiagnosticDescriptor X1005_FactMethodShouldNotHaveTestData { get; } = Diagnostic( "xUnit1005", "Fact methods should not have test data", Usage, Warning, "Fact methods should not have test data. Remove the test data, or convert the Fact to a Theory." ); public static DiagnosticDescriptor X1006_TheoryMethodShouldHaveParameters { get; } = Diagnostic( "xUnit1006", "Theory methods should have parameters", Usage, Warning, "Theory methods should have parameters. Add parameter(s) to the theory method." ); public static DiagnosticDescriptor X1007_ClassDataAttributeMustPointAtValidClass { get; } = Diagnostic( "xUnit1007", "ClassData must point at a valid class", Usage, Error, "ClassData must point at a valid class. The class {0} must be public, not sealed, with an empty constructor, and implement {1}." ); public static DiagnosticDescriptor X1008_DataAttributeShouldBeUsedOnATheory { get; } = Diagnostic( "xUnit1008", "Test data attribute should only be used on a Theory", Usage, Warning, "Test data attribute should only be used on a Theory. Remove the test data, or add the Theory attribute to the test method." ); public static DiagnosticDescriptor X1009_InlineDataMustMatchTheoryParameters_TooFewValues { get; } = Diagnostic( "xUnit1009", "InlineData values must match the number of method parameters", Usage, Error, "InlineData values must match the number of method parameters. Remove unused parameters, or add more data for the missing parameters." ); public static DiagnosticDescriptor X1010_InlineDataMustMatchTheoryParameters_IncompatibleValueType { get; } = Diagnostic( "xUnit1010", "The value is not convertible to the method parameter type", Usage, Error, "The value is not convertible to the method parameter '{0}' of type '{1}'. Use a compatible data value." ); public static DiagnosticDescriptor X1011_InlineDataMustMatchTheoryParameters_ExtraValue { get; } = Diagnostic( "xUnit1011", "There is no matching method parameter", Usage, Error, "There is no matching method parameter for value: {0}. Remove unused value(s), or add more parameter(s)." ); public static DiagnosticDescriptor X1012_InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameter { get; } = Diagnostic( "xUnit1012", "Null should only be used for nullable parameters", Usage, Warning, "Null should not be used for type parameter '{0}' of type '{1}'. Use a non-null value, or convert the parameter to a nullable type." ); public static DiagnosticDescriptor X1013_PublicMethodShouldBeMarkedAsTest { get; } = Diagnostic( "xUnit1013", "Public method should be marked as test", Usage, Warning, "Public method '{0}' on test class '{1}' should be marked as a {2}. Reduce the visibility of the method, or add a {2} attribute to the method." ); public static DiagnosticDescriptor X1014_MemberDataShouldUseNameOfOperator { get; } = Diagnostic( "xUnit1014", "MemberData should use nameof operator for member name", Usage, Warning, "MemberData should use nameof operator to reference member '{0}' on type '{1}'. Replace the constant string with nameof." ); public static DiagnosticDescriptor X1015_MemberDataMustReferenceExistingMember { get; } = Diagnostic( "xUnit1015", "MemberData must reference an existing member", Usage, Error, "MemberData must reference an existing member '{0}' on type '{1}'. Fix the member reference, or add the missing data member." ); public static DiagnosticDescriptor X1016_MemberDataMustReferencePublicMember { get; } = Diagnostic( "xUnit1016", "MemberData must reference a public member", Usage, Error, "MemberData must reference a public member. Add or change the visibility of the data member to public." ); public static DiagnosticDescriptor X1017_MemberDataMustReferenceStaticMember { get; } = Diagnostic( "xUnit1017", "MemberData must reference a static member", Usage, Error, "MemberData must reference a static member. Add the static modifier to the data member." ); public static DiagnosticDescriptor X1018_MemberDataMustReferenceValidMemberKind { get; } = Diagnostic( "xUnit1018", "MemberData must reference a valid member kind", Usage, Error, "MemberData must reference a property, field, or method. Convert the data member to a compatible member type." ); public static DiagnosticDescriptor X1019_MemberDataMustReferenceMemberOfValidType { get; } = Diagnostic( "xUnit1019", "MemberData must reference a member providing a valid data type", Usage, Error, "MemberData must reference a data type assignable to {0}. The referenced type '{1}' is not valid." ); public static DiagnosticDescriptor X1020_MemberDataPropertyMustHaveGetter { get; } = Diagnostic( "xUnit1020", "MemberData must reference a property with a public getter", Usage, Error, "MemberData must reference a property with a public getter. Add a public getter to the data member, or change the visibility of the existing getter to public." ); public static DiagnosticDescriptor X1021_MemberDataNonMethodShouldNotHaveParameters { get; } = Diagnostic( "xUnit1021", "MemberData should not have parameters if the referenced member is not a method", Usage, Warning, "MemberData should not have parameters if the referenced member is not a method. Remove the parameter values, or convert the data member to a method with parameters." ); public static DiagnosticDescriptor X1022_TheoryMethodCannotHaveParameterArray { get; } = Diagnostic( "xUnit1022", "Theory methods cannot have a parameter array", Usage, Error, "Theory method '{0}' on test class '{1}' cannot have a parameter array '{2}'. Upgrade to xUnit.net 2.2 or later to enable this feature." ); public static DiagnosticDescriptor X1023_TheoryMethodCannotHaveDefaultParameter { get; } = Diagnostic( "xUnit1023", "Theory methods cannot have default parameter values", Usage, Error, "Theory method '{0}' on test class '{1}' parameter '{2}' cannot have a default value. Upgrade to xUnit.net 2.2 or later to enable this feature." ); public static DiagnosticDescriptor X1024_TestMethodCannotHaveOverloads { get; } = Diagnostic( "xUnit1024", "Test methods cannot have overloads", Usage, Error, "Test method '{0}' on test class '{1}' has the same name as another method declared on class '{2}'. Rename method(s) so that there are no overloaded names." ); public static DiagnosticDescriptor X1025_InlineDataShouldBeUniqueWithinTheory { get; } = Diagnostic( "xUnit1025", "InlineData should be unique within the Theory it belongs to", Usage, Warning, "Theory method '{0}' on test class '{1}' has InlineData duplicate(s). Remove redundant attribute(s) from the theory method." ); public static DiagnosticDescriptor X1026_TheoryMethodShouldUseAllParameters { get; } = Diagnostic( "xUnit1026", "Theory methods should use all of their parameters", Usage, Warning, "Theory method '{0}' on test class '{1}' does not use parameter '{2}'. Use the parameter, or remove the parameter and associated data." ); public static DiagnosticDescriptor X1027_CollectionDefinitionClassMustBePublic { get; } = Diagnostic( "xUnit1027", "Collection definition classes must be public", Usage, Error, "Collection definition classes must be public. Add or change the visibility modifier of the collection definition class to public." ); public static DiagnosticDescriptor X1028_TestMethodHasInvalidReturnType { get; } = Diagnostic( "xUnit1028", "Test method must have valid return type", Usage, Error, "Test methods must have a supported return type. Valid types are: {0}. Change the return type to one of the compatible types." ); public static DiagnosticDescriptor X1029_LocalFunctionsCannotBeTestFunctions { get; } = Diagnostic( "xUnit1029", "Local functions cannot be test functions", Usage, Error, "Local functions cannot be test functions. Remove '{0}'." ); public static DiagnosticDescriptor X1030_DoNotUseConfigureAwait { get; } = Diagnostic( "xUnit1030", "Do not call ConfigureAwait(false) in test method", Usage, Warning, "Test methods should not call ConfigureAwait({0}), as it may bypass parallelization limits. {1}" ); public static DiagnosticDescriptor X1031_DoNotUseBlockingTaskOperations { get; } = Diagnostic( "xUnit1031", "Do not use blocking task operations in test method", Usage, Warning, "Test methods should not use blocking task operations, as they can cause deadlocks. Use an async test method and await instead." ); public static DiagnosticDescriptor X1032_TestClassCannotBeNestedInGenericClass { get; } = Diagnostic( "xUnit1032", "Test classes cannot be nested within a generic class", Usage, Error, "Test classes cannot be nested within a generic class. Move the test class out of the class it is nested in." ); public static DiagnosticDescriptor X1033_TestClassShouldHaveTFixtureArgument { get; } = Diagnostic( "xUnit1033", "Test classes decorated with 'Xunit.IClassFixture' or 'Xunit.ICollectionFixture' should add a constructor argument of type TFixture", Usage, Info, "Test class '{0}' does not contain constructor argument of type '{1}'. Add a constructor argument to consume the fixture data." ); public static DiagnosticDescriptor X1034_MemberDataArgumentsMustMatchMethodParameters_NullShouldNotBeUsedForIncompatibleParameter { get; } = Diagnostic( "xUnit1034", "Null should only be used for nullable parameters", Usage, Warning, "Null should not be used for type parameter '{0}' of type '{1}'. Use a non-null value, or convert the parameter to a nullable type." ); public static DiagnosticDescriptor X1035_MemberDataArgumentsMustMatchMethodParameters_IncompatibleValueType { get; } = Diagnostic( "xUnit1035", "The value is not convertible to the method parameter type", Usage, Error, "The value is not convertible to the method parameter '{0}' of type '{1}'. Use a compatible data value." ); public static DiagnosticDescriptor X1036_MemberDataArgumentsMustMatchMethodParameters_ExtraValue { get; } = Diagnostic( "xUnit1036", "There is no matching method parameter", Usage, Error, "There is no matching method parameter for value: {0}. Remove unused value(s), or add more parameter(s)." ); public static DiagnosticDescriptor X1037_TheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters { get; } = Diagnostic( "xUnit1037", "There are fewer theory data type arguments than required by the parameters of the test method", Usage, Error, "There are fewer {0} type arguments than required by the parameters of the test method. Add more type parameters to match the method signature, or remove parameters from the test method." ); public static DiagnosticDescriptor X1038_TheoryDataTypeArgumentsMustMatchTestMethodParameters_ExtraTypeParameters { get; } = Diagnostic( "xUnit1038", "There are more theory data type arguments than allowed by the parameters of the test method", Usage, Error, "There are more {0} type arguments than allowed by the parameters of the test method. Remove unused type arguments, or add more parameters." ); public static DiagnosticDescriptor X1039_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleTypes { get; } = Diagnostic( "xUnit1039", "The type argument to theory data is not compatible with the type of the corresponding test method parameter", Usage, Error, "The type argument {0} from {1} is not compatible with the type of the corresponding test method parameter {2}." ); public static DiagnosticDescriptor X1040_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleNullability { get; } = Diagnostic( "xUnit1040", "The type argument to theory data is nullable, while the type of the corresponding test method parameter is not", Usage, Warning, "The type argument {0} from {1} is nullable, while the type of the corresponding test method parameter {2} is not. Make the theory data type non-nullable, or make the test method parameter nullable." ); public static DiagnosticDescriptor X1041_EnsureFixturesHaveASource { get; } = Diagnostic( "xUnit1041", "Fixture arguments to test classes must have fixture sources", Usage, Warning, "Fixture argument '{0}' does not have a fixture source (if it comes from a collection definition, ensure the definition is in the same assembly as the test)" ); public static DiagnosticDescriptor X1042_MemberDataTheoryDataIsRecommendedForStronglyTypedAnalysis { get; } = Diagnostic( "xUnit1042", "The member referenced by the MemberData attribute returns untyped data rows", Usage, Info, "The member referenced by the MemberData attribute returns untyped data rows, such as object[]. Consider using {0} as the return type to provide better type safety." ); public static DiagnosticDescriptor X1043_ConstructorOnFactAttributeSubclassShouldBePublic { get; } = Diagnostic( "xUnit1043", "Constructors on classes derived from FactAttribute must be public when used on test methods", Usage, Error, "Constructor '{0}' must be public to be used on a test method." ); public static DiagnosticDescriptor X1044_AvoidUsingTheoryDataTypeArgumentsThatAreNotSerializable { get; } = Diagnostic( "xUnit1044", "Avoid using TheoryData type arguments that are not serializable", Usage, Info, "The type argument {0} is not serializable, which will cause Test Explorer to not enumerate individual data rows. Consider using a type that is known to be serializable." ); public static DiagnosticDescriptor X1045_AvoidUsingTheoryDataTypeArgumentsThatMightNotBeSerializable { get; } = Diagnostic( "xUnit1045", "Avoid using TheoryData type arguments that might not be serializable", Usage, Info, "The type argument {0} might not be serializable, which may cause Test Explorer to not enumerate individual data rows. Consider using a type that is known to be serializable." ); public static DiagnosticDescriptor X1046_AvoidUsingTheoryDataRowArgumentsThatAreNotSerializable { get; } = Diagnostic( "xUnit1046", "Avoid using TheoryDataRow arguments that are not serializable", Usage, Info, "The argument '{0}' of type '{1}' is not serializable, which will cause Test Explorer to not enumerate individual data rows. Consider using a value that is known to be serializable." ); public static DiagnosticDescriptor X1047_AvoidUsingTheoryDataRowArgumentsThatMightNotBeSerializable { get; } = Diagnostic( "xUnit1047", "Avoid using TheoryDataRow arguments that might not be serializable", Usage, Info, "The argument '{0}' of type '{1}' might not be serializable, which may cause Test Explorer to not enumerate individual data rows. Consider using a value that is known to be serializable." ); public static DiagnosticDescriptor X1048_DoNotUseAsyncVoidForTestMethods_V2 { get; } = Diagnostic( "xUnit1048", "Avoid using 'async void' for test methods as it is deprecated in xUnit.net v3", Usage, Warning, "Support for 'async void' unit tests is being removed from xUnit.net v3. To simplify upgrading, convert the test to 'async Task' instead." ); public static DiagnosticDescriptor X1049_DoNotUseAsyncVoidForTestMethods_V3 { get; } = Diagnostic( "xUnit1049", "Do not use 'async void' for test methods as it is no longer supported", Usage, Error, "Support for 'async void' unit tests has been removed from xUnit.net v3. Convert the test to 'async Task' or 'async ValueTask' instead." ); public static DiagnosticDescriptor X1050_ClassDataTheoryDataRowIsRecommendedForStronglyTypedAnalysis { get; } = Diagnostic( "xUnit1050", "The class referenced by the ClassData attribute returns untyped data rows", Usage, Info, "The class referenced by the ClassData attribute returns untyped data rows, such as object[] or ITheoryDataRow. Consider using generic TheoryDataRow<> as the row type to provide better type safety." ); public static DiagnosticDescriptor X1051_UseCancellationToken { get; } = Diagnostic( "xUnit1051", "Calls to methods which accept CancellationToken should use TestContext.Current.CancellationToken", Usage, Warning, "Calls to methods which accept CancellationToken should use TestContext.Current.CancellationToken to allow test cancellation to be more responsive." ); public static DiagnosticDescriptor X1052_TheoryDataShouldNotUseITheoryDataRow { get; } = Diagnostic( "xUnit1052", "Avoid using 'TheoryData<...>' with types that implement 'ITheoryDataRow'.", Usage, Warning, "'TheoryData<...>' should not be used with one or more type arguments that implement 'ITheoryDataRow' or a derived variant. This usage is not supported. Use either 'TheoryData' or a type of 'ITheoryDataRow' exclusively." ); public static DiagnosticDescriptor X1053_MemberDataMemberMustBeStaticallyWrittenTo { get; } = Diagnostic( "xUnit1053", "The static member used as theory data must be statically initialized.", Usage, Warning, "The member {0} referenced by MemberData is not initialized before use. Add an inline initializer or initialize the value in the static constructor." ); // Placeholder for rule X1054 // Placeholder for rule X1055 // Placeholder for rule X1056 // Placeholder for rule X1057 // Placeholder for rule X1058 // Placeholder for rule X1059 // Placeholder for rule X1060 } ================================================ FILE: src/xunit.analyzers/Utility/Descriptors.xUnit2xxx.cs ================================================ using Microsoft.CodeAnalysis; using static Microsoft.CodeAnalysis.DiagnosticSeverity; using static Xunit.Analyzers.Category; namespace Xunit.Analyzers; public static partial class Descriptors { public static DiagnosticDescriptor X2000_AssertEqualLiteralValueShouldBeFirst { get; } = Diagnostic( "xUnit2000", "Constants and literals should be the expected argument", Assertions, Warning, "The literal or constant value {0} should be passed as the 'expected' argument in the call to '{1}' in method '{2}' on type '{3}'. Swap the parameter values." ); public static DiagnosticDescriptor X2001_AssertEqualsShouldNotBeUsed { get; } = Diagnostic( "xUnit2001", "Do not use invalid equality check", Assertions, Hidden, "Do not use {0}. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2002_AssertNullShouldNotBeCalledOnValueTypes { get; } = Diagnostic( "xUnit2002", "Do not use null check on value type", Assertions, Warning, "Do not use {0} on value type '{1}'. Remove this assert." ); public static DiagnosticDescriptor X2003_AssertEqualShouldNotUsedForNullCheck { get; } = Diagnostic( "xUnit2003", "Do not use equality check to test for null value", Assertions, Warning, "Do not use {0} to check for null value. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2004_AssertEqualShouldNotUsedForBoolLiteralCheck { get; } = Diagnostic( "xUnit2004", "Do not use equality check to test for boolean conditions", Assertions, Warning, "Do not use {0} to check for boolean conditions. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2005_AssertSameShouldNotBeCalledOnValueTypes { get; } = Diagnostic( "xUnit2005", "Do not use identity check on value type", Assertions, Warning, "Do not use {0} on value type '{1}'. Value types do not have identity. Use Assert.{2} instead." ); public static DiagnosticDescriptor X2006_AssertEqualGenericShouldNotBeUsedForStringValue { get; } = Diagnostic( "xUnit2006", "Do not use invalid string equality check", Assertions, Warning, "Do not use {0} to test for string equality. Use {1} instead." ); public static DiagnosticDescriptor X2007_AssertIsTypeShouldUseGenericOverload { get; } = Diagnostic( "xUnit2007", "Do not use typeof expression to check the type", Assertions, Warning, "Do not use typeof({0}) expression to check the type. Use Assert.IsType<{0}> instead." ); public static DiagnosticDescriptor X2008_AssertRegexMatchShouldNotUseBoolLiteralCheck { get; } = Diagnostic( "xUnit2008", "Do not use boolean check to match on regular expressions", Assertions, Warning, "Do not use {0} to match on regular expressions. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2009_AssertSubstringCheckShouldNotUseBoolCheck { get; } = Diagnostic( "xUnit2009", "Do not use boolean check to check for substrings", Assertions, Warning, "Do not use {0} to check for substrings. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2010_AssertStringEqualityCheckShouldNotUseBoolCheckFixer { get; } = Diagnostic( "xUnit2010", "Do not use boolean check to check for string equality", Assertions, Warning, "Do not use {0} to check for string equality. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2011_AssertEmptyCollectionCheckShouldNotBeUsed { get; } = Diagnostic( "xUnit2011", "Do not use empty collection check", Assertions, Warning, "Do not use {0} to check for empty collections. Add element inspectors (for non-empty collections), or use Assert.Empty (for empty collections) instead." ); public static DiagnosticDescriptor X2012_AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck { get; } = Diagnostic( "xUnit2012", "Do not use boolean check to check if a value exists in a collection", Assertions, Warning, "Do not use {0} to check if a value exists in a collection. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2013_AssertEqualShouldNotBeUsedForCollectionSizeCheck { get; } = Diagnostic( "xUnit2013", "Do not use equality check to check for collection size.", Assertions, Warning, "Do not use {0} to check for collection size. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2014_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck { get; } = Diagnostic( "xUnit2014", "Do not use throws check to check for asynchronously thrown exception", Assertions, Error, "Do not use {0} to check for asynchronously thrown exceptions. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2015_AssertThrowsShouldUseGenericOverload { get; } = Diagnostic( "xUnit2015", "Do not use typeof expression to check the exception type", Assertions, Warning, "Do not use typeof({1}) expression to check the exception type. Use Assert.{0}<{1}> instead." ); public static DiagnosticDescriptor X2016_AssertEqualPrecisionShouldBeInRange { get; } = Diagnostic( "xUnit2016", "Keep precision in the allowed range when asserting equality of doubles or decimals.", Assertions, Error, "Keep precision in range {0} when asserting equality of {1} typed actual value." ); public static DiagnosticDescriptor X2017_AssertCollectionContainsShouldNotUseBoolCheck { get; } = Diagnostic( "xUnit2017", "Do not use Contains() to check if a value exists in a collection", Assertions, Warning, "Do not use {0} to check if a value exists in a collection. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2018_AssertIsTypeShouldNotBeUsedForAbstractType { get; } = Diagnostic( "xUnit2018", "Do not compare an object's exact type to an abstract class or interface", Assertions, Warning, "Do not compare an object's exact type to the {0} '{1}'. Use {2} instead." ); // Note: X2019 was already covered by X2014, and should not be reused public static DiagnosticDescriptor X2020_UseAssertFailInsteadOfBooleanAssert { get; } = Diagnostic( "xUnit2020", "Do not use always-failing boolean assertions", Assertions, Warning, "Do not use Assert.{0}({1}, message) to fail a test. Use Assert.Fail(message) instead." ); public static DiagnosticDescriptor X2021_AsyncAssertionsShouldBeAwaited { get; } = Diagnostic( "xUnit2021", "Async assertions should be awaited", Assertions, Error, "Assert.{0} is async. The resulting task should be awaited (or stored for later awaiting)." ); public static DiagnosticDescriptor X2022_BooleanAssertionsShouldNotBeNegated { get; } = Diagnostic( "xUnit2022", "Boolean assertions should not be negated", Assertions, Info, "Do not negate your value when calling Assert.{0}. Call Assert.{1} without the negation instead." ); public static DiagnosticDescriptor X2023_AssertSingleShouldBeUsedForSingleParameter { get; } = Diagnostic( "xUnit2023", "Do not use collection methods for single-item collections", Assertions, Info, "Do not use Assert.{0} if there is one element in the collection. Use Assert.Single instead." ); public static DiagnosticDescriptor X2024_BooleanAssertionsShouldNotBeUsedForSimpleEqualityCheck { get; } = Diagnostic( "xUnit2024", "Do not use boolean asserts for simple equality tests", Assertions, Info, "Do not use Assert.{0} to test equality against null, numeric, string, or enum literals. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2025_BooleanAssertionCanBeSimplified { get; } = Diagnostic( "xUnit2025", "The boolean assertion statement can be simplified", Assertions, Info, "The use of Assert.{0} can be simplified to avoid using a boolean literal value in an equality test." ); public static DiagnosticDescriptor X2026_SetsMustBeComparedWithEqualityComparer { get; } = Diagnostic( "xUnit2026", "Comparison of sets must be done with IEqualityComparer", Assertions, Warning, "Comparison of two sets may produce inconsistent results if GetHashCode() is not overriden. Consider using Assert.{0}(IEnumerable, IEnumerable, IEqualityComparer) instead." ); public static DiagnosticDescriptor X2027_SetsShouldNotBeComparedToLinearContainers { get; } = Diagnostic( "xUnit2027", "Comparison of sets to linear containers have undefined results", Assertions, Warning, "Comparing an instance of {0} with an instance of {1} has undefined results, because the order of items in the set is not predictable. Create a stable order for the set (i.e., by using OrderBy from Linq)." ); public static DiagnosticDescriptor X2028_DoNotUseAssertEmptyWithProblematicTypes { get; } = Diagnostic( "xUnit2028", "Do not use Assert.Empty or Assert.NotEmpty with problematic types", Assertions, Warning, "Using Assert.{0} with an instance of {1} is problematic, because {2}. Check the length with .Count instead." ); public static DiagnosticDescriptor X2029_AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck { get; } = Diagnostic( "xUnit2029", "Do not use Assert.Empty to check if a value does not exist in a collection", Assertions, Warning, "Do not use Assert.Empty to check if a value does not exist in a collection. Use Assert.DoesNotContain instead." ); public static DiagnosticDescriptor X2030_AssertNotEmptyShouldNotBeUsedForCollectionContainsCheck { get; } = Diagnostic( "xUnit2030", "Do not use Assert.NotEmpty to check if a value exists in a collection", Assertions, Warning, "Do not use Assert.NotEmpty to check if a value exists in a collection. Use Assert.Contains instead." ); public static DiagnosticDescriptor X2031_AssertSingleShouldUseTwoArgumentCall { get; } = Diagnostic( "xUnit2031", "Do not use Where clause with Assert.Single", Assertions, Warning, "Do not use a Where clause to filter before calling Assert.Single. Use the overload of Assert.Single that accepts a filtering function." ); public static DiagnosticDescriptor X2032_AssignableFromAssertionIsConfusinglyNamed { get; } = Diagnostic( "xUnit2032", "Type assertions based on 'assignable from' are confusingly named", Assertions, Info, "The naming of Assert.{0} can be confusing. An overload of Assert.{1} is available with an exact match flag which can be set to false to perform the same operation." ); // Placeholder for rule X2033 // Placeholder for rule X2034 // Placeholder for rule X2035 // Placeholder for rule X2036 // Placeholder for rule X2037 // Placeholder for rule X2038 // Placeholder for rule X2039 } ================================================ FILE: src/xunit.analyzers/Utility/Descriptors.xUnit3xxx.cs ================================================ using Microsoft.CodeAnalysis; using static Microsoft.CodeAnalysis.DiagnosticSeverity; using static Xunit.Analyzers.Category; namespace Xunit.Analyzers; public static partial class Descriptors { public static DiagnosticDescriptor X3000_CrossAppDomainClassesMustBeLongLivedMarshalByRefObject { get; } = Diagnostic( "xUnit3000", "Classes which cross AppDomain boundaries must derive directly or indirectly from LongLivedMarshalByRefObject", Extensibility, Error, "Class {0} must derive directly or indirectly from LongLivedMarshalByRefObject." ); public static DiagnosticDescriptor X3001_SerializableClassMustHaveParameterlessConstructor { get; } = Diagnostic( "xUnit3001", "Classes that are marked as serializable (or created by the test framework at runtime) must have a public parameterless constructor", Extensibility, Error, "Class {0} must have a public parameterless constructor to support {1}." ); public static DiagnosticDescriptor X3002_DoNotTestForConcreteTypeOfJsonSerializableTypes { get; } = Diagnostic( "xUnit3002", "Classes which are JSON serializable should not be tested for their concrete type", Extensibility, Warning, "Class {0} is JSON serializable and should not be tested for its concrete type. Test for its primary interface instead." ); public static DiagnosticDescriptor X3003_ProvideConstructorForFactAttributeOverride { get; } = Diagnostic( "xUnit3003", "Classes which extend FactAttribute (directly or indirectly) should provide a public constructor for source information", Extensibility, Warning, "Class {0} extends FactAttribute. It should include a public constructor for source information." ); // Placeholder for rule X3004 // Placeholder for rule X3005 // Placeholder for rule X3006 // Placeholder for rule X3007 // Placeholder for rule X3008 // Placeholder for rule X3009 } ================================================ FILE: src/xunit.analyzers/Utility/EmptyAssertContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class EmptyAssertContext : IAssertContext { EmptyAssertContext() { } public INamedTypeSymbol? AssertType => null; public static EmptyAssertContext Instance { get; } = new(); public bool SupportsAssertFail => false; public bool SupportsAssertNullWithPointers => false; public bool SupportsInexactTypeAssertions => false; public Version Version { get; } = new(); } ================================================ FILE: src/xunit.analyzers/Utility/EmptyCommonContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class EmptyCommonContext : ICommonContext { EmptyCommonContext() { } public static EmptyCommonContext Instance { get; } = new(); public INamedTypeSymbol? IMessageSinkType => null; public INamedTypeSymbol? ISourceInformationProviderType => null; public INamedTypeSymbol? ITestAssemblyType => null; public INamedTypeSymbol? ITestCaseType => null; public INamedTypeSymbol? ITestClassType => null; public INamedTypeSymbol? ITestCollectionType => null; public INamedTypeSymbol? ITestFrameworkDiscovererType => null; public INamedTypeSymbol? ITestFrameworkExecutorType => null; public INamedTypeSymbol? ITestFrameworkType => null; public INamedTypeSymbol? ITestMethodType => null; public INamedTypeSymbol? ITestType => null; public INamedTypeSymbol? IXunitSerializableType => null; public Version Version => new(); } ================================================ FILE: src/xunit.analyzers/Utility/EmptyCoreContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class EmptyCoreContext : ICoreContext { EmptyCoreContext() { } public INamedTypeSymbol? ClassDataAttributeType => null; public INamedTypeSymbol? CollectionAttributeType => null; public INamedTypeSymbol? CollectionDefinitionAttributeType => null; public INamedTypeSymbol? DataAttributeType => null; public INamedTypeSymbol? FactAttributeType => null; public INamedTypeSymbol? IClassFixtureType => null; public INamedTypeSymbol? ICollectionFixtureType => null; public INamedTypeSymbol? InlineDataAttributeType => null; public INamedTypeSymbol? ITestOutputHelperType => null; public static EmptyCoreContext Instance { get; } = new(); public INamedTypeSymbol? MemberDataAttributeType => null; public INamedTypeSymbol? TheoryAttributeType => null; public bool TheorySupportsConversionFromStringToDateTimeOffsetAndGuid => false; public bool TheorySupportsDefaultParameterValues => false; public bool TheorySupportsParameterArrays => false; public Version Version { get; } = new(); } ================================================ FILE: src/xunit.analyzers/Utility/EmptyRunnerUtilityContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class EmptyRunnerUtilityContext : IRunnerUtilityContext { EmptyRunnerUtilityContext() { } public static EmptyRunnerUtilityContext Instance { get; } = new(); public INamedTypeSymbol? LongLivedMarshalByRefObjectType => null; public string Platform => "N/A"; public Version Version { get; } = new(); } ================================================ FILE: src/xunit.analyzers/Utility/EnumerableExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using Xunit; /// /// Extension methods for . /// static partial class EnumerableExtensions { static readonly Func notNullTest = x => x is not null; public static void Add( this Dictionary> dictionary, TKey key, TValue value) where TKey : notnull { if (!dictionary.TryGetValue(key, out var list)) { list = []; dictionary[key] = list; } list.Add(value); } public static void AddRange( this HashSet hashSet, IEnumerable source) { Guard.ArgumentNotNull(hashSet); Guard.ArgumentNotNull(source); foreach (var item in source) hashSet.Add(item); } /// /// Returns as an enumerable of with /// all the null items removed. /// public static IEnumerable WhereNotNull(this IEnumerable source) where T : class => Guard.ArgumentNotNull(source).Where((Func)notNullTest)!; } ================================================ FILE: src/xunit.analyzers/Utility/IAssertContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public interface IAssertContext { /// /// Gets a reference to type Assert, if available. /// INamedTypeSymbol? AssertType { get; } /// /// Gets a flag indicating whether Assert.Fail is supported. /// bool SupportsAssertFail { get; } /// /// Gets a flag indicating whether Assert.Null and Assert.NotNull supports /// unsafe pointers. /// bool SupportsAssertNullWithPointers { get; } /// /// Gets a flag indicating whether Assert.IsType and Assert.IsNotType /// support inexact matches (soft-deprecating Assert.IsAssignableFrom /// and Assert.IsNotAssignableFrom). /// bool SupportsInexactTypeAssertions { get; } /// /// Gets the version number of the assertion assembly. /// Version Version { get; } } ================================================ FILE: src/xunit.analyzers/Utility/ICommonContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; /// /// Context for types that that originated in xunit.abstractions in v2, and moved in v3 /// to one of xunit.v3.common, xunit.v3.core, or xunit.v3.runner.common. /// public interface ICommonContext { /// /// Gets a reference to type IMessageSink, if available. /// INamedTypeSymbol? IMessageSinkType { get; } /// /// Gets a reference to type ISourceInformationProvider, if available. /// INamedTypeSymbol? ISourceInformationProviderType { get; } /// /// Gets a reference to type ITestAssembly, if available. /// INamedTypeSymbol? ITestAssemblyType { get; } /// /// Gets a reference to type ITestCase, if available. /// INamedTypeSymbol? ITestCaseType { get; } /// /// Gets a reference to type ITestClass, if available. /// INamedTypeSymbol? ITestClassType { get; } /// /// Gets a reference to type ITestCollection, if available. /// INamedTypeSymbol? ITestCollectionType { get; } /// /// Gets a reference to type ITestFrameworkDiscoverer, if available. /// INamedTypeSymbol? ITestFrameworkDiscovererType { get; } /// /// Gets a reference to type ITestFrameworkExecutor, if available. /// INamedTypeSymbol? ITestFrameworkExecutorType { get; } /// /// Gets a reference to type ITestFramework, if available. /// INamedTypeSymbol? ITestFrameworkType { get; } /// /// Gets a reference to type ITestMethod, if available. /// INamedTypeSymbol? ITestMethodType { get; } /// /// Gets a reference to type ITest, if available. /// INamedTypeSymbol? ITestType { get; } /// /// Gets a reference to type IXunitSerializable, if available. /// INamedTypeSymbol? IXunitSerializableType { get; } /// /// Gets the version number of the xunit.abstractions or xunit.v3.common assembly. /// public Version Version { get; } } ================================================ FILE: src/xunit.analyzers/Utility/ICoreContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public interface ICoreContext { /// /// Gets a reference to type ClassDataAttribute, if available. /// INamedTypeSymbol? ClassDataAttributeType { get; } /// /// Gets a reference to type CollectionAttribute, if available. /// INamedTypeSymbol? CollectionAttributeType { get; } /// /// Gets a reference to type CollectionDefinitionAttribute, if available. /// INamedTypeSymbol? CollectionDefinitionAttributeType { get; } /// /// Gets a reference to type DataAttribute, if available. /// INamedTypeSymbol? DataAttributeType { get; } /// /// Gets a reference to type FactAttribute, if available. /// INamedTypeSymbol? FactAttributeType { get; } /// /// Gets a reference to type IClassFixture<T>, if available. /// INamedTypeSymbol? IClassFixtureType { get; } /// /// Gets a reference to type ICollectionFixture<T>, if available. /// INamedTypeSymbol? ICollectionFixtureType { get; } /// /// Gets a reference to type InlineDataAttribute, if available. /// INamedTypeSymbol? InlineDataAttributeType { get; } /// /// Gets a reference to type ITestOutputHelper, if available. /// INamedTypeSymbol? ITestOutputHelperType { get; } /// /// Gets a reference to type MemberDataAttribute, if available. /// INamedTypeSymbol? MemberDataAttributeType { get; } /// /// Gets a reference to type TheoryAttribute, if available. /// INamedTypeSymbol? TheoryAttributeType { get; } /// /// Gets a flag indicating whether theory data can be automatically converted from a /// value into a or a . /// bool TheorySupportsConversionFromStringToDateTimeOffsetAndGuid { get; } /// /// Gets a flag indicating whether theory methods support default parameter values. /// bool TheorySupportsDefaultParameterValues { get; } /// /// Gets a flag indicating whether theory methods support params arrays. /// bool TheorySupportsParameterArrays { get; } /// /// Gets the version number of the core assembly. /// Version Version { get; } } ================================================ FILE: src/xunit.analyzers/Utility/IRunnerUtilityContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public interface IRunnerUtilityContext { /// /// Gets a reference to type Xunit.Sdk.LongLivedMarshalByRefObject, if available. /// INamedTypeSymbol? LongLivedMarshalByRefObjectType { get; } /// /// Gets a description of the target platform for the runner utility (i.e., "net452"). This is /// typically extracted from the assembly name (i.e., "xunit.runner.utility.net452"). /// string Platform { get; } /// /// Gets the version number of the runner utility assembly. /// Version Version { get; } } ================================================ FILE: src/xunit.analyzers/Utility/Serializability.cs ================================================ namespace Xunit.Analyzers; public enum Serializability { NeverSerializable, PossiblySerializable, AlwaysSerializable } ================================================ FILE: src/xunit.analyzers/Utility/SerializabilityAnalyzer.cs ================================================ using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public sealed class SerializabilityAnalyzer(SerializableTypeSymbols typeSymbols) { static readonly Version Version_3_0_1 = new(3, 0, 1); /// /// Analyze the given type to determine whether it is always, possibly, or never serializable. /// /// /// The logic in this method corresponds to the logic in SerializationHelper.IsSerializable /// and SerializationHelper.Serialize. /// public Serializability AnalayzeSerializability( ITypeSymbol type, XunitContext xunitContext) { Guard.ArgumentNotNull(xunitContext); type = type.UnwrapNullable(); if (GetTypeKindSerializability(type.TypeKind) == Serializability.NeverSerializable) return Serializability.NeverSerializable; if (type.TypeKind == TypeKind.Array && type is IArrayTypeSymbol arrayType) return AnalayzeSerializability(arrayType.ElementType, xunitContext); if (typeSymbols.Type.IsAssignableFrom(type)) return Serializability.AlwaysSerializable; if (type.Equals(typeSymbols.TraitDictionary, SymbolEqualityComparer.Default)) return Serializability.AlwaysSerializable; if (typeSymbols.IXunitSerializable.IsAssignableFrom(type)) return Serializability.AlwaysSerializable; if (type.SpecialType != SpecialType.None) return GetSpecialTypeSerializability(type.SpecialType); if (type.Equals(typeSymbols.BigInteger, SymbolEqualityComparer.Default) || type.Equals(typeSymbols.DateTimeOffset, SymbolEqualityComparer.Default) || type.Equals(typeSymbols.TimeSpan, SymbolEqualityComparer.Default) || type.Equals(typeSymbols.DateOnly, SymbolEqualityComparer.Default) || type.Equals(typeSymbols.TimeOnly, SymbolEqualityComparer.Default)) return Serializability.AlwaysSerializable; if (xunitContext.HasV3References) { if (type.Equals(typeSymbols.Guid, SymbolEqualityComparer.Default) || type.Equals(typeSymbols.Index, SymbolEqualityComparer.Default) || type.Equals(typeSymbols.Range, SymbolEqualityComparer.Default) || type.Equals(typeSymbols.Uri, SymbolEqualityComparer.Default) || type.Equals(typeSymbols.Version, SymbolEqualityComparer.Default)) return Serializability.AlwaysSerializable; if (typeSymbols.IFormattable.IsAssignableFrom(type) && typeSymbols.IParsableOfT is not null) { var iParsableOfSelf = typeSymbols.IParsableOfT.Construct(type); if (iParsableOfSelf.IsAssignableFrom(type)) return Serializability.AlwaysSerializable; } if (xunitContext.V3Core?.Version >= Version_3_0_1 && typeSymbols.ITuple is not null && typeSymbols.ITuple.IsAssignableFrom(type)) return Serializability.AlwaysSerializable; } if (typeSymbols.TypesWithCustomSerializers.Any(t => t.IsAssignableFrom(type))) return Serializability.AlwaysSerializable; if (type.TypeKind == TypeKind.Class && !type.IsSealed) return Serializability.PossiblySerializable; if (type.TypeKind == TypeKind.Interface) return Serializability.PossiblySerializable; if (type.TypeKind == TypeKind.Enum) return Serializability.PossiblySerializable; return Serializability.NeverSerializable; } static Serializability GetSpecialTypeSerializability(SpecialType type) => type switch { SpecialType.System_String or SpecialType.System_Char or SpecialType.System_Byte or SpecialType.System_SByte or SpecialType.System_Int16 or SpecialType.System_UInt16 or SpecialType.System_Int32 or SpecialType.System_UInt32 or SpecialType.System_Int64 or SpecialType.System_UInt64 or SpecialType.System_Single or SpecialType.System_Double or SpecialType.System_Decimal or SpecialType.System_Boolean or SpecialType.System_DateTime => Serializability.AlwaysSerializable, SpecialType.None or SpecialType.System_Object or SpecialType.System_Array or SpecialType.System_Enum or SpecialType.System_ValueType or SpecialType.System_Nullable_T or SpecialType.System_Collections_IEnumerable or SpecialType.System_IDisposable => Serializability.PossiblySerializable, _ => Serializability.NeverSerializable }; static Serializability GetTypeKindSerializability(TypeKind kind) => kind switch { TypeKind.Array or TypeKind.Class or TypeKind.Enum or TypeKind.Interface or TypeKind.Struct => Serializability.PossiblySerializable, _ => Serializability.NeverSerializable }; static bool TypeKindShouldBeIgnored(TypeKind kind) => kind switch { TypeKind.Unknown or TypeKind.Enum or TypeKind.Error or TypeKind.Module or TypeKind.TypeParameter or TypeKind.Submission => true, _ => false }; /// /// Determine whether the given type should be ignored when analyzing serializability. /// Types are ignored by type kind (and special type for ). /// Arrays and generic types are ignored if they are composed of ignored types, recursively. /// /// /// Enumerations are serializable if and only if they are not from the Global Assembly Cache, /// which exists in .NET Framework only. However, static analysis cannot reliably determine /// whether a type is from a local assembly or the GAC. Therefore, /// and are ignored, in order to prevent a diagnostic from /// being always found for all enumeration types. /// public bool TypeShouldBeIgnored([NotNullWhen(false)] ITypeSymbol? type) { if (type is null) return true; if (TypeKindShouldBeIgnored(type.TypeKind) || type.SpecialType == SpecialType.System_Enum) return true; if (type is IArrayTypeSymbol arrayType) return TypeShouldBeIgnored(arrayType.ElementType); if (type is INamedTypeSymbol namedType && namedType.IsGenericType) return namedType.TypeArguments.Where(TypeShouldBeIgnored).Any(); return false; } } ================================================ FILE: src/xunit.analyzers/Utility/SerializableTypeSymbols.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public sealed class SerializableTypeSymbols { readonly Lazy bigInteger; readonly Lazy dateOnly; readonly Lazy dateTimeOffset; readonly Lazy guid; readonly Lazy iFormattable; readonly Lazy index; readonly Lazy iParsableOfT; readonly Lazy iTuple; readonly Lazy iXunitSerializable; readonly Lazy range; readonly Lazy theoryDataBaseType; readonly Dictionary theoryDataTypes; readonly Lazy timeOnly; readonly Lazy timeSpan; readonly Lazy traitDictionary; readonly Lazy type; readonly Lazy> typesWithCustomSerializers; readonly Lazy uri; readonly Lazy version; SerializableTypeSymbols( Compilation compilation, XunitContext xunitContext, INamedTypeSymbol classDataAttribute, INamedTypeSymbol dataAttribute, INamedTypeSymbol memberDataAttribute, INamedTypeSymbol theoryAttribute, Dictionary theoryDataTypes) { this.theoryDataTypes = theoryDataTypes; bigInteger = new(() => TypeSymbolFactory.BigInteger(compilation)); dateOnly = new(() => TypeSymbolFactory.DateOnly(compilation)); dateTimeOffset = new(() => TypeSymbolFactory.DateTimeOffset(compilation)); guid = new(() => TypeSymbolFactory.Guid(compilation)); iFormattable = new(() => TypeSymbolFactory.IFormattable(compilation)); index = new(() => TypeSymbolFactory.Index(compilation)); iParsableOfT = new(() => TypeSymbolFactory.IParsableOfT(compilation)); iTuple = new(() => TypeSymbolFactory.ITuple(compilation)); iXunitSerializable = new(() => xunitContext.Common.IXunitSerializableType); range = new(() => TypeSymbolFactory.Range(compilation)); // For v2 and early versions of v3, the base type is "TheoryData" (non-generic). For later versions // of v3, it's "TheoryDataBase". In either case, getting "TheoryData" // and going up one layer gets us the type we want to be able to search for. theoryDataBaseType = new(() => TheoryData(arity: 1)?.BaseType); timeOnly = new(() => TypeSymbolFactory.TimeOnly(compilation)); timeSpan = new(() => TypeSymbolFactory.TimeSpan(compilation)); traitDictionary = new(() => GetTraitDictionary(compilation)); type = new(() => TypeSymbolFactory.Type(compilation)); typesWithCustomSerializers = new(() => { var registerXunitSerializer = TypeSymbolFactory.RegisterXunitSerializerAttribute_V3(compilation); if (registerXunitSerializer is null) #pragma warning disable IDE0301 // Cannot convert this due to Roslyn 3.11 return ImmutableArray.Empty; #pragma warning restore IDE0301 return compilation .Assembly .GetAttributes() .Where(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, registerXunitSerializer) && a.ConstructorArguments.Length > 1 && a.ConstructorArguments[1].Kind == TypedConstantKind.Array) .SelectMany(a => a.ConstructorArguments[1].Values.Select(v => v.Value as INamedTypeSymbol)) .WhereNotNull() .ToImmutableArray(); }); uri = new(() => TypeSymbolFactory.Uri(compilation)); version = new(() => TypeSymbolFactory.Version(compilation)); ClassDataAttribute = classDataAttribute; DataAttribute = dataAttribute; MemberDataAttribute = memberDataAttribute; TheoryAttribute = theoryAttribute; } public INamedTypeSymbol? BigInteger => bigInteger.Value; public INamedTypeSymbol ClassDataAttribute { get; } public INamedTypeSymbol DataAttribute { get; } public INamedTypeSymbol? DateOnly => dateOnly.Value; public INamedTypeSymbol? DateTimeOffset => dateTimeOffset.Value; public INamedTypeSymbol? Guid => guid.Value; public INamedTypeSymbol? IFormattable => iFormattable.Value; public INamedTypeSymbol? Index => index.Value; public INamedTypeSymbol? IParsableOfT => iParsableOfT.Value; public INamedTypeSymbol? ITuple => iTuple.Value; public INamedTypeSymbol? IXunitSerializable => iXunitSerializable.Value; public INamedTypeSymbol MemberDataAttribute { get; } public INamedTypeSymbol? Range => range.Value; public INamedTypeSymbol TheoryAttribute { get; } public INamedTypeSymbol? TheoryDataBaseType => theoryDataBaseType.Value; public INamedTypeSymbol? TimeOnly => timeOnly.Value; public INamedTypeSymbol? TimeSpan => timeSpan.Value; public INamedTypeSymbol? TraitDictionary => traitDictionary.Value; public INamedTypeSymbol? Type => type.Value; public ImmutableArray TypesWithCustomSerializers => typesWithCustomSerializers.Value; public INamedTypeSymbol? Uri => uri.Value; public INamedTypeSymbol? Version => version.Value; public static SerializableTypeSymbols? Create( Compilation compilation, XunitContext xunitContext) { Guard.ArgumentNotNull(compilation); Guard.ArgumentNotNull(xunitContext); if (xunitContext.Core.TheoryAttributeType is not INamedTypeSymbol theoryAttribute) return null; if (xunitContext.Core.DataAttributeType is not INamedTypeSymbol dataAttribute) return null; if (xunitContext.Core.ClassDataAttributeType is not INamedTypeSymbol classDataAttribute) return null; if (xunitContext.Core.MemberDataAttributeType is not INamedTypeSymbol memberDataAttribute) return null; return new SerializableTypeSymbols( compilation, xunitContext, classDataAttribute, dataAttribute, memberDataAttribute, theoryAttribute, TypeSymbolFactory.TheoryData_ByGenericArgumentCount(compilation) ); } static INamedTypeSymbol? GetTraitDictionary(Compilation compilation) { if (TypeSymbolFactory.DictionaryofTKeyTValue(compilation) is not INamedTypeSymbol dictionaryType) return null; if (TypeSymbolFactory.ListOfT(compilation) is not INamedTypeSymbol listType) return null; var stringType = compilation.GetSpecialType(SpecialType.System_String); var listOfStringType = listType.Construct(stringType); return dictionaryType.Construct(stringType, listOfStringType); } public INamedTypeSymbol? TheoryData(int arity) { theoryDataTypes.TryGetValue(arity, out var result); return result; } } ================================================ FILE: src/xunit.analyzers/Utility/SymbolExtensions.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public static class SymbolExtensions { public static bool ContainsAttributeType( this ImmutableArray attributes, INamedTypeSymbol attributeType, bool exactMatch = false) => attributes.Any(a => a.IsInstanceOf(attributeType, exactMatch)); /// /// If the passed is , /// then returns the enumerable type (aka, T); otherwise, returns null. /// public static ITypeSymbol? GetAsyncEnumerableType(this ITypeSymbol? typeSymbol) { if (typeSymbol is not INamedTypeSymbol namedTypeSymbol) return null; // Ideally we'd use symbol comparison here, but that would require threading the // Compilation object here, and with 44 callers to IsAssignableFrom that seemed // like an unnecessarily daunting task. We cross fingers that this is good. :) if (namedTypeSymbol.Name == "IAsyncEnumerable" && namedTypeSymbol.ContainingNamespace.ToString() == "System.Collections.Generic") return namedTypeSymbol.TypeArguments[0]; return null; } /// /// If the passed is , then returns /// the enumerable type (aka, T); otherwise, returns null. /// public static ITypeSymbol? GetEnumerableType(this ITypeSymbol? typeSymbol) { if (typeSymbol is not INamedTypeSymbol namedTypeSymbol) return null; if (namedTypeSymbol.OriginalDefinition.SpecialType != SpecialType.System_Collections_Generic_IEnumerable_T) return null; return namedTypeSymbol.TypeArguments[0]; } public static INamedTypeSymbol? GetGenericInterfaceImplementation( this ITypeSymbol implementingType, INamedTypeSymbol openInterfaceType) { Guard.ArgumentNotNull(implementingType); Guard.ArgumentNotNull(openInterfaceType); if (SymbolEqualityComparer.Default.Equals(implementingType.OriginalDefinition, openInterfaceType)) return implementingType as INamedTypeSymbol; return implementingType.AllInterfaces.FirstOrDefault(i => SymbolEqualityComparer.Default.Equals(i.OriginalDefinition, openInterfaceType)); } public static ISymbol? GetMember( this INamespaceOrTypeSymbol namespaceOrType, string name) => Guard.ArgumentNotNull(namespaceOrType).GetMembers(name).FirstOrDefault(); public static ImmutableArray GetInheritedAndOwnMembers( this ITypeSymbol? symbol, string? name = null) { var builder = ImmutableArray.CreateBuilder(); while (symbol is not null) { builder.AddRange(name is null ? symbol.GetMembers() : symbol.GetMembers(name)); symbol = symbol.BaseType; } return builder.ToImmutable(); } public static bool IsAssignableFrom( this ITypeSymbol? targetType, ITypeSymbol? sourceType, bool exactMatch = false) { if (targetType is not null) { var targetEnumerableType = targetType.GetEnumerableType(); var targetAsyncEnumerableType = targetType.GetAsyncEnumerableType(); while (sourceType is not null) { if (SymbolEqualityComparer.Default.Equals(sourceType, targetType)) return true; if (exactMatch) return false; // Special handling for IEnumerable == IEnumerable, because the Ts are covariant and the // symbol equality comparerer is an exact comparison, not a compatibility test. if (targetEnumerableType is not null) { var sourceEnumerableType = sourceType.GetEnumerableType(); if (sourceEnumerableType is not null) return IsAssignableFrom(targetEnumerableType, sourceEnumerableType); } // Special handling for IAsyncEnumerable == IAsyncEnumerable as well if (targetAsyncEnumerableType is not null) { var sourceAsyncEnumerableType = sourceType.GetAsyncEnumerableType(); if (sourceAsyncEnumerableType is not null) return IsAssignableFrom(targetAsyncEnumerableType, sourceAsyncEnumerableType); } // Special handling for tuples as tuples with differently named fields are still assignable if (targetType.IsTupleType && sourceType.IsTupleType) { var targetTupleType = ((INamedTypeSymbol)targetType).TupleUnderlyingType ?? targetType; var sourceTupleType = ((INamedTypeSymbol)sourceType).TupleUnderlyingType ?? sourceType; return SymbolEqualityComparer.Default.Equals(sourceTupleType, targetTupleType); } // Special handling for generic types, possibly with type constraints if (targetType is INamedTypeSymbol namedTargetType && namedTargetType.IsGenericType && sourceType is INamedTypeSymbol namedSourceType && namedSourceType.IsGenericType) { if (SymbolEqualityComparer.Default.Equals(namedTargetType.OriginalDefinition, namedSourceType.OriginalDefinition)) { // Unbound generic types (e.g., List<>) are assignable from any open/closed generic of the same definition (e.g., List or List) if (namedTargetType.IsUnboundGenericType) return true; // Open/closed generic types must have compatible type arguments bool argumentsMatch = true; for (int i = 0; i < namedTargetType.TypeArguments.Length; i++) { var tArg = namedTargetType.TypeArguments[i]; var sArg = namedSourceType.TypeArguments[i]; if (tArg.TypeKind == TypeKind.TypeParameter) continue; // Recursively verify nested arguments (e.g., IEnumerable inside Task>) if (!IsAssignableFrom(tArg, sArg, exactMatch)) { argumentsMatch = false; break; } } if (argumentsMatch) return true; } } if (targetType.TypeKind == TypeKind.Interface) return sourceType.AllInterfaces.Any(i => IsAssignableFrom(targetType, i)); sourceType = sourceType.BaseType; } } return false; } public static bool IsInstanceOf( this AttributeData attribute, INamedTypeSymbol attributeType, bool exactMatch = false) => Guard.ArgumentNotNull(attributeType).IsAssignableFrom( Guard.ArgumentNotNull(attribute).AttributeClass, exactMatch ); public static ITypeSymbol? UnwrapEnumerable( this ITypeSymbol? type, Compilation compilation, bool unwrapArray = false) { if (type is null) return null; var iEnumerableOfT = TypeSymbolFactory.IEnumerableOfT(compilation); var result = UnwrapEnumerable(type, iEnumerableOfT, unwrapArray); if (result is null) { var iAsyncEnumerableOfT = TypeSymbolFactory.IAsyncEnumerableOfT(compilation); if (iAsyncEnumerableOfT is not null) result = UnwrapEnumerable(type, iAsyncEnumerableOfT, unwrapArray); } return result; } public static ITypeSymbol? UnwrapEnumerable( this ITypeSymbol? type, ITypeSymbol enumerableType, bool unwrapArray = false) { if (type is null) return null; if (unwrapArray && type is IArrayTypeSymbol arrayType) return arrayType.ElementType; IEnumerable interfaces = type.AllInterfaces; if (type is INamedTypeSymbol namedType) interfaces = interfaces.Concat([namedType]); foreach (var @interface in interfaces) if (SymbolEqualityComparer.Default.Equals(@interface.OriginalDefinition, enumerableType)) return @interface.TypeArguments[0]; return null; } public static ITypeSymbol UnwrapNullable(this ITypeSymbol type) { Guard.ArgumentNotNull(type); if (type is not INamedTypeSymbol namedType) return type; if (namedType.IsGenericType && namedType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) return namedType.TypeArguments[0]; return type; } } ================================================ FILE: src/xunit.analyzers/Utility/SyntaxExtensions.cs ================================================ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Xunit.Analyzers; public static class SyntaxExtensions { public static bool ContainsAttributeType( this SyntaxList attributeLists, SemanticModel semanticModel, INamedTypeSymbol attributeType, bool exactMatch = false) { Guard.ArgumentNotNull(semanticModel); Guard.ArgumentNotNull(attributeType); foreach (var attributeList in attributeLists) { foreach (var attribute in attributeList.Attributes) { var type = semanticModel.GetTypeInfo(attribute).Type; if (attributeType.IsAssignableFrom(type, exactMatch)) return true; } } return false; } public static SimpleNameSyntax? GetSimpleName(this InvocationExpressionSyntax invocation) => Guard.ArgumentNotNull(invocation).Expression switch { MemberAccessExpressionSyntax memberAccess => memberAccess.Name, SimpleNameSyntax simpleName => simpleName, _ => null, }; public static bool IsEnumValueExpression( this ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken = default) { Guard.ArgumentNotNull(expression); Guard.ArgumentNotNull(semanticModel); if (!expression.IsKind(SyntaxKind.SimpleMemberAccessExpression)) return false; var symbol = semanticModel.GetSymbolInfo(expression, cancellationToken).Symbol; return symbol?.Kind == SymbolKind.Field && symbol.ContainingType.TypeKind == TypeKind.Enum; } public static bool IsNameofExpression( this ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken = default) { Guard.ArgumentNotNull(expression); Guard.ArgumentNotNull(semanticModel); if (!expression.IsKind(SyntaxKind.InvocationExpression)) return false; if (expression is not InvocationExpressionSyntax invocation) return false; if (invocation.ArgumentList.Arguments.Count != 1) return false; if ((invocation.Expression as IdentifierNameSyntax)?.Identifier.ValueText != "nameof") return false; // A real nameof expression doesn't have a matching symbol, but it does have the string type return semanticModel.GetSymbolInfo(expression, cancellationToken).Symbol is null && semanticModel.GetTypeInfo(expression, cancellationToken).Type?.SpecialType == SpecialType.System_String; } } ================================================ FILE: src/xunit.analyzers/Utility/TypeHierarchyComparer.cs ================================================ using System; using System.Collections.Generic; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class TypeHierarchyComparer : IComparer { TypeHierarchyComparer() { } public static TypeHierarchyComparer Instance { get; } = new TypeHierarchyComparer(); public int Compare( ITypeSymbol? x, ITypeSymbol? y) { Guard.ArgumentValid("The argument must be a class", x?.TypeKind == TypeKind.Class, nameof(x)); Guard.ArgumentValid("The argument must be a class", y?.TypeKind == TypeKind.Class, nameof(x)); if (SymbolEqualityComparer.Default.Equals(x, y)) return 0; if (x.IsAssignableFrom(y)) return -1; if (y.IsAssignableFrom(x)) return 1; throw new InvalidOperationException("Encountered types not in a hierarchy"); } } ================================================ FILE: src/xunit.analyzers/Utility/TypeSymbolFactory.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public static class TypeSymbolFactory { public static INamedTypeSymbol? Action(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Action"); public static INamedTypeSymbol? Action( Compilation compilation, int arity = 1) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName($"System.Action`{ValidateArity(arity, min: 1, max: 16)}"); public static INamedTypeSymbol? ArraySegmentOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.ArraySegment`1"); public static INamedTypeSymbol? AssemblyFixtureAttribute_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.AssemblyFixtureAttribute_V3); public static INamedTypeSymbol? Assert(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.Assert); public static INamedTypeSymbol? AttributeUsageAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.AttributeUsageAttribute"); public static INamedTypeSymbol? BigInteger(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Numerics.BigInteger"); public static INamedTypeSymbol? CallerFilePathAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Runtime.CompilerServices.CallerFilePathAttribute"); public static INamedTypeSymbol? CallerLineNumberAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Runtime.CompilerServices.CallerLineNumberAttribute"); public static INamedTypeSymbol? CancellationToken(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Threading.CancellationToken"); public static INamedTypeSymbol? ClassDataAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ClassDataAttribute); public static INamedTypeSymbol? ClassDataAttributeOfT_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ClassDataAttributeOfT_V3); public static INamedTypeSymbol? CollectionAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.CollectionAttribute); public static INamedTypeSymbol? CollectionAttributeOfT_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.CollectionAttributeOfT_V3); public static INamedTypeSymbol? CollectionDefinitionAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.CollectionDefinitionAttribute); public static INamedTypeSymbol? ConfigureAwaitOptions(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Threading.Tasks.ConfigureAwaitOptions"); public static INamedTypeSymbol? ConfiguredTaskAwaitable(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Runtime.CompilerServices.ConfiguredTaskAwaitable"); public static INamedTypeSymbol? DataAttribute_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.DataAttribute_V2); public static INamedTypeSymbol? DataAttribute_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.DataAttribute_V3); public static INamedTypeSymbol? DateOnly(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.DateOnly"); public static INamedTypeSymbol? DateTimeOffset(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.DateTimeOffset"); public static INamedTypeSymbol? DictionaryofTKeyTValue(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Collections.Generic.Dictionary`2"); public static INamedTypeSymbol? FactAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.FactAttribute); public static INamedTypeSymbol? Func( Compilation compilation, int arity = 1) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName($"System.Func`{ValidateArity(arity, min: 1, max: 17)}"); public static INamedTypeSymbol? Guid(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Guid"); public static INamedTypeSymbol? IAssemblyInfo_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IAssemblyInfo_V2); public static INamedTypeSymbol? IAsyncEnumerableOfITheoryDataRow(Compilation compilation) { var iAsyncEnumerableOfT = IAsyncEnumerableOfT(compilation); if (iAsyncEnumerableOfT is null) return null; var iTheoryDataRow = ITheoryDataRow_V3(compilation); if (iTheoryDataRow is null) return null; return iAsyncEnumerableOfT.Construct(iTheoryDataRow); } public static INamedTypeSymbol? IAsyncEnumerableOfObjectArray(Compilation compilation) { var iAsyncEnumerableOfT = IAsyncEnumerableOfT(compilation); if (iAsyncEnumerableOfT is null) return null; var objectArray = ObjectArray(compilation); return iAsyncEnumerableOfT.Construct(objectArray); } public static INamedTypeSymbol? IAsyncEnumerableOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Collections.Generic.IAsyncEnumerable`1"); public static INamedTypeSymbol? IAsyncEnumerableOfTuple(Compilation compilation) { var iTuple = ITuple(compilation); if (iTuple is null) return null; return IAsyncEnumerableOfT(compilation)?.Construct(iTuple); } public static INamedTypeSymbol? IAsyncLifetime(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IAsyncLifetime); public static INamedTypeSymbol? IAttributeInfo_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IAttributeInfo_V2); public static INamedTypeSymbol? IClassFixureOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IClassFixtureOfT); public static INamedTypeSymbol? ICollection(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Collections.ICollection"); public static INamedTypeSymbol? ICollectionFixtureOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ICollectionFixtureOfT); public static INamedTypeSymbol ICollectionOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetSpecialType(SpecialType.System_Collections_Generic_ICollection_T); public static INamedTypeSymbol? ICriticalNotifyCompletion(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Runtime.CompilerServices.ICriticalNotifyCompletion"); public static INamedTypeSymbol? IDataAttribute_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IDataAttribute_V3); public static INamedTypeSymbol IDisposable(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetSpecialType(SpecialType.System_IDisposable); public static INamedTypeSymbol? IEnumerableOfITheoryDataRow(Compilation compilation) { var iTheoryDataRow = ITheoryDataRow_V3(compilation); if (iTheoryDataRow is null) return null; return IEnumerableOfT(compilation).Construct(iTheoryDataRow); } public static INamedTypeSymbol IEnumerableOfObjectArray(Compilation compilation) => IEnumerableOfT(compilation).Construct(ObjectArray(compilation)); public static INamedTypeSymbol IEnumerableOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T); public static INamedTypeSymbol? IEnumerableOfTuple(Compilation compilation) { var iTuple = ITuple(compilation); if (iTuple is null) return null; return IEnumerableOfT(compilation).Construct(iTuple); } public static INamedTypeSymbol? IFormattable(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.IFormattable"); public static INamedTypeSymbol? IMessageSink_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IMessageSink_V2); public static INamedTypeSymbol? IMessageSink_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IMessageSink_V3); public static INamedTypeSymbol? IMessageSinkMessage_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IMessageSinkMessage_V2); public static INamedTypeSymbol? IMethodInfo_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IMethodInfo_V2); public static INamedTypeSymbol? Index(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Index"); public static INamedTypeSymbol? InlineDataAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.InlineDataAttribute); public static INamedTypeSymbol? IParameterInfo_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IParameterInfo_V2); public static INamedTypeSymbol? IParsableOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.IParsable`1"); public static INamedTypeSymbol IReadOnlyCollectionOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetSpecialType(SpecialType.System_Collections_Generic_IReadOnlyCollection_T); public static INamedTypeSymbol? IReadOnlySetOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Collections.Generic.IReadOnlySet`1"); public static INamedTypeSymbol? IRunnerReporter_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IRunnerReporter_V3); public static INamedTypeSymbol? ISetOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Collections.Generic.ISet`1"); public static INamedTypeSymbol? ISourceInformation_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ISourceInformation_V2); public static INamedTypeSymbol? ISourceInformationProvider_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ISourceInformationProvider_V2); public static INamedTypeSymbol? ISourceInformationProvider_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ISourceInformationProvider_V3); public static INamedTypeSymbol? ITest_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITest_V2); public static INamedTypeSymbol? ITest_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITest_V3); public static INamedTypeSymbol? ITestAssembly_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestAssembly_V2); public static INamedTypeSymbol? ITestAssembly_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestAssembly_V3); public static INamedTypeSymbol? ITestCase_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestCase_V2); public static INamedTypeSymbol? ITestCase_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestCase_V3); public static INamedTypeSymbol? ITestClass_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestClass_V2); public static INamedTypeSymbol? ITestClass_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestClass_V3); public static INamedTypeSymbol? ITestCollection_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestCollection_V2); public static INamedTypeSymbol? ITestCollection_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestCollection_V3); public static INamedTypeSymbol? ITestContextAccessor_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestContextAccessor_V3); public static INamedTypeSymbol? ITestFramework_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestFramework_V2); public static INamedTypeSymbol? ITestFramework_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestFramework_V3); public static INamedTypeSymbol? ITestFrameworkDiscoverer_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestFrameworkDiscoverer_V2); public static INamedTypeSymbol? ITestFrameworkDiscoverer_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestFrameworkDiscoverer_V3); public static INamedTypeSymbol? ITestFrameworkExecutor_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestFrameworkExecutor_V2); public static INamedTypeSymbol? ITestFrameworkExecutor_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestFrameworkExecutor_V3); public static INamedTypeSymbol? ITestMethod_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestMethod_V2); public static INamedTypeSymbol? ITestMethod_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestMethod_V3); public static INamedTypeSymbol? ITestOutputHelper_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestOutputHelper_V2); public static INamedTypeSymbol? ITestOutputHelper_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITestOutputHelper_V3); public static INamedTypeSymbol? ITheoryDataRow_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITheoryDataRow_V3); public static INamedTypeSymbol? ITuple(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Runtime.CompilerServices.ITuple"); public static INamedTypeSymbol? ITypeInfo_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITypeInfo_V2); public static INamedTypeSymbol? IValueTaskSource(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Threading.Tasks.Sources.IValueTaskSource"); public static INamedTypeSymbol? IValueTaskSourceOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Threading.Tasks.Sources.IValueTaskSource`1"); public static INamedTypeSymbol? IXunitSerializable_V2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IXunitSerializable_V2); public static INamedTypeSymbol? IXunitSerializable_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IXunitSerializable_V3); public static INamedTypeSymbol? IXunitSerializer_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IXunitSerializer_V3); public static INamedTypeSymbol? JsonTypeIDAttribute_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.JsonTypeIDAttribute_V3); public static INamedTypeSymbol? ListOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Collections.Generic.List`1"); public static INamedTypeSymbol? LongLivedMarshalByRefObject_ExecutionV2(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.LongLivedMarshalByRefObject_Execution_V2); public static INamedTypeSymbol? LongLivedMarshalByRefObject_RunnerUtility(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.LongLivedMarshalByRefObject_RunnerUtility); public static INamedTypeSymbol? MemberDataAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.MemberDataAttribute); public static INamedTypeSymbol NullableOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetSpecialType(SpecialType.System_Nullable_T); public static IArrayTypeSymbol ObjectArray(Compilation compilation) => Guard.ArgumentNotNull(compilation).CreateArrayTypeSymbol(Guard.ArgumentNotNull(compilation).ObjectType); public static INamedTypeSymbol? ObsoleteAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.System.ObsoleteAttribute); public static INamedTypeSymbol? OptionalAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Runtime.InteropServices.OptionalAttribute"); public static INamedTypeSymbol? Range(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Range"); public static INamedTypeSymbol? Record(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.Record); public static INamedTypeSymbol? RegisterXunitSerializerAttribute_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.RegisterXunitSerializerAttribute_V3); public static INamedTypeSymbol? SortedSetOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Collections.Generic.SortedSet`1"); public static INamedTypeSymbol String(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetSpecialType(SpecialType.System_String); public static INamedTypeSymbol? StringValues(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("Microsoft.Extensions.Primitives.StringValues"); public static INamedTypeSymbol? Task(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Threading.Tasks.Task"); public static INamedTypeSymbol? TaskOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Threading.Tasks.Task`1"); public static INamedTypeSymbol? TestContext_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.TestContext_V3); public static INamedTypeSymbol? TheoryAttribute(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.TheoryAttribute); public static INamedTypeSymbol? TheoryData(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.TheoryData); // Centralized here so we don't repeat knowledge of how many arities exist // (in case we decide to add more later). public static Dictionary TheoryData_ByGenericArgumentCount(Compilation compilation) { var result = new Dictionary(); var type = TheoryData(compilation); if (type is not null) result[0] = type; for (var i = 1; ; i++) { type = compilation.GetTypeByMetadataName(Constants.Types.Xunit.TheoryData + "`" + i.ToString(CultureInfo.InvariantCulture)); if (type is not null) result[i] = type; else break; } return result; } public static INamedTypeSymbol? TheoryDataRow_V3(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.TheoryDataRow_V3); // Centralized here so we don't repeat knowledge of how many arities exist // (in case we decide to add more later). public static Dictionary TheoryDataRow_ByGenericArgumentCount_V3(Compilation compilation) { var result = new Dictionary(); var type = TheoryDataRow_V3(compilation); if (type is not null) result[0] = type; for (var i = 1; ; i++) { type = compilation.GetTypeByMetadataName(Constants.Types.Xunit.TheoryDataRow_V3 + "`" + i.ToString(CultureInfo.InvariantCulture)); if (type is not null) result[i] = type; else break; } return result; } public static INamedTypeSymbol? TimeOnly(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.TimeOnly"); public static INamedTypeSymbol? TimeSpan(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.TimeSpan"); public static INamedTypeSymbol? Type(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Type"); static int ValidateArity( int arity, int min, int max) { if (arity >= min && arity <= max) return arity; throw new ArgumentOutOfRangeException(nameof(arity), $"Arity {arity} must be between {min} and {max}."); } public static INamedTypeSymbol? Uri(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Uri"); public static INamedTypeSymbol? ValueTask(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Threading.Tasks.ValueTask"); public static INamedTypeSymbol? ValueTaskOfT(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Threading.Tasks.ValueTask`1"); public static INamedTypeSymbol? Version(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Version"); public static INamedTypeSymbol Void(Compilation compilation) => Guard.ArgumentNotNull(compilation).GetSpecialType(SpecialType.System_Void); } ================================================ FILE: src/xunit.analyzers/Utility/V2AbstractionsContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V2AbstractionsContext : ICommonContext { readonly Lazy lazyIAssemblyInfoType; readonly Lazy lazyIAttributeInfoType; readonly Lazy lazyIMessageSinkMessageType; readonly Lazy lazyIMessageSinkType; readonly Lazy lazyIMethodInfoType; readonly Lazy lazyIParameterInfoType; readonly Lazy lazyISourceInformationProviderType; readonly Lazy lazyISourceInformationType; readonly Lazy lazyITestAssemblyType; readonly Lazy lazyITestCaseType; readonly Lazy lazyITestClassType; readonly Lazy lazyITestCollectionType; readonly Lazy lazyITestFrameworkDiscovererType; readonly Lazy lazyITestFrameworkExecutorType; readonly Lazy lazyITestFrameworkType; readonly Lazy lazyITestMethodType; readonly Lazy lazyITestType; readonly Lazy lazyITypeInfoType; readonly Lazy lazyIXunitSerializableType; V2AbstractionsContext( Compilation compilation, Version version) { Version = version; lazyIAssemblyInfoType = new(() => TypeSymbolFactory.IAssemblyInfo_V2(compilation)); lazyIAttributeInfoType = new(() => TypeSymbolFactory.IAttributeInfo_V2(compilation)); lazyIMessageSinkMessageType = new(() => TypeSymbolFactory.IMessageSinkMessage_V2(compilation)); lazyIMessageSinkType = new(() => TypeSymbolFactory.IMessageSink_V2(compilation)); lazyIMethodInfoType = new(() => TypeSymbolFactory.IMethodInfo_V2(compilation)); lazyIParameterInfoType = new(() => TypeSymbolFactory.IParameterInfo_V2(compilation)); lazyISourceInformationProviderType = new(() => TypeSymbolFactory.ISourceInformationProvider_V2(compilation)); lazyISourceInformationType = new(() => TypeSymbolFactory.ISourceInformation_V2(compilation)); lazyITestAssemblyType = new(() => TypeSymbolFactory.ITestAssembly_V2(compilation)); lazyITestCaseType = new(() => TypeSymbolFactory.ITestCase_V2(compilation)); lazyITestClassType = new(() => TypeSymbolFactory.ITestClass_V2(compilation)); lazyITestCollectionType = new(() => TypeSymbolFactory.ITestCollection_V2(compilation)); lazyITestFrameworkDiscovererType = new(() => TypeSymbolFactory.ITestFrameworkDiscoverer_V2(compilation)); lazyITestFrameworkExecutorType = new(() => TypeSymbolFactory.ITestFrameworkExecutor_V2(compilation)); lazyITestFrameworkType = new(() => TypeSymbolFactory.ITestFramework_V2(compilation)); lazyITestMethodType = new(() => TypeSymbolFactory.ITestMethod_V2(compilation)); lazyITestType = new(() => TypeSymbolFactory.ITest_V2(compilation)); lazyITypeInfoType = new(() => TypeSymbolFactory.ITypeInfo_V2(compilation)); lazyIXunitSerializableType = new(() => TypeSymbolFactory.IXunitSerializable_V2(compilation)); } /// /// Gets a reference to type IAssemblyInfo, if available. /// public INamedTypeSymbol? IAssemblyInfoType => lazyIAssemblyInfoType.Value; /// /// Gets a reference to type IAttributeInfo, if available. /// public INamedTypeSymbol? IAttributeInfoType => lazyIAttributeInfoType.Value; /// /// Gets a reference to type IMessageSinkMessage, if available. /// public INamedTypeSymbol? IMessageSinkMessageType => lazyIMessageSinkMessageType.Value; /// public INamedTypeSymbol? IMessageSinkType => lazyIMessageSinkType.Value; /// /// Gets a reference to type IMethodInfo, if available. /// public INamedTypeSymbol? IMethodInfoType => lazyIMethodInfoType.Value; /// /// Gets a reference to type IParameterInfo, if available. /// public INamedTypeSymbol? IParameterInfoType => lazyIParameterInfoType.Value; /// public INamedTypeSymbol? ISourceInformationProviderType => lazyISourceInformationProviderType.Value; /// /// Gets a reference to type ISourceInformation, if available. /// public INamedTypeSymbol? ISourceInformationType => lazyISourceInformationType.Value; /// public INamedTypeSymbol? ITestAssemblyType => lazyITestAssemblyType.Value; /// public INamedTypeSymbol? ITestCaseType => lazyITestCaseType.Value; /// public INamedTypeSymbol? ITestClassType => lazyITestClassType.Value; /// public INamedTypeSymbol? ITestCollectionType => lazyITestCollectionType.Value; /// public INamedTypeSymbol? ITestFrameworkDiscovererType => lazyITestFrameworkDiscovererType.Value; /// public INamedTypeSymbol? ITestFrameworkExecutorType => lazyITestFrameworkExecutorType.Value; /// public INamedTypeSymbol? ITestFrameworkType => lazyITestFrameworkType.Value; /// public INamedTypeSymbol? ITestMethodType => lazyITestMethodType.Value; /// public INamedTypeSymbol? ITestType => lazyITestType.Value; /// /// Gets a reference to type ITypeInfo, if available. /// public INamedTypeSymbol? ITypeInfoType => lazyITypeInfoType.Value; /// public INamedTypeSymbol? IXunitSerializableType => lazyIXunitSerializableType.Value; /// /// Gets the version number of the xunit.abstractions assembly. /// public Version Version { get; } public static V2AbstractionsContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var version = versionOverride ?? compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.Equals("xunit.abstractions", StringComparison.OrdinalIgnoreCase)) ?.Version; return version is null ? null : new(compilation, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V2AssertContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V2AssertContext : IAssertContext { internal static readonly Version Version_2_5_0 = new("2.5.0"); internal static readonly Version Version_2_9_3 = new("2.9.3"); readonly Lazy lazyAssertType; V2AssertContext( Compilation compilation, Version version) { Version = version; lazyAssertType = new(() => TypeSymbolFactory.Assert(compilation)); } /// public INamedTypeSymbol? AssertType => lazyAssertType.Value; /// public bool SupportsAssertFail => Version >= Version_2_5_0; /// public bool SupportsAssertNullWithPointers => false; /// public bool SupportsInexactTypeAssertions => Version >= Version_2_9_3; /// public Version Version { get; } public static V2AssertContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var version = versionOverride ?? compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.Equals("xunit.assert", StringComparison.OrdinalIgnoreCase) || a.Name.Equals("xunit.assert.source", StringComparison.OrdinalIgnoreCase)) ?.Version; return version is null ? null : new(compilation, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V2CoreContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V2CoreContext : ICoreContext { internal static readonly Version Version_2_2_0 = new("2.2.0"); internal static readonly Version Version_2_4_0 = new("2.4.0"); readonly Lazy lazyClassDataAttributeType; readonly Lazy lazyCollectionAttributeType; readonly Lazy lazyCollectionDefinitionAttributeType; readonly Lazy lazyDataAttributeType; readonly Lazy lazyFactAttributeType; readonly Lazy lazyIClassFixtureType; readonly Lazy lazyICollectionFixtureType; readonly Lazy lazyInlineDataAttributeType; readonly Lazy lazyITestOutputHelperType; readonly Lazy lazyMemberDataAttributeType; readonly Lazy lazyTheoryAttributeType; V2CoreContext( Compilation compilation, Version version) { Version = version; lazyClassDataAttributeType = new(() => TypeSymbolFactory.ClassDataAttribute(compilation)); lazyCollectionAttributeType = new(() => TypeSymbolFactory.CollectionAttribute(compilation)); lazyCollectionDefinitionAttributeType = new(() => TypeSymbolFactory.CollectionDefinitionAttribute(compilation)); lazyDataAttributeType = new(() => TypeSymbolFactory.DataAttribute_V2(compilation)); lazyFactAttributeType = new(() => TypeSymbolFactory.FactAttribute(compilation)); lazyIClassFixtureType = new(() => TypeSymbolFactory.IClassFixureOfT(compilation)); lazyICollectionFixtureType = new(() => TypeSymbolFactory.ICollectionFixtureOfT(compilation)); lazyInlineDataAttributeType = new(() => TypeSymbolFactory.InlineDataAttribute(compilation)); lazyITestOutputHelperType = new(() => TypeSymbolFactory.ITestOutputHelper_V2(compilation)); lazyMemberDataAttributeType = new(() => TypeSymbolFactory.MemberDataAttribute(compilation)); lazyTheoryAttributeType = new(() => TypeSymbolFactory.TheoryAttribute(compilation)); } /// public INamedTypeSymbol? ClassDataAttributeType => lazyClassDataAttributeType.Value; /// public INamedTypeSymbol? CollectionAttributeType => lazyCollectionAttributeType.Value; /// public INamedTypeSymbol? CollectionDefinitionAttributeType => lazyCollectionDefinitionAttributeType.Value; /// public INamedTypeSymbol? DataAttributeType => lazyDataAttributeType.Value; /// public INamedTypeSymbol? FactAttributeType => lazyFactAttributeType.Value; /// public INamedTypeSymbol? IClassFixtureType => lazyIClassFixtureType.Value; /// public INamedTypeSymbol? ICollectionFixtureType => lazyICollectionFixtureType.Value; /// public INamedTypeSymbol? InlineDataAttributeType => lazyInlineDataAttributeType.Value; /// /// Gets a reference to type Xunit.Abstractions.ITestOutputHelper, if available. /// public INamedTypeSymbol? ITestOutputHelperType => lazyITestOutputHelperType.Value; /// public INamedTypeSymbol? MemberDataAttributeType => lazyMemberDataAttributeType.Value; /// public INamedTypeSymbol? TheoryAttributeType => lazyTheoryAttributeType.Value; // See: https://github.com/xunit/xunit/pull/1546 /// public bool TheorySupportsConversionFromStringToDateTimeOffsetAndGuid => Version >= Version_2_4_0; /// public bool TheorySupportsDefaultParameterValues => Version >= Version_2_2_0; /// public bool TheorySupportsParameterArrays => Version >= Version_2_2_0; /// public Version Version { get; } public static V2CoreContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var version = versionOverride ?? compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.Equals("xunit.core", StringComparison.OrdinalIgnoreCase)) ?.Version; return version is null ? null : new(compilation, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V2ExecutionContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V2ExecutionContext { const string assemblyPrefix = "xunit.execution."; readonly Lazy lazyLongLivedMarshalByRefObjectType; V2ExecutionContext( Compilation compilation, string platform, Version version) { Platform = platform; Version = version; lazyLongLivedMarshalByRefObjectType = new(() => TypeSymbolFactory.LongLivedMarshalByRefObject_ExecutionV2(compilation)); } /// /// Gets a reference to type Xunit.LongLivedMarshalByRefObject, if available. /// public INamedTypeSymbol? LongLivedMarshalByRefObjectType => lazyLongLivedMarshalByRefObjectType.Value; /// /// Gets a description of the target platform for the execution library (i.e., "desktop"). This is /// typically extracted from the assembly name (i.e., "xunit.execution.desktop"). /// public string Platform { get; } /// /// Gets the version number of the execution assembly. /// public Version Version { get; } public static V2ExecutionContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var assembly = compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.StartsWith(assemblyPrefix, StringComparison.OrdinalIgnoreCase)); if (assembly is null) return null; var version = versionOverride ?? assembly.Version; var platform = assembly.Name.Substring(assemblyPrefix.Length); return version is null ? null : new(compilation, platform, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V2RunnerUtilityContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V2RunnerUtilityContext : IRunnerUtilityContext { const string assemblyPrefix = "xunit.runner.utility."; readonly Lazy lazyLongLivedMarshalByRefObjectType; V2RunnerUtilityContext( Compilation compilation, string platform, Version version) { Platform = platform; Version = version; lazyLongLivedMarshalByRefObjectType = new(() => TypeSymbolFactory.LongLivedMarshalByRefObject_RunnerUtility(compilation)); } /// public INamedTypeSymbol? LongLivedMarshalByRefObjectType => lazyLongLivedMarshalByRefObjectType.Value; /// public string Platform { get; } /// public Version Version { get; } public static V2RunnerUtilityContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var assembly = compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.StartsWith(assemblyPrefix, StringComparison.OrdinalIgnoreCase)); if (assembly is null) return null; var version = versionOverride ?? assembly.Version; var platform = assembly.Name.Substring(assemblyPrefix.Length); return version is null ? null : new(compilation, platform, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V3AssertContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V3AssertContext : IAssertContext { internal static readonly Version Version_0_6_0 = new("0.6.0"); internal static readonly Version Version_3_0_1 = new("3.0.1"); readonly Lazy lazyAssertType; V3AssertContext( Compilation compilation, Version version) { Version = version; lazyAssertType = new(() => TypeSymbolFactory.Assert(compilation)); } /// public INamedTypeSymbol? AssertType => lazyAssertType.Value; /// public bool SupportsAssertFail => true; /// public bool SupportsAssertNullWithPointers => Version >= Version_3_0_1; /// public bool SupportsInexactTypeAssertions => Version >= Version_0_6_0; /// public Version Version { get; } public static V3AssertContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var version = versionOverride ?? compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.Equals("xunit.v3.assert", StringComparison.OrdinalIgnoreCase) || a.Name.Equals("xunit.v3.assert.source", StringComparison.OrdinalIgnoreCase)) ?.Version; return version is null ? null : new(compilation, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V3CommonContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V3CommonContext : ICommonContext { readonly Lazy lazyIMessageSinkType; readonly Lazy lazyISourceInformationProviderType; readonly Lazy lazyITestAssemblyType; readonly Lazy lazyITestCaseType; readonly Lazy lazyITestClassType; readonly Lazy lazyITestCollectionType; readonly Lazy lazyITestFrameworkDiscovererType; readonly Lazy lazyITestFrameworkExecutorType; readonly Lazy lazyITestFrameworkType; readonly Lazy lazyITestMethodType; readonly Lazy lazyITestType; readonly Lazy lazyIXunitSerializableType; readonly Lazy lazyIXunitSerializerType; V3CommonContext( Compilation compilation, Version version) { Version = version; lazyIMessageSinkType = new(() => TypeSymbolFactory.IMessageSink_V3(compilation)); lazyISourceInformationProviderType = new(() => TypeSymbolFactory.ISourceInformationProvider_V3(compilation)); lazyITestAssemblyType = new(() => TypeSymbolFactory.ITestAssembly_V3(compilation)); lazyITestCaseType = new(() => TypeSymbolFactory.ITestCase_V3(compilation)); lazyITestClassType = new(() => TypeSymbolFactory.ITestClass_V3(compilation)); lazyITestCollectionType = new(() => TypeSymbolFactory.ITestCollection_V3(compilation)); lazyITestFrameworkDiscovererType = new(() => TypeSymbolFactory.ITestFrameworkDiscoverer_V3(compilation)); lazyITestFrameworkExecutorType = new(() => TypeSymbolFactory.ITestFrameworkExecutor_V3(compilation)); lazyITestFrameworkType = new(() => TypeSymbolFactory.ITestFramework_V3(compilation)); lazyITestMethodType = new(() => TypeSymbolFactory.ITestMethod_V3(compilation)); lazyITestType = new(() => TypeSymbolFactory.ITest_V3(compilation)); lazyIXunitSerializableType = new(() => TypeSymbolFactory.IXunitSerializable_V3(compilation)); lazyIXunitSerializerType = new(() => TypeSymbolFactory.IXunitSerializer_V3(compilation)); } /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? IMessageSinkType => lazyIMessageSinkType.Value; /// /// This type lives in xunit.v3.runner.common. public INamedTypeSymbol? ISourceInformationProviderType => lazyISourceInformationProviderType.Value; /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? ITestAssemblyType => lazyITestAssemblyType.Value; /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? ITestCaseType => lazyITestCaseType.Value; /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? ITestClassType => lazyITestClassType.Value; /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? ITestCollectionType => lazyITestCollectionType.Value; /// /// This type lives in xunit.v3.core. public INamedTypeSymbol? ITestFrameworkDiscovererType => lazyITestFrameworkDiscovererType.Value; /// /// This type lives in xunit.v3.core. public INamedTypeSymbol? ITestFrameworkExecutorType => lazyITestFrameworkExecutorType.Value; /// /// This type lives in xunit.v3.core. public INamedTypeSymbol? ITestFrameworkType => lazyITestFrameworkType.Value; /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? ITestMethodType => lazyITestMethodType.Value; /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? ITestType => lazyITestType.Value; /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? IXunitSerializableType => lazyIXunitSerializableType.Value; /// /// Gets a reference to type IXunitSerializer, if available. /// /// This type lives in xunit.v3.common. public INamedTypeSymbol? IXunitSerializerType => lazyIXunitSerializerType.Value; /// /// Gets the version number of the xunit.v3.common assembly. /// public Version Version { get; } public static V3CommonContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var version = versionOverride ?? compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.Equals("xunit.v3.common", StringComparison.OrdinalIgnoreCase)) ?.Version; return version is null ? null : new(compilation, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V3CoreContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V3CoreContext : ICoreContext { readonly Lazy lazyAssemblyFixtureAttributeType; readonly Lazy lazyClassDataAttributeType; readonly Lazy lazyClassDataAttributeOfTType; readonly Lazy lazyCollectionAttributeType; readonly Lazy lazyCollectionAttributeOfTType; readonly Lazy lazyCollectionDefinitionAttributeType; readonly Lazy lazyDataAttributeType; readonly Lazy lazyFactAttributeType; readonly Lazy lazyIClassFixtureType; readonly Lazy lazyICollectionFixtureType; readonly Lazy lazyIDataAttributeType; readonly Lazy lazyInlineDataAttributeType; readonly Lazy lazyITestContextAccessorType; readonly Lazy lazyITestOutputHelperType; readonly Lazy lazyJsonTypeIDAttributeType; readonly Lazy lazyMemberDataAttributeType; readonly Lazy lazyTheoryAttributeType; V3CoreContext( Compilation compilation, Version version) { Version = version; lazyAssemblyFixtureAttributeType = new(() => TypeSymbolFactory.AssemblyFixtureAttribute_V3(compilation)); lazyClassDataAttributeType = new(() => TypeSymbolFactory.ClassDataAttribute(compilation)); lazyClassDataAttributeOfTType = new(() => TypeSymbolFactory.ClassDataAttributeOfT_V3(compilation)); lazyCollectionAttributeType = new(() => TypeSymbolFactory.CollectionAttribute(compilation)); lazyCollectionAttributeOfTType = new(() => TypeSymbolFactory.CollectionAttributeOfT_V3(compilation)); lazyCollectionDefinitionAttributeType = new(() => TypeSymbolFactory.CollectionDefinitionAttribute(compilation)); lazyDataAttributeType = new(() => TypeSymbolFactory.DataAttribute_V3(compilation)); lazyFactAttributeType = new(() => TypeSymbolFactory.FactAttribute(compilation)); lazyIClassFixtureType = new(() => TypeSymbolFactory.IClassFixureOfT(compilation)); lazyICollectionFixtureType = new(() => TypeSymbolFactory.ICollectionFixtureOfT(compilation)); lazyIDataAttributeType = new(() => TypeSymbolFactory.IDataAttribute_V3(compilation)); lazyInlineDataAttributeType = new(() => TypeSymbolFactory.InlineDataAttribute(compilation)); lazyITestContextAccessorType = new(() => TypeSymbolFactory.ITestContextAccessor_V3(compilation)); lazyITestOutputHelperType = new(() => TypeSymbolFactory.ITestOutputHelper_V3(compilation)); lazyJsonTypeIDAttributeType = new(() => TypeSymbolFactory.JsonTypeIDAttribute_V3(compilation)); lazyMemberDataAttributeType = new(() => TypeSymbolFactory.MemberDataAttribute(compilation)); lazyTheoryAttributeType = new(() => TypeSymbolFactory.TheoryAttribute(compilation)); } /// /// Gets a reference to type AssemblyFixtureAttribute, if available. /// public INamedTypeSymbol? AssemblyFixtureAttributeType => lazyAssemblyFixtureAttributeType.Value; /// public INamedTypeSymbol? ClassDataAttributeType => lazyClassDataAttributeType.Value; /// /// Gets a reference to type ClassDataAttribute<T>, if available. /// public INamedTypeSymbol? ClassDataAttributeOfTType => lazyClassDataAttributeOfTType.Value; /// public INamedTypeSymbol? CollectionAttributeType => lazyCollectionAttributeType.Value; /// /// Gets a reference to type CollectionAttribute<T>, if available. /// public INamedTypeSymbol? CollectionAttributeOfTType => lazyCollectionAttributeOfTType.Value; /// public INamedTypeSymbol? CollectionDefinitionAttributeType => lazyCollectionDefinitionAttributeType.Value; /// public INamedTypeSymbol? DataAttributeType => lazyDataAttributeType.Value; /// public INamedTypeSymbol? FactAttributeType => lazyFactAttributeType.Value; /// public INamedTypeSymbol? IClassFixtureType => lazyIClassFixtureType.Value; /// public INamedTypeSymbol? ICollectionFixtureType => lazyICollectionFixtureType.Value; /// /// Gets a reference to type IDataAttribute, if available. /// public INamedTypeSymbol? IDataAttributeType => lazyIDataAttributeType.Value; /// public INamedTypeSymbol? InlineDataAttributeType => lazyInlineDataAttributeType.Value; /// /// Gets a reference to type ITestContextAccessor, if available. /// public INamedTypeSymbol? ITestContextAccessorType => lazyITestContextAccessorType.Value; /// public INamedTypeSymbol? ITestOutputHelperType => lazyITestOutputHelperType.Value; /// /// Gets a reference to type JsonTypeIDAttribute, if available. /// public INamedTypeSymbol? JsonTypeIDAttributeType => lazyJsonTypeIDAttributeType.Value; /// public INamedTypeSymbol? MemberDataAttributeType => lazyMemberDataAttributeType.Value; /// public INamedTypeSymbol? TheoryAttributeType => lazyTheoryAttributeType.Value; /// public bool TheorySupportsConversionFromStringToDateTimeOffsetAndGuid => true; /// public bool TheorySupportsDefaultParameterValues => true; /// public bool TheorySupportsParameterArrays => true; /// public Version Version { get; } public static V3CoreContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var version = versionOverride ?? compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.Equals("xunit.v3.core", StringComparison.OrdinalIgnoreCase)) ?.Version; return version is null ? null : new(compilation, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V3RunnerCommonContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V3RunnerCommonContext { readonly Lazy lazyIRunnerReporterType; V3RunnerCommonContext( Compilation compilation, Version version) { Version = version; lazyIRunnerReporterType = new(() => TypeSymbolFactory.IRunnerReporter_V3(compilation)); } public INamedTypeSymbol? IRunnerReporterType => lazyIRunnerReporterType.Value; /// public Version Version { get; } public static V3RunnerCommonContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var assembly = compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.Equals("xunit.v3.runner.common", StringComparison.OrdinalIgnoreCase)); if (assembly is null) return null; var version = versionOverride ?? assembly.Version; return version is null ? null : new(compilation, version); } } ================================================ FILE: src/xunit.analyzers/Utility/V3RunnerUtilityContext.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; public class V3RunnerUtilityContext : IRunnerUtilityContext { const string assemblyPrefix = "xunit.v3.runner.utility."; readonly Lazy lazyLongLivedMarshalByRefObjectType; V3RunnerUtilityContext( Compilation compilation, string platform, Version version) { Platform = platform; Version = version; lazyLongLivedMarshalByRefObjectType = new(() => TypeSymbolFactory.LongLivedMarshalByRefObject_RunnerUtility(compilation)); } /// public INamedTypeSymbol? LongLivedMarshalByRefObjectType => lazyLongLivedMarshalByRefObjectType.Value; /// public string Platform { get; } /// public Version Version { get; } public static V3RunnerUtilityContext? Get( Compilation compilation, Version? versionOverride = null) { Guard.ArgumentNotNull(compilation); var assembly = compilation .ReferencedAssemblyNames .FirstOrDefault(a => a.Name.StartsWith(assemblyPrefix, StringComparison.OrdinalIgnoreCase)); if (assembly is null) return null; var version = versionOverride ?? assembly.Version; var platform = assembly.Name.Substring(assemblyPrefix.Length); return version is null ? null : new(compilation, platform, version); } } ================================================ FILE: src/xunit.analyzers/Utility/XunitContext.cs ================================================ using System; using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; /// /// Class which provides information about references to xUnit.net assemblies. /// public class XunitContext { IAssertContext? assert; ICommonContext? common; ICoreContext? core; IRunnerUtilityContext? runnerUtility; static readonly Version v2AbstractionsVersion = new(2, 0, 3); XunitContext() { } /// /// Initializes a new instance of the class. /// /// The Roslyn compilation object used to look up types and /// inspect references public XunitContext(Compilation compilation) { V2Abstractions = V2AbstractionsContext.Get(compilation); V2Assert = V2AssertContext.Get(compilation); V2Core = V2CoreContext.Get(compilation); V2Execution = V2ExecutionContext.Get(compilation); V2RunnerUtility = V2RunnerUtilityContext.Get(compilation); V3Assert = V3AssertContext.Get(compilation); V3Common = V3CommonContext.Get(compilation); V3Core = V3CoreContext.Get(compilation); V3RunnerCommon = V3RunnerCommonContext.Get(compilation); } /// /// Gets a combined view of the assertion library features available to either v2 tests (linked /// against xunit.assert or xunit.assert.source) or v3 tests (linked against /// xunit.v3.assert or xunit.v3.assert.source). /// public IAssertContext Assert { get { assert ??= V3Assert ?? (IAssertContext?)V2Assert ?? EmptyAssertContext.Instance; return assert; } } /// /// Gets a combined view of features that are common to both tests and runners, available to either /// v2 tests (linked against xunit.abstractions) or v3 tests (linked against xunit.v3.common). /// public ICommonContext Common { get { common ??= V3Common ?? (ICommonContext?)V2Abstractions ?? EmptyCommonContext.Instance; return common; } } /// /// Gets a combined view of features available to either v2 tests (linked against xunit.core) /// or v3 tests (linked against xunit.v3.core). /// public ICoreContext Core { get { core ??= V3Core ?? (ICoreContext?)V2Core ?? EmptyCoreContext.Instance; return core; } } /// /// Gets a flag which indicates whether there are any xUnit.net v2 references in the project /// (including abstractions, assert, core, execution, and runner utility references). /// public bool HasV2References => V2Abstractions is not null || V2Assert is not null || V2Core is not null || V2Execution is not null || V2RunnerUtility is not null; /// /// Gets a flag which indicates whether there are any xUnit.net v3 references in the project /// (including assert, common, and core references). /// public bool HasV3References => V3Assert is not null || V3Common is not null || V3Core is not null; /// /// Gets a combined view of features available to either v2 runners (linked against xunit.runner.utility) /// or v3 runners (linked against xunit.v3.runner.utility). /// public IRunnerUtilityContext RunnerUtility { get { runnerUtility ??= V3RunnerUtility ?? (IRunnerUtilityContext?)V2RunnerUtility ?? EmptyRunnerUtilityContext.Instance; return runnerUtility; } } /// /// Gets information about the reference to xunit.abstractions (v2). If the project does /// not reference the v2 abstractions library, then returns null. /// public V2AbstractionsContext? V2Abstractions { get; private set; } /// /// Gets information about the reference to xunit.assert or xunit.assert.source (v2). If /// the project does not reference the v2 assertion library, then returns null. /// public V2AssertContext? V2Assert { get; private set; } /// /// Gets information about the reference to xunit.core(v2). If the project does not /// reference the v2 core library, then returns null. /// public V2CoreContext? V2Core { get; private set; } /// /// Gets information about the reference to xunit.execution (v2). If the project does /// not reference the v2 execution library, then returns null. /// public V2ExecutionContext? V2Execution { get; private set; } /// /// Gets information about the reference to xunit.runner.utility (v2). If the project does /// not reference the v2 runner utility library, then returns null. /// public V2RunnerUtilityContext? V2RunnerUtility { get; private set; } /// /// Gets information about the reference to xunit.v3.assert or xunit.v3.assert.source /// (v3). If the project does not reference the v3 assertion library, then returns null. /// public V3AssertContext? V3Assert { get; private set; } /// /// Gets types that exist in xunit.v3.common (v3). If the project does not reference /// the v3 common library, then returns null. /// /// /// This also contains a few selected types from xunit.v3.core and xunit.v3.runner.common /// to align with the types that were all originally in xunit.abstractions in v2, to support /// the composite view from . /// public V3CommonContext? V3Common { get; private set; } /// /// Gets information about the reference to xunit.v3.core (v3). If the project does not /// reference the v3 core library, then returns null. /// public V3CoreContext? V3Core { get; private set; } /// /// Gets information about the reference to xunit.v3.runner.common (v3). If the project does /// not reference the v3 runner common library, then returns null. /// public V3RunnerCommonContext? V3RunnerCommon { get; private set; } /// /// Gets information about the reference to xunit.v3.runner.utility (v3). If the project does /// not reference the v3 runner utility library, then returns null. /// public V3RunnerUtilityContext? V3RunnerUtility { get; private set; } /// /// Used to create a context object for testing v2 analyzers and fixers. This includes references /// to xunit.abstractions (at version 2.0.3) and xunit.core. /// /// The Roslyn compilation object used to look up types /// The overridden version for xunit.core public static XunitContext ForV2( Compilation compilation, Version? versionOverride = null) => new() { V2Abstractions = V2AbstractionsContext.Get(compilation, v2AbstractionsVersion), V2Core = V2CoreContext.Get(compilation, versionOverride), }; /// /// Used to create a context object for testing v2 analyzers and fixers. This includes references /// to xunit.abstactions (at version 2.0.3). /// The Roslyn compilation object used to look up types /// public static XunitContext ForV2Abstractions(Compilation compilation) => new() { V2Abstractions = V2AbstractionsContext.Get(compilation, v2AbstractionsVersion), }; /// /// Used to create a context object for testing v2 analyzers and fixers. This includes references /// to xunit.assert. /// /// The Roslyn compilation object used to look up types /// The overridden version for xunit.assert public static XunitContext ForV2Assert( Compilation compilation, Version? versionOverride = null) => new() { V2Assert = V2AssertContext.Get(compilation, versionOverride), }; /// /// Used to create a context object for testing v2 analyzers and fixers. This includes references /// to xunit.abstractions (at version 2.0.3) and xunit.execution. /// /// The Roslyn compilation object used to look up types /// The overridden version for xunit.execution public static XunitContext ForV2Execution( Compilation compilation, Version? versionOverride = null) => new() { V2Abstractions = V2AbstractionsContext.Get(compilation, v2AbstractionsVersion), V2Execution = V2ExecutionContext.Get(compilation, versionOverride), }; /// /// Used to create a context object for testing v2 analyzers and fixers. This includes references /// to xunit.abstractions (at version 2.0.3) and xunit.runner.utility. /// /// The Roslyn compilation object used to look up types /// The overridden version for xunit.runner.utility public static XunitContext ForV2RunnerUtility( Compilation compilation, Version? versionOverride = null) => new() { V2Abstractions = V2AbstractionsContext.Get(compilation, v2AbstractionsVersion), V2RunnerUtility = V2RunnerUtilityContext.Get(compilation, versionOverride), }; /// /// Used to create a context object for testing v3 analyzers and fixers. This includes references /// for xunit.v3.assert, xunit.v3.common, xunit.v3.core, and /// xunit.v3.runner.common. /// /// The Roslyn compilation object used to look up types /// The overridden version for all libraries public static XunitContext ForV3( Compilation compilation, Version? versionOverride = null) => new() { V3Assert = V3AssertContext.Get(compilation, versionOverride), V3Common = V3CommonContext.Get(compilation, versionOverride), V3Core = V3CoreContext.Get(compilation, versionOverride), V3RunnerCommon = V3RunnerCommonContext.Get(compilation, versionOverride), }; /// /// Used to create a context object for testing v3 analyzers and fixers. This includes references /// for xunit.v3.assert. /// /// The Roslyn compilation object used to look up types /// The overridden version for xunit.v3.assert public static XunitContext ForV3Assert( Compilation compilation, Version? versionOverride = null) => new() { V3Assert = V3AssertContext.Get(compilation, versionOverride), }; } ================================================ FILE: src/xunit.analyzers/Utility/XunitDiagnosticAnalyzer.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; /// /// Base class for diagnostic analyzers which support xUnit.net v2 and v3. /// public abstract class XunitDiagnosticAnalyzer(params DiagnosticDescriptor[] descriptors) : DiagnosticAnalyzer { /// #pragma warning disable IDE0305 // Cannot convert this due to Roslyn 3.11 public sealed override ImmutableArray SupportedDiagnostics { get; } = descriptors.ToImmutableArray(); #pragma warning restore IDE0305 /// /// Analyzes compilation to discover diagnostics. /// /// The Roslyn diagnostic context /// The xUnit.net context public abstract void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext); /// /// Override this factory method to influence the creation of . /// Typically used by derived classes wanting to provide version overrides for specific /// references. /// /// The Roslyn compilation context protected virtual XunitContext CreateXunitContext(Compilation compilation) => new(compilation); /// public sealed override void Initialize(AnalysisContext context) { Guard.ArgumentNotNull(context); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.EnableConcurrentExecution(); context.RegisterCompilationStartAction(context => { var xunitContext = CreateXunitContext(context.Compilation); if (ShouldAnalyze(xunitContext)) AnalyzeCompilation(context, xunitContext); }); } /// /// Override this method to influence when we should consider diagnostic analysis. By /// default analyzes all assemblies that have a reference to xUnit.net v2 or v3. /// /// The xUnit.net context /// Return true to analyze source; return false to skip analysis protected virtual bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).HasV2References || xunitContext.HasV3References; } ================================================ FILE: src/xunit.analyzers/Utility/XunitDiagnosticSuppressor.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Xunit.Analyzers; namespace Xunit.Suppressors; /// /// Base class for diagnostic suppressors which support xUnit.net v2 and v3. /// public abstract class XunitDiagnosticSuppressor(SuppressionDescriptor descriptor) : DiagnosticSuppressor { protected SuppressionDescriptor Descriptor => SupportedSuppressions[0]; /// #pragma warning disable IDE0305 // Cannot convert this due to Roslyn 3.11 public override ImmutableArray SupportedSuppressions { get; } = new[] { descriptor }.ToImmutableArray(); #pragma warning restore IDE0305 /// /// Override this factory method to influence the creation of . /// Typically used by derived classes wanting to provide version overrides for specific /// references. /// /// The Roslyn compilation context protected virtual XunitContext CreateXunitContext(Compilation compilation) => new(compilation); /// public sealed override void ReportSuppressions(SuppressionAnalysisContext context) { var xunitContext = CreateXunitContext(context.Compilation); if (ShouldAnalyze(xunitContext)) foreach (var diagnostic in context.ReportedDiagnostics) if (ShouldSuppress(diagnostic, context, xunitContext)) context.ReportSuppression(Suppression.Create(Descriptor, diagnostic)); } /// /// Override this method to influence when we should consider diagnostic analysis. By /// default analyzes all assemblies that have a reference to xUnit.net v2 or v3. /// /// The xUnit.net context /// Return true to analyze source; return false to skip analysis protected virtual bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).HasV2References || xunitContext.HasV3References; /// /// Analyzes the given diagnostic to determine if it should be suppressed. /// /// The diagnostic to analyze /// The Roslyn supression analysis context /// The xUnit.net context protected abstract bool ShouldSuppress( Diagnostic diagnostic, SuppressionAnalysisContext context, XunitContext xunitContext); } ================================================ FILE: src/xunit.analyzers/Utility/XunitV2DiagnosticAnalyzer.cs ================================================ using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; /// /// Base class for diagnostic analyzers which support xUnit.net v2 only. /// public abstract class XunitV2DiagnosticAnalyzer(params DiagnosticDescriptor[] descriptors) : XunitDiagnosticAnalyzer(descriptors) { protected override bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).HasV2References; } ================================================ FILE: src/xunit.analyzers/Utility/XunitV2DiagnosticSuppressor.cs ================================================ using Microsoft.CodeAnalysis; using Xunit.Analyzers; namespace Xunit.Suppressors; /// /// Base class for diagnostic suppressors which support xUnit.net v2 only. /// public abstract class XunitV2DiagnosticSuppressor(SuppressionDescriptor descriptor) : XunitDiagnosticSuppressor(descriptor) { protected override bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).HasV2References; } ================================================ FILE: src/xunit.analyzers/Utility/XunitV3DiagnosticAnalyzer.cs ================================================ using Microsoft.CodeAnalysis; namespace Xunit.Analyzers; /// /// Base class for diagnostic analyzers which support xUnit.net v3 only. /// public abstract class XunitV3DiagnosticAnalyzer(params DiagnosticDescriptor[] descriptors) : XunitDiagnosticAnalyzer(descriptors) { protected override bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).HasV3References; } ================================================ FILE: src/xunit.analyzers/Utility/XunitV3DiagnosticSuppressor.cs ================================================ using Microsoft.CodeAnalysis; using Xunit.Analyzers; namespace Xunit.Suppressors; /// /// Base class for diagnostic suppressors which support xUnit.net v3 only. /// public abstract class XunitV3DiagnosticSuppressor(SuppressionDescriptor descriptor) : XunitDiagnosticSuppressor(descriptor) { protected override bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).HasV3References; } ================================================ FILE: src/xunit.analyzers/X1000/ClassDataAttributeMustPointAtValidClass.cs ================================================ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class ClassDataAttributeMustPointAtValidClass : XunitDiagnosticAnalyzer { const string typesV2 = "IEnumerable"; const string typesV3 = "IEnumerable, IAsyncEnumerable, IEnumerable, or IAsyncEnumerable"; public ClassDataAttributeMustPointAtValidClass() : base( Descriptors.X1007_ClassDataAttributeMustPointAtValidClass, Descriptors.X1037_TheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters, Descriptors.X1038_TheoryDataTypeArgumentsMustMatchTestMethodParameters_ExtraTypeParameters, Descriptors.X1039_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleTypes, Descriptors.X1040_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleNullability, Descriptors.X1050_ClassDataTheoryDataRowIsRecommendedForStronglyTypedAnalysis ) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var compilation = context.Compilation; var iEnumerableOfObjectArray = TypeSymbolFactory.IEnumerableOfObjectArray(compilation); var iEnumerableOfTheoryDataRow = TypeSymbolFactory.IEnumerableOfITheoryDataRow(compilation); var iAsyncEnumerableOfObjectArray = TypeSymbolFactory.IAsyncEnumerableOfObjectArray(compilation); var iAsyncEnumerableOfTheoryDataRow = TypeSymbolFactory.IAsyncEnumerableOfITheoryDataRow(compilation); var theoryDataRowTypes = TypeSymbolFactory.TheoryDataRow_ByGenericArgumentCount_V3(compilation); context.RegisterSyntaxNodeAction(context => { if (context.Node is not MethodDeclarationSyntax testMethod) return; var attributeLists = testMethod.AttributeLists; var semanticModel = context.SemanticModel; foreach (var attributeSyntax in attributeLists.WhereNotNull().SelectMany(attList => attList.Attributes)) { context.CancellationToken.ThrowIfCancellationRequested(); var attributeType = semanticModel.GetTypeInfo(attributeSyntax).Type as INamedTypeSymbol; if (attributeType is null) continue; var classType = default(INamedTypeSymbol); // [ClassData(typeof(...))] if (SymbolEqualityComparer.Default.Equals(attributeType, xunitContext.Core.ClassDataAttributeType)) { if (attributeSyntax.ArgumentList is null) continue; if (attributeSyntax.ArgumentList.Arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpression) continue; classType = semanticModel.GetTypeInfo(typeOfExpression.Type).Type as INamedTypeSymbol; } // [ClassData<...>] else if (attributeType.IsGenericType) { var classDataOfTType = xunitContext.V3Core?.ClassDataAttributeOfTType?.ConstructUnboundGenericType(); if (classDataOfTType is not null && SymbolEqualityComparer.Default.Equals(attributeType.ConstructUnboundGenericType(), classDataOfTType)) classType = attributeType.TypeArguments[0] as INamedTypeSymbol; } if (classType is null || classType.Kind == SymbolKind.ErrorType) continue; // Make sure the class implements a compatible interface var isValidDeclaration = VerifyDataSourceDeclaration(context, compilation, xunitContext, classType, attributeSyntax); // Everything from here is based on ensuring I(Async)Enumerable>, which is // only available in v3. if (!xunitContext.HasV3References) continue; var rowType = classType.UnwrapEnumerable(compilation); if (rowType is null) continue; if (IsGenericTheoryDataRowType(rowType, theoryDataRowTypes, out var theoryDataReturnType)) VerifyGenericArgumentTypes(semanticModel, context, testMethod, theoryDataRowTypes[0], theoryDataReturnType, classType, attributeSyntax); else if (isValidDeclaration) ReportClassReturnsUnsafeTypeValue(context, attributeSyntax); } }, SyntaxKind.MethodDeclaration); } static bool IsGenericTheoryDataRowType( ITypeSymbol? rowType, Dictionary theoryDataRowTypes, [NotNullWhen(true)] out INamedTypeSymbol? theoryReturnType) { theoryReturnType = default; var working = rowType as INamedTypeSymbol; for (; working is not null; working = working.BaseType) { var returnTypeArguments = working.TypeArguments; if (returnTypeArguments.Length != 0 && theoryDataRowTypes.TryGetValue(returnTypeArguments.Length, out var theoryDataType) && SymbolEqualityComparer.Default.Equals(theoryDataType, working.OriginalDefinition)) break; } if (working is null) return false; theoryReturnType = working; return true; } static void ReportClassReturnsUnsafeTypeValue( SyntaxNodeAnalysisContext context, AttributeSyntax attribute) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1050_ClassDataTheoryDataRowIsRecommendedForStronglyTypedAnalysis, attribute.GetLocation() ) ); static void ReportExtraTypeArguments( SyntaxNodeAnalysisContext context, AttributeSyntax attribute, INamedTypeSymbol theoryDataType) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1038_TheoryDataTypeArgumentsMustMatchTestMethodParameters_ExtraTypeParameters, attribute.GetLocation(), SymbolDisplay.ToDisplayString(theoryDataType) ) ); static void ReportIncompatibleType( SyntaxNodeAnalysisContext context, TypeSyntax parameterType, ITypeSymbol theoryDataTypeParameter, INamedTypeSymbol namedClassType, IParameterSymbol parameter) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1039_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleTypes, parameterType.GetLocation(), SymbolDisplay.ToDisplayString(theoryDataTypeParameter), SymbolDisplay.ToDisplayString(namedClassType), parameter.Name ) ); static void ReportIncorrectImplementationType( SyntaxNodeAnalysisContext context, string validSymbols, AttributeSyntax attribute, ITypeSymbol classType) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1007_ClassDataAttributeMustPointAtValidClass, attribute.GetLocation(), classType.Name, validSymbols ) ); static void ReportNullabilityMismatch( SyntaxNodeAnalysisContext context, TypeSyntax parameterType, ITypeSymbol theoryDataTypeParameter, INamedTypeSymbol namedClassType, IParameterSymbol parameter) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1040_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleNullability, parameterType.GetLocation(), SymbolDisplay.ToDisplayString(theoryDataTypeParameter), SymbolDisplay.ToDisplayString(namedClassType), parameter.Name ) ); static void ReportTooFewTypeArguments( SyntaxNodeAnalysisContext context, AttributeSyntax attribute, INamedTypeSymbol theoryDataType) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1037_TheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters, attribute.GetLocation(), SymbolDisplay.ToDisplayString(theoryDataType) ) ); static bool VerifyDataSourceDeclaration( SyntaxNodeAnalysisContext context, Compilation compilation, XunitContext xunitContext, INamedTypeSymbol classType, AttributeSyntax attribute) { var v3 = xunitContext.HasV3References; var iEnumerableOfObjectArrayType = TypeSymbolFactory.IEnumerableOfObjectArray(compilation); var iEnumerableOfTheoryDataRowType = TypeSymbolFactory.IEnumerableOfITheoryDataRow(compilation); var iAsyncEnumerableOfObjectArrayType = TypeSymbolFactory.IAsyncEnumerableOfObjectArray(compilation); var iAsyncEnumerableOfTheoryDataRowType = TypeSymbolFactory.IAsyncEnumerableOfITheoryDataRow(compilation); // Make sure we implement one of the interfaces var valid = iEnumerableOfObjectArrayType.IsAssignableFrom(classType); if (!valid && v3 && iAsyncEnumerableOfObjectArrayType is not null) valid = iAsyncEnumerableOfObjectArrayType.IsAssignableFrom(classType); if (!valid && v3 && iEnumerableOfTheoryDataRowType is not null) valid = iEnumerableOfTheoryDataRowType.IsAssignableFrom(classType); if (!valid && v3 && iAsyncEnumerableOfTheoryDataRowType is not null) valid = iAsyncEnumerableOfTheoryDataRowType.IsAssignableFrom(classType); // Also make sure we're non-abstract and have an empty constructor valid = valid && !classType.IsAbstract && classType.InstanceConstructors.Any(c => c.Parameters.IsEmpty && c.DeclaredAccessibility == Accessibility.Public); if (!valid) ReportIncorrectImplementationType( context, xunitContext.HasV3References ? typesV3 : typesV2, attribute, classType ); return valid; } static void VerifyGenericArgumentTypes( SemanticModel semanticModel, SyntaxNodeAnalysisContext context, MethodDeclarationSyntax testMethod, INamedTypeSymbol theoryDataType, INamedTypeSymbol theoryReturnType, ITypeSymbol classType, AttributeSyntax attribute) { if (classType is not INamedTypeSymbol namedClassType) return; var returnTypeArguments = theoryReturnType.TypeArguments; var testMethodSymbol = semanticModel.GetDeclaredSymbol(testMethod, context.CancellationToken); if (testMethodSymbol is null) return; var testMethodParameterSymbols = testMethodSymbol.Parameters; var testMethodParameterSyntaxes = testMethod.ParameterList.Parameters; if (testMethodParameterSymbols.Length > returnTypeArguments.Length && testMethodParameterSymbols.Skip(returnTypeArguments.Length).Any(p => !p.IsOptional && !p.IsParams)) { ReportTooFewTypeArguments(context, attribute, theoryDataType); return; } int typeArgumentIdx = 0, parameterTypeIdx = 0; for (; typeArgumentIdx < returnTypeArguments.Length && parameterTypeIdx < testMethodParameterSymbols.Length; typeArgumentIdx++) { var parameterSyntax = testMethodParameterSyntaxes[parameterTypeIdx]; if (parameterSyntax.Type is null) continue; var parameter = testMethodParameterSymbols[parameterTypeIdx]; if (parameter.Type is null) continue; var parameterType = parameter.IsParams && parameter.Type is IArrayTypeSymbol paramsArraySymbol ? paramsArraySymbol.ElementType : parameter.Type; var typeArgument = returnTypeArguments[typeArgumentIdx]; if (typeArgument is null) continue; if (parameterType.Kind != SymbolKind.TypeParameter && !parameterType.IsAssignableFrom(typeArgument)) { var report = true; // The user might be providing the full array for 'params'; if they do, we need to move // the parameter type index forward because it's been consumed by the array if (parameter.IsParams && parameter.Type.IsAssignableFrom(typeArgument)) { report = false; parameterTypeIdx++; } if (report) ReportIncompatibleType(context, parameterSyntax.Type, typeArgument, namedClassType, parameter); } // Nullability of value types is handled by the type compatibility test, // but nullability of reference types isn't if (parameterType.IsReferenceType && typeArgument.IsReferenceType && parameterType.NullableAnnotation == NullableAnnotation.NotAnnotated && typeArgument.NullableAnnotation == NullableAnnotation.Annotated) ReportNullabilityMismatch(context, parameterSyntax.Type, typeArgument, namedClassType, parameter); // Only move the parameter type index forward when the current parameter is not a 'params' if (!parameter.IsParams) parameterTypeIdx++; } if (typeArgumentIdx < returnTypeArguments.Length) ReportExtraTypeArguments(context, attribute, theoryDataType); } } ================================================ FILE: src/xunit.analyzers/X1000/CollectionDefinitionsMustBePublic.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class CollectionDefinitionClassesMustBePublic : XunitDiagnosticAnalyzer { public CollectionDefinitionClassesMustBePublic() : base(Descriptors.X1027_CollectionDefinitionClassMustBePublic) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.CollectionDefinitionAttributeType is null) return; if (context.Symbol.DeclaredAccessibility == Accessibility.Public) return; if (context.Symbol is not INamedTypeSymbol classSymbol) return; var doesClassContainCollectionDefinitionAttribute = classSymbol .GetAttributes() .Any(a => xunitContext.Core.CollectionDefinitionAttributeType.IsAssignableFrom(a.AttributeClass)); if (!doesClassContainCollectionDefinitionAttribute) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1027_CollectionDefinitionClassMustBePublic, classSymbol.Locations.First(), classSymbol.Locations.Skip(1) ) ); }, SymbolKind.NamedType); } } ================================================ FILE: src/xunit.analyzers/X1000/ConstructorsOnFactAttributeSubclassShouldBePublic.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class ConstructorsOnFactAttributeSubclassShouldBePublic : XunitDiagnosticAnalyzer { public ConstructorsOnFactAttributeSubclassShouldBePublic() : base(Descriptors.X1043_ConstructorOnFactAttributeSubclassShouldBePublic) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); if (xunitContext.Core.FactAttributeType is null) return; context.RegisterSymbolAction(context => { if (context.Symbol is not IMethodSymbol method) return; var attributes = method.GetAttributes(); foreach (var attribute in attributes) { var attributeClass = attribute.AttributeClass; if (attributeClass is null) continue; if (!xunitContext.Core.FactAttributeType.IsAssignableFrom(attributeClass)) continue; var constructor = attribute.AttributeConstructor; if (constructor is null) continue; if (constructor.DeclaredAccessibility is Accessibility.ProtectedOrInternal or Accessibility.Internal) { if (attribute.ApplicationSyntaxReference?.GetSyntax(context.CancellationToken) is not AttributeSyntax attributeSyntax) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1043_ConstructorOnFactAttributeSubclassShouldBePublic, attributeSyntax.GetLocation(), constructor.ToDisplayString() ) ); } } }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/DataAttributeShouldBeUsedOnATheory.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class DataAttributeShouldBeUsedOnATheory : XunitDiagnosticAnalyzer { public DataAttributeShouldBeUsedOnATheory() : base(Descriptors.X1008_DataAttributeShouldBeUsedOnATheory) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null || xunitContext.Core.DataAttributeType is null) return; if (context.Symbol is not IMethodSymbol methodSymbol) return; var attributes = methodSymbol.GetAttributes(); if (attributes.Length == 0) return; // Instead of checking for Theory, we check for any Fact. If it is a Fact which is not a Theory, // we will let other rules (i.e. FactMethodShouldNotHaveTestData) handle that case. if (!attributes.ContainsAttributeType(xunitContext.Core.FactAttributeType) && attributes.ContainsAttributeType(xunitContext.Core.DataAttributeType)) { var properties = new Dictionary { [Constants.Properties.DataAttributeTypeName] = xunitContext.HasV3References ? Constants.Types.Xunit.DataAttribute_V3 : Constants.Types.Xunit.DataAttribute_V2 }.ToImmutableDictionary(); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1008_DataAttributeShouldBeUsedOnATheory, methodSymbol.Locations.First(), properties ) ); } }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/DoNotUseAsyncVoidForTestMethods.cs ================================================ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class DoNotUseAsyncVoidForTestMethods : XunitDiagnosticAnalyzer { public DoNotUseAsyncVoidForTestMethods() : base([ Descriptors.X1048_DoNotUseAsyncVoidForTestMethods_V2, Descriptors.X1049_DoNotUseAsyncVoidForTestMethods_V3, ]) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var attributeUsageType = TypeSymbolFactory.AttributeUsageAttribute(context.Compilation); if (attributeUsageType is null) return; context.RegisterSymbolAction(context => { if (context.Symbol is not IMethodSymbol method) return; if (!method.IsAsync || !method.ReturnsVoid) return; if (!method.IsTestMethod(xunitContext, attributeUsageType, strict: true)) return; var location = context.Symbol.Locations.FirstOrDefault(); if (location is null) return; var propertiesBuilder = ImmutableDictionary.CreateBuilder(); propertiesBuilder.Add(Constants.Properties.DeclaringType, method.ContainingType.ToDisplayString()); propertiesBuilder.Add(Constants.Properties.MemberName, method.Name); var properties = propertiesBuilder.ToImmutableDictionary(); if (xunitContext.HasV3References) context.ReportDiagnostic(Diagnostic.Create(Descriptors.X1049_DoNotUseAsyncVoidForTestMethods_V3, location, properties)); else context.ReportDiagnostic(Diagnostic.Create(Descriptors.X1048_DoNotUseAsyncVoidForTestMethods_V2, location, properties)); }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/DoNotUseBlockingTaskOperations.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using System.Threading.Tasks.Sources; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Text; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class DoNotUseBlockingTaskOperations : XunitDiagnosticAnalyzer { static readonly string[] blockingAwaiterMethods = [ // We will "steal" the name from TaskAwaiter, but we look for it by pattern: if the type is // assignable from ICriticalNotifyCompletion and it contains a method with this name. We // also explicitly look for IValueTaskSource and IValueTaskSource. nameof(IValueTaskSource.GetResult), ]; static readonly string[] blockingTaskMethods = [ // These are only on Task, and not on ValueTask nameof(Task.Wait), nameof(Task.WaitAny), nameof(Task.WaitAll), ]; static readonly string[] blockingTaskProperties = [ // These are on both Task and ValueTask nameof(Task.Result), ]; static readonly string[] whenAll = [ nameof(Task.WhenAll), ]; static readonly string[] whenAny = [ nameof(Task.WhenAny), ]; public DoNotUseBlockingTaskOperations() : base(Descriptors.X1031_DoNotUseBlockingTaskOperations) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); if (xunitContext.Core.FactAttributeType is null || xunitContext.Core.TheoryAttributeType is null) return; var iCriticalNotifyCompletionType = TypeSymbolFactory.ICriticalNotifyCompletion(context.Compilation); var iValueTaskSourceType = TypeSymbolFactory.IValueTaskSource(context.Compilation); var iValueTaskSourceOfTType = TypeSymbolFactory.IValueTaskSourceOfT(context.Compilation); var taskType = TypeSymbolFactory.Task(context.Compilation); var taskOfTType = TypeSymbolFactory.TaskOfT(context.Compilation); var valueTaskOfTType = TypeSymbolFactory.ValueTaskOfT(context.Compilation); if (taskType is null) return; // Need to dynamically look for ICriticalNotifyCompletion, for use with blockingAwaiterMethods context.RegisterOperationAction(context => { if (context.Operation is not IInvocationOperation invocation) return; var foundSymbol = FindSymbol(invocation.TargetMethod, invocation, taskType, blockingTaskMethods, xunitContext, out var foundSymbolName) || FindSymbol(invocation.TargetMethod, invocation, iCriticalNotifyCompletionType, blockingAwaiterMethods, xunitContext, out foundSymbolName) || FindSymbol(invocation.TargetMethod, invocation, iValueTaskSourceType, blockingAwaiterMethods, xunitContext, out foundSymbolName) || FindSymbol(invocation.TargetMethod, invocation, iValueTaskSourceOfTType, blockingAwaiterMethods, xunitContext, out foundSymbolName); if (!foundSymbol) return; // Ignore anything inside a lambda expression or a local function for (var current = context.Operation; current is not null; current = current.Parent) if (current is IAnonymousFunctionOperation or ILocalFunctionOperation) return; var symbolsForSearch = default(IEnumerable); switch (foundSymbolName) { case nameof(IValueTaskSource.GetResult): { // Only handles 'task.GetAwaiter().GetResult()', which gives us direct access to the task // TODO: How hard would it be to trace back something like: // var awaiter = task.GetAwaiter(); // var result = awaiter.GetResult(); if (invocation.Instance is IInvocationOperation getAwaiterOperation && getAwaiterOperation.TargetMethod.Name == nameof(Task.GetAwaiter) && getAwaiterOperation.Instance is ILocalReferenceOperation localReferenceOperation) symbolsForSearch = [localReferenceOperation.Local]; break; } case nameof(Task.Wait): { if (invocation.Instance is ILocalReferenceOperation localReferenceOperation) symbolsForSearch = [localReferenceOperation.Local]; break; } case nameof(Task.WaitAll): case nameof(Task.WaitAny): { if (invocation.Arguments.Length == 1 && invocation.Arguments[0].Value is IArrayCreationOperation arrayCreationOperation && arrayCreationOperation.Initializer is not null) symbolsForSearch = arrayCreationOperation.Initializer.ElementValues.OfType().Select(l => l.Local); break; } } if (symbolsForSearch is not null) if (TaskIsKnownToBeCompleted(invocation, symbolsForSearch, taskType, xunitContext)) return; // Should have two child nodes: "(some other code).(target method)" and the arguments var invocationChildren = invocation.Syntax.ChildNodes().ToList(); if (invocationChildren.Count != 2) return; // First child node should be split into two nodes: "(some other code)" and "(target method)" var methodCallChildren = invocationChildren[0].ChildNodes().ToList(); if (methodCallChildren.Count != 2) return; // Construct a location that covers the target method and arguments var length = methodCallChildren[1].Span.Length + invocationChildren[1].Span.Length; var textSpan = new TextSpan(methodCallChildren[1].SpanStart, length); var location = Location.Create(invocation.Syntax.SyntaxTree, textSpan); context.ReportDiagnostic(Diagnostic.Create(Descriptors.X1031_DoNotUseBlockingTaskOperations, location)); }, OperationKind.Invocation); context.RegisterOperationAction(context => { if (context.Operation is not IPropertyReferenceOperation reference) return; var foundSymbol = FindSymbol(reference.Property, reference, taskOfTType, blockingTaskProperties, xunitContext, out var foundSymbolName) || FindSymbol(reference.Property, reference, valueTaskOfTType, blockingTaskProperties, xunitContext, out foundSymbolName); if (!foundSymbol) return; if (foundSymbolName == nameof(Task.Result) && reference.Instance is ILocalReferenceOperation localReferenceOperation && TaskIsKnownToBeCompleted(reference, [localReferenceOperation.Local], taskType, xunitContext)) return; // Should have two child nodes: "(some other code)" and "(property name)" var propertyChildren = reference.Syntax.ChildNodes().ToList(); if (propertyChildren.Count != 2) return; var location = propertyChildren[1].GetLocation(); context.ReportDiagnostic(Diagnostic.Create(Descriptors.X1031_DoNotUseBlockingTaskOperations, location)); }, OperationKind.PropertyReference); } static bool FindSymbol( ISymbol symbol, IOperation operation, INamedTypeSymbol? targetType, string[] targetNames, XunitContext xunitContext, [NotNullWhen(true)] out string? foundSymbolName) { foundSymbolName = default; if (targetType is null) return false; var containingType = symbol.ContainingType; if (containingType.IsGenericType && targetType.IsGenericType) { containingType = containingType.ConstructUnboundGenericType(); targetType = targetType.ConstructUnboundGenericType(); } if (!targetType.IsAssignableFrom(containingType)) return false; if (!targetNames.Contains(symbol.Name)) return false; // Only trigger when you're inside a test method var (foundSymbol, lambdaOwner) = operation.IsInTestMethod(xunitContext); if (!foundSymbol || lambdaOwner is not null) return false; foundSymbolName = symbol.Name; return true; } static bool TaskIsKnownToBeCompleted( IOperation? operation, IEnumerable symbols, INamedTypeSymbol taskType, XunitContext xunitContext) { var ourOperations = new List(); var unfoundSymbols = new HashSet(symbols, SymbolEqualityComparer.Default); bool validateSafeTasks(IOperation op) { #if ROSLYN_LATEST foreach (var childOperation in op.ChildOperations) #else foreach (var childOperation in op.Children) #endif { // Stop looking once we've found the operation that is ours, since any // code after that operation isn't something we should consider if (ourOperations.Contains(childOperation)) break; // Could be marked as safe because of "Task.WhenAll(..., symbol, ...)" if (childOperation is IInvocationOperation childInvocationOperation) ValidateTasksInWhenAll(childInvocationOperation, unfoundSymbols, taskType, xunitContext); // Could be marked as safe because of "var symbol = await WhenAny(...)" if (childOperation is IVariableDeclaratorOperation variableDeclaratorOperation) ValidateTaskFromWhenAny(variableDeclaratorOperation, unfoundSymbols, taskType, xunitContext); // If we've run out of symbols to validate, we're done if (unfoundSymbols.Count == 0) return true; #if ROSLYN_LATEST if (childOperation.ChildOperations.Any(c => validateSafeTasks(c))) #else if (childOperation.Children.Any(c => validateSafeTasks(c))) #endif return true; } return false; } for (; operation is not null && unfoundSymbols.Count != 0; operation = operation.Parent) { if (operation is IBlockOperation blockOperation) if (validateSafeTasks(blockOperation)) return true; ourOperations.Add(operation); } return false; } static void ValidateTaskFromWhenAny( IVariableDeclaratorOperation operation, HashSet unfoundSymbols, INamedTypeSymbol taskType, XunitContext xunitContext) { if (!unfoundSymbols.Contains(operation.Symbol)) return; #if ROSLYN_LATEST if (operation.ChildOperations.FirstOrDefault() is not IVariableInitializerOperation variableInitializerOperation) #else if (operation.Children.FirstOrDefault() is not IVariableInitializerOperation variableInitializerOperation) #endif return; #if ROSLYN_LATEST if (variableInitializerOperation.Value.ChildOperations.FirstOrDefault() is not IInvocationOperation variableInitializerInvocationOperation) #else if (variableInitializerOperation.Value.Children.FirstOrDefault() is not IInvocationOperation variableInitializerInvocationOperation) #endif return; if (!FindSymbol(variableInitializerInvocationOperation.TargetMethod, variableInitializerInvocationOperation, taskType, whenAny, xunitContext, out var _)) return; unfoundSymbols.Remove(operation.Symbol); } static void ValidateTasksInWhenAll( IInvocationOperation operation, HashSet unfoundSymbols, INamedTypeSymbol taskType, XunitContext xunitContext) { if (!FindSymbol(operation.TargetMethod, operation, taskType, whenAll, xunitContext, out var _)) return; var argument = operation.Arguments.FirstOrDefault(); if (argument is null) return; if (argument.Value is not IArrayCreationOperation arrayCreation) return; if (arrayCreation.Initializer is null) return; foreach (var arrayElement in arrayCreation.Initializer.ElementValues.OfType()) unfoundSymbols.Remove(arrayElement.Local); } } ================================================ FILE: src/xunit.analyzers/X1000/DoNotUseConfigureAwait.cs ================================================ using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Text; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class DoNotUseConfigureAwait : XunitDiagnosticAnalyzer { public DoNotUseConfigureAwait() : base(Descriptors.X1030_DoNotUseConfigureAwait) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var taskType = TypeSymbolFactory.Task(context.Compilation); var taskOfTType = TypeSymbolFactory.TaskOfT(context.Compilation)?.ConstructUnboundGenericType(); var valueTaskType = TypeSymbolFactory.ValueTask(context.Compilation); var valueTaskOfTType = TypeSymbolFactory.ValueTaskOfT(context.Compilation)?.ConstructUnboundGenericType(); if (xunitContext.Core.FactAttributeType is null || xunitContext.Core.TheoryAttributeType is null) return; context.RegisterOperationAction(context => { if (context.Operation is not IInvocationOperation invocation) return; var methodSymbol = invocation.TargetMethod; if (methodSymbol.MethodKind != MethodKind.Ordinary || methodSymbol.Name != nameof(Task.ConfigureAwait)) return; bool match; if (methodSymbol.ContainingType.IsGenericType) { var unboundGeneric = methodSymbol.ContainingType.ConstructUnboundGenericType(); match = SymbolEqualityComparer.Default.Equals(unboundGeneric, taskOfTType) || SymbolEqualityComparer.Default.Equals(unboundGeneric, valueTaskOfTType); } else match = SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingType, taskType) || SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingType, valueTaskType); if (!match) return; var (foundSymbol, lambdaOwner) = invocation.IsInTestMethod(xunitContext); if (!foundSymbol || lambdaOwner is not null) return; // invocation should be two nodes: "(some other code).ConfigureAwait" and the arguments (like "(false)") var invocationChildren = invocation.Syntax.ChildNodes().ToList(); if (invocationChildren.Count != 2) return; // We only care about invocations with a single parameter var arguments = invocationChildren[1]; var argumentChildren = arguments.ChildNodes().ToList(); if (argumentChildren.Count != 1 || argumentChildren[0] is not ArgumentSyntax argumentSyntax) return; // Determine the invocation type and resolution var parameterType = invocation.TargetMethod.Parameters[0].Type; var configureAwaitOptions = TypeSymbolFactory.ConfigureAwaitOptions(context.Compilation); var argumentValue = argumentSyntax.ToFullString(); string resolution; string replacement; // We want to exempt calls with "(true)" because of CA2007 if (SymbolEqualityComparer.Default.Equals(parameterType, context.Compilation.GetSpecialType(SpecialType.System_Boolean))) { if (argumentSyntax.Expression is LiteralExpressionSyntax literalExpression && literalExpression.IsKind(SyntaxKind.TrueLiteralExpression)) return; resolution = "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007."; replacement = "true"; } // We want to exempt calls which include ConfigureAwaitOptions.ContinueOnCapturedContext else if (SymbolEqualityComparer.Default.Equals(parameterType, configureAwaitOptions)) { if (invocation.SemanticModel is null) return; if (ContainsContinueOnCapturedContext(argumentSyntax.Expression, invocation.SemanticModel, configureAwaitOptions, context.CancellationToken)) return; resolution = "Ensure ConfigureAwaitOptions.ContinueOnCapturedContext in the flags."; replacement = argumentValue + " | ConfigureAwaitOptions.ContinueOnCapturedContext"; } else return; // First child node should be split into three pieces: "(some other code)", ".", and "ConfigureAwait" var methodCallChildren = invocationChildren[0].ChildNodesAndTokens().ToList(); if (methodCallChildren.Count != 3) return; // Construct a location that covers "ConfigureAwait(arguments)" var length = methodCallChildren[2].Span.Length + invocationChildren[1].Span.Length; var textSpan = new TextSpan(methodCallChildren[2].SpanStart, length); var location = Location.Create(invocation.Syntax.SyntaxTree, textSpan); // Provide the original value and replacement value to the fixer var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ArgumentValue] = argumentValue; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1030_DoNotUseConfigureAwait, location, builder.ToImmutable(), argumentValue, resolution ) ); }, OperationKind.Invocation); } static bool ContainsContinueOnCapturedContext( ExpressionSyntax expression, SemanticModel semanticModel, INamedTypeSymbol configureAwaitOptions, CancellationToken cancellationToken) { // If we have a binary expression of bitwise OR, we evaluate both sides of the expression if (expression is BinaryExpressionSyntax binaryExpression && binaryExpression.Kind() == SyntaxKind.BitwiseOrExpression) return ContainsContinueOnCapturedContext(binaryExpression.Left, semanticModel, configureAwaitOptions, cancellationToken) || ContainsContinueOnCapturedContext(binaryExpression.Right, semanticModel, configureAwaitOptions, cancellationToken); // Look for constant value of enum type var symbol = semanticModel.GetSymbolInfo(expression, cancellationToken).Symbol; if (symbol is not null && SymbolEqualityComparer.Default.Equals(symbol.ContainingType, configureAwaitOptions) && symbol.Name == "ContinueOnCapturedContext") return true; return false; } } ================================================ FILE: src/xunit.analyzers/X1000/EnsureFixturesHaveASource.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class EnsureFixturesHaveASource : XunitDiagnosticAnalyzer { public EnsureFixturesHaveASource() : base(Descriptors.X1041_EnsureFixturesHaveASource) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var collectionAttributeType = xunitContext.Core.CollectionAttributeType; var collectionAttributeOfTType = xunitContext.V3Core?.CollectionAttributeOfTType?.ConstructUnboundGenericType(); var collectionDefinitionAttributeType = xunitContext.Core.CollectionDefinitionAttributeType; context.RegisterSymbolAction(context => { if (context.Symbol is not INamedTypeSymbol namedType) return; if (namedType.IsAbstract) return; if (!namedType.IsTestClass(xunitContext, strict: true)) return; // Only evaluate if there's a single public constructor var ctors = namedType .Constructors .Where(c => c is { IsStatic: false, DeclaredAccessibility: Accessibility.Public }) .ToImmutableArray(); if (ctors.Length != 1) return; // Get the collection name from [Collection], if present object? collectionDefinition = null; for (var type = namedType; type is not null && collectionDefinition is null; type = type.BaseType) { // Check [Collection("name"))] or [Collection(typeof(T))] collectionDefinition = type .GetAttributes() .FirstOrDefault(a => collectionAttributeType.IsAssignableFrom(a.AttributeClass)) ?.ConstructorArguments .FirstOrDefault() .Value; // Check [Collection] if (collectionDefinition is null && collectionAttributeOfTType is not null) collectionDefinition = type .GetAttributes() .FirstOrDefault(a => collectionAttributeOfTType.IsAssignableFrom(a.AttributeClass)) ?.AttributeClass ?.TypeArguments .FirstOrDefault(); } // Need to construct a full set of types we know can be resolved. Start with things // like ITestOutputHelper and ITestContextAccessor (since they're injected by the framework) var validConstructorArgumentTypes = new HashSet(SymbolEqualityComparer.Default) { xunitContext.Core.ITestOutputHelperType, xunitContext.V3Core?.ITestContextAccessorType }; // Add types from IClassFixture<> on the class var classFixtureType = xunitContext.Core.IClassFixtureType?.ConstructUnboundGenericType(); validConstructorArgumentTypes.AddRange( namedType .AllInterfaces .Where(i => i.IsGenericType && SymbolEqualityComparer.Default.Equals(classFixtureType, i.ConstructUnboundGenericType())) .Select(i => i.TypeArguments.First()) ); // Determine how we've referenced the collection, and whether there's an associated type var collectionDefinitionType = collectionDefinition as ITypeSymbol; if (collectionDefinitionType is null && collectionDefinition is string collectionDefinitionName) collectionDefinitionType = namedType.ContainingAssembly.FindNamedType( symbol => symbol.GetAttributes().Any(a => a.AttributeClass.IsAssignableFrom(collectionDefinitionAttributeType) && !a.ConstructorArguments.IsDefaultOrEmpty && a.ConstructorArguments[0].Value?.ToString() == collectionDefinitionName ) ); // Add types from IClassFixture<> and ICollectionFixture<> on the collection definition if (collectionDefinitionType is not null) { var collectionFixtureType = xunitContext.Core.ICollectionFixtureType?.ConstructUnboundGenericType(); foreach (var @interface in collectionDefinitionType.AllInterfaces.Where(i => i.IsGenericType)) { var unboundGeneric = @interface.ConstructUnboundGenericType(); if (SymbolEqualityComparer.Default.Equals(classFixtureType, unboundGeneric) || SymbolEqualityComparer.Default.Equals(collectionFixtureType, unboundGeneric)) { var fixtureTypeSymbol = @interface.TypeArguments.First(); if (fixtureTypeSymbol is INamedTypeSymbol namedFixtureType) { if (xunitContext.HasV3References && namedFixtureType.IsGenericType && namedFixtureType.TypeArguments.Any(t => t is ITypeParameterSymbol)) namedFixtureType = namedFixtureType.ConstructedFrom; validConstructorArgumentTypes.Add(namedFixtureType); } } } } // Add types from AssemblyFixtureAttribute on the assembly var assemblyFixtureAttributeType = xunitContext.V3Core?.AssemblyFixtureAttributeType; if (assemblyFixtureAttributeType is not null) validConstructorArgumentTypes.AddRange( namedType .ContainingAssembly .GetAttributes() .Where(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, assemblyFixtureAttributeType)) .Select(a => a.ConstructorArguments[0].Value as ITypeSymbol) ); foreach (var parameter in ctors[0].Parameters.Where(p => !p.IsOptional && !p.IsParams && !validConstructorArgumentTypes.Contains(p.Type) && (xunitContext.HasV2References || p.Type is not INamedTypeSymbol nts || !nts.IsGenericType || !validConstructorArgumentTypes.Contains(nts.ConstructedFrom)))) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1041_EnsureFixturesHaveASource, parameter.Locations.FirstOrDefault(), parameter.Name ) ); }, SymbolKind.NamedType); } } ================================================ FILE: src/xunit.analyzers/X1000/FactMethodMustNotHaveParameters.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class FactMethodMustNotHaveParameters : XunitDiagnosticAnalyzer { public FactMethodMustNotHaveParameters() : base(Descriptors.X1001_FactMethodMustNotHaveParameters) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null) return; if (context.Symbol is not IMethodSymbol symbol) return; if (symbol.Parameters.IsEmpty) return; var attributes = symbol.GetAttributes(); if (!attributes.IsEmpty && attributes.ContainsAttributeType(xunitContext.Core.FactAttributeType, exactMatch: true)) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1001_FactMethodMustNotHaveParameters, symbol.Locations.First(), symbol.Name ) ); }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/FactMethodShouldNotHaveTestData.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class FactMethodShouldNotHaveTestData : XunitDiagnosticAnalyzer { public FactMethodShouldNotHaveTestData() : base(Descriptors.X1005_FactMethodShouldNotHaveTestData) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null || xunitContext.Core.TheoryAttributeType is null || xunitContext.Core.DataAttributeType is null) return; if (context.Symbol is not IMethodSymbol symbol) return; var attributes = symbol.GetAttributes(); if (attributes.Length > 1 && attributes.ContainsAttributeType(xunitContext.Core.FactAttributeType, exactMatch: true) && !attributes.ContainsAttributeType(xunitContext.Core.TheoryAttributeType) && attributes.ContainsAttributeType(xunitContext.Core.DataAttributeType)) { var properties = new Dictionary { [Constants.Properties.DataAttributeTypeName] = xunitContext.HasV3References ? Constants.Types.Xunit.DataAttribute_V3 : Constants.Types.Xunit.DataAttribute_V2 }.ToImmutableDictionary(); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1005_FactMethodShouldNotHaveTestData, symbol.Locations.First(), properties ) ); } }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/InlineDataMustMatchTheoryParameters.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class InlineDataMustMatchTheoryParameters : XunitDiagnosticAnalyzer { public InlineDataMustMatchTheoryParameters() : base( Descriptors.X1009_InlineDataMustMatchTheoryParameters_TooFewValues, Descriptors.X1010_InlineDataMustMatchTheoryParameters_IncompatibleValueType, Descriptors.X1011_InlineDataMustMatchTheoryParameters_ExtraValue, Descriptors.X1012_InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameter ) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); if (xunitContext.Core.TheoryAttributeType is null || xunitContext.Core.InlineDataAttributeType is null) return; var xunitSupportsParameterArrays = xunitContext.Core.TheorySupportsParameterArrays; var xunitSupportsDefaultParameterValues = xunitContext.Core.TheorySupportsDefaultParameterValues; var compilation = context.Compilation; var systemRuntimeInteropServicesOptionalAttribute = TypeSymbolFactory.OptionalAttribute(compilation); var objectArrayType = compilation.CreateArrayTypeSymbol(compilation.ObjectType); context.RegisterSymbolAction(context => { if (context.Symbol is not IMethodSymbol method) return; var attributes = method.GetAttributes(); if (!attributes.ContainsAttributeType(xunitContext.Core.TheoryAttributeType)) return; foreach (var attribute in attributes) { context.CancellationToken.ThrowIfCancellationRequested(); if (!SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, xunitContext.Core.InlineDataAttributeType)) continue; // Check if the semantic model indicates there are no syntax/compilation errors if (attribute.ConstructorArguments.Length != 1 || !SymbolEqualityComparer.Default.Equals(objectArrayType, attribute.ConstructorArguments.FirstOrDefault().Type)) continue; if (attribute.ApplicationSyntaxReference?.GetSyntax(context.CancellationToken) is not AttributeSyntax attributeSyntax) return; var arrayStyle = ParameterArrayStyleType.Initializer; var dataParameterExpressions = GetParameterExpressionsFromArrayArgument(attributeSyntax); if (dataParameterExpressions is null) { arrayStyle = ParameterArrayStyleType.Params; dataParameterExpressions = attributeSyntax .ArgumentList ?.Arguments .Select(a => a.Expression) .ToList() ?? []; } var dataArrayArgument = attribute.ConstructorArguments.Single(); // Need to special case InlineData(null) as the compiler will treat the whole data array as being initialized to null #pragma warning disable IDE0303 // Cannot convert this due to Roslyn 3.11 var values = dataArrayArgument.IsNull ? ImmutableArray.Create(dataArrayArgument) : dataArrayArgument.Values; #pragma warning restore IDE0303 if (values.Length < method.Parameters.Count(p => RequiresMatchingValue(p, xunitSupportsParameterArrays, xunitSupportsDefaultParameterValues, systemRuntimeInteropServicesOptionalAttribute))) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ParameterArrayStyle] = arrayStyle.ToString(); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1009_InlineDataMustMatchTheoryParameters_TooFewValues, attributeSyntax.GetLocation(), builder.ToImmutable() ) ); } int valueIdx = 0, paramIdx = 0; for (; valueIdx < values.Length && paramIdx < method.Parameters.Length; valueIdx++) { var parameter = method.Parameters[paramIdx]; var value = values[valueIdx]; // If the value isn't legal (malformed or illegal type), then just skip validation and let // the compiler report the problem. if (value.Kind == TypedConstantKind.Error) continue; // If the parameter type is object, everything is compatible, though we still need to check for nullability if (SymbolEqualityComparer.Default.Equals(parameter.Type, compilation.ObjectType) && (!value.IsNull || parameter.Type.NullableAnnotation != NullableAnnotation.NotAnnotated)) { paramIdx++; continue; } // If this is a params array (and we're using a version of xUnit.net that supports params arrays), // get the element type so we can compare it appropriately. var paramsElementType = xunitSupportsParameterArrays && parameter.IsParams && parameter.Type is IArrayTypeSymbol arrayParam ? arrayParam.ElementType : null; // For params array of object, just consume everything that's left if (paramsElementType is not null && SymbolEqualityComparer.Default.Equals(paramsElementType, compilation.ObjectType) && paramsElementType.NullableAnnotation != NullableAnnotation.NotAnnotated) { valueIdx = values.Length; break; } if (value.IsNull) { // Special case: if this is the only value of the params array, and it's null, // and the params array itself is nullable, then this is allowable, since we'll // end up passing null for the array itself. if (paramsElementType is not null && valueIdx == values.Length - 1 && parameter.Type.NullableAnnotation == NullableAnnotation.Annotated) { valueIdx = values.Length; break; } var isValueTypeParam = paramsElementType is not null ? paramsElementType.IsValueType && paramsElementType.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T : parameter.Type.IsValueType && parameter.Type.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T; var isNonNullableReferenceTypeParam = paramsElementType is not null ? paramsElementType.IsReferenceType && paramsElementType.NullableAnnotation == NullableAnnotation.NotAnnotated : parameter.Type.IsReferenceType && parameter.Type.NullableAnnotation == NullableAnnotation.NotAnnotated; if (isValueTypeParam || isNonNullableReferenceTypeParam) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ParameterIndex] = paramIdx.ToString(CultureInfo.InvariantCulture); builder[Constants.Properties.ParameterName] = parameter.Name; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1012_InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameter, valueIdx < dataParameterExpressions.Count ? dataParameterExpressions[valueIdx].GetLocation() : null, builder.ToImmutable(), parameter.Name, SymbolDisplay.ToDisplayString(paramsElementType ?? parameter.Type) ) ); } } else { if (value.Type is null) continue; var isCompatible = ConversionChecker.IsConvertible(compilation, value.Type, parameter.Type, xunitContext, value.Kind == TypedConstantKind.Primitive ? value.Value : null); if (!isCompatible && paramsElementType is not null) isCompatible = ConversionChecker.IsConvertible(compilation, value.Type, paramsElementType, xunitContext, value.Kind == TypedConstantKind.Primitive ? value.Value : null); if (!isCompatible) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ParameterIndex] = paramIdx.ToString(CultureInfo.InvariantCulture); builder[Constants.Properties.ParameterName] = parameter.Name; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1010_InlineDataMustMatchTheoryParameters_IncompatibleValueType, valueIdx < dataParameterExpressions.Count ? dataParameterExpressions[valueIdx].GetLocation() : null, builder.ToImmutable(), parameter.Name, SymbolDisplay.ToDisplayString(paramsElementType ?? parameter.Type) ) ); } } if (!parameter.IsParams) { // Stop moving paramIdx forward if the argument is a parameter array, regardless of xunit's support for it paramIdx++; } } for (; valueIdx < values.Length; valueIdx++) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ParameterIndex] = valueIdx.ToString(CultureInfo.InvariantCulture); builder[Constants.Properties.ParameterSpecialType] = values[valueIdx].Type?.SpecialType.ToString() ?? string.Empty; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1011_InlineDataMustMatchTheoryParameters_ExtraValue, valueIdx < dataParameterExpressions.Count ? dataParameterExpressions[valueIdx].GetLocation() : null, builder.ToImmutable(), values[valueIdx].ToCSharpString() ) ); } } }, SymbolKind.Method); } static bool RequiresMatchingValue( IParameterSymbol parameter, bool supportsParamsArray, bool supportsDefaultValue, INamedTypeSymbol? optionalAttribute) => !(parameter.HasExplicitDefaultValue && supportsDefaultValue) && !(parameter.IsParams && supportsParamsArray) && !parameter.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, optionalAttribute)); static IList? GetParameterExpressionsFromArrayArgument(AttributeSyntax attribute) { if (attribute.ArgumentList?.Arguments.Count != 1) return null; var argumentExpression = attribute.ArgumentList.Arguments.Single().Expression; var initializer = argumentExpression.Kind() switch { SyntaxKind.ArrayCreationExpression => ((ArrayCreationExpressionSyntax)argumentExpression).Initializer, SyntaxKind.ImplicitArrayCreationExpression => ((ImplicitArrayCreationExpressionSyntax)argumentExpression).Initializer, _ => null, }; if (initializer is null) return null; return [.. initializer.Expressions]; } public enum ParameterArrayStyleType { /// /// E.g. InlineData(1, 2, 3) /// Params, /// /// E.g. InlineData(data: new object[] { 1, 2, 3 }) or InlineData(new object[] { 1, 2, 3 }) /// Initializer, } } ================================================ FILE: src/xunit.analyzers/X1000/InlineDataShouldBeUniqueWithinTheory.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class InlineDataShouldBeUniqueWithinTheory : XunitDiagnosticAnalyzer { public InlineDataShouldBeUniqueWithinTheory() : base(Descriptors.X1025_InlineDataShouldBeUniqueWithinTheory) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => AnalyzeMethod(context, xunitContext), SymbolKind.Method); } static void AnalyzeMethod( SymbolAnalysisContext context, XunitContext xunitContext) { if (xunitContext.Core.TheoryAttributeType is null) return; if (context.Symbol is not IMethodSymbol method) return; var methodAllAttributes = method.GetAttributes(); if (!methodAllAttributes.ContainsAttributeType(xunitContext.Core.TheoryAttributeType)) return; var objectArrayType = TypeSymbolFactory.ObjectArray(context.Compilation); var wellFormedInlineDataAttributes = methodAllAttributes .Where(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, xunitContext.Core.InlineDataAttributeType) && HasAttributeDeclarationNoCompilationErrors(a, objectArrayType)); AnalyzeInlineDataAttributesWithinTheory(context, wellFormedInlineDataAttributes); } static void AnalyzeInlineDataAttributesWithinTheory( SymbolAnalysisContext context, IEnumerable inlineDataAttributes) { if (context.Symbol is not IMethodSymbol attributeRelatedMethod) return; var uniqueAttributes = new HashSet(new InlineDataUniquenessComparer(attributeRelatedMethod)); foreach (var currentInlineData in inlineDataAttributes) { context.CancellationToken.ThrowIfCancellationRequested(); #pragma warning disable CA1868 // This is not a strict guard, since we're reporting errors if (uniqueAttributes.Contains(currentInlineData)) ReportDuplicate(context, currentInlineData); else uniqueAttributes.Add(currentInlineData); #pragma warning restore CA1868 } } static AttributeSyntax? GetAttributeSyntax( SymbolAnalysisContext context, AttributeData attribute) => attribute.ApplicationSyntaxReference?.GetSyntax(context.CancellationToken) as AttributeSyntax; static void ReportDuplicate( SymbolAnalysisContext context, AttributeData duplicateAttribute) { if (context.Symbol is not IMethodSymbol method) return; var attributeSyntax = GetAttributeSyntax(context, duplicateAttribute); if (attributeSyntax == null) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1025_InlineDataShouldBeUniqueWithinTheory, attributeSyntax.GetLocation(), method.Name, method.ContainingType.Name ) ); } static bool HasAttributeDeclarationNoCompilationErrors( AttributeData attribute, IArrayTypeSymbol objectArrayType) => attribute.ConstructorArguments.Length == 1 && SymbolEqualityComparer.Default.Equals(objectArrayType, attribute.ConstructorArguments.FirstOrDefault().Type); sealed class InlineDataUniquenessComparer(IMethodSymbol attributeRelatedMethod) : IEqualityComparer { readonly ImmutableArray methodParametersWithExplicitDefaults = attributeRelatedMethod .Parameters .Where(p => p.HasExplicitDefaultValue) .ToImmutableArray(); public bool Equals( AttributeData? x, AttributeData? y) { if (x is null || y is null) return x is null && y is null; var xArguments = GetEffectiveTestArguments(x); var yArguments = GetEffectiveTestArguments(y); var areBothNullEntirely = IsSingleNullByInlineDataOrByDefaultParamValue(xArguments) && IsSingleNullByInlineDataOrByDefaultParamValue(yArguments); return areBothNullEntirely || AreArgumentsEqual(xArguments, yArguments); } // Since arguments can be object[] at any level we need to compare 2 sequences of trees for equality. // The algorithm traverses each tree in a sequence and compares with the corresponding tree in the other sequence. // Any difference at any stage results in inequality proved and false returned. static bool AreArgumentsEqual( ImmutableArray xArguments, ImmutableArray yArguments) { if (xArguments.Length != yArguments.Length) return false; for (var i = 0; i < xArguments.Length; i++) { var x = xArguments[i]; var y = yArguments[i]; switch (x) { case TypedConstant xArgPrimitive when xArgPrimitive.Kind != TypedConstantKind.Array: switch (y) { case TypedConstant yArgPrimitive when yArgPrimitive.Kind != TypedConstantKind.Array: if (!xArgPrimitive.Equals(yArgPrimitive)) return false; break; case IParameterSymbol yMethodParamDefault: if (!object.Equals(xArgPrimitive.Value, yMethodParamDefault.ExplicitDefaultValue)) return false; break; default: return false; } break; case IParameterSymbol xMethodParamDefault: switch (y) { case TypedConstant yArgPrimitive when yArgPrimitive.Kind != TypedConstantKind.Array: if (!object.Equals(xMethodParamDefault.ExplicitDefaultValue, yArgPrimitive.Value)) return false; break; case IParameterSymbol yMethodParamDefault: if (!object.Equals(xMethodParamDefault.ExplicitDefaultValue, yMethodParamDefault.ExplicitDefaultValue)) return false; break; default: return false; } break; case TypedConstant xArgArray when xArgArray.Kind == TypedConstantKind.Array && !xArgArray.IsNull: switch (y) { case TypedConstant yArgArray when yArgArray.Kind == TypedConstantKind.Array && !yArgArray.IsNull: if (!AreArgumentsEqual(xArgArray.Values.Cast().ToImmutableArray(), yArgArray.Values.Cast().ToImmutableArray())) return false; break; default: return false; } break; default: return false; } } return true; } // A special search for a degenerated case of either: // 1. InlineData(null) or // 2. InlineData() and a single param method with default returning null. static bool IsSingleNullByInlineDataOrByDefaultParamValue(ImmutableArray args) { if (args.Length != 1) return false; return args[0] switch { TypedConstant xSingleNull when xSingleNull.Kind == TypedConstantKind.Array && xSingleNull.IsNull => true, IParameterSymbol xParamDefaultNull when xParamDefaultNull.ExplicitDefaultValue is null => true, _ => false, }; } /// /// Flattens a collection of arguments (ie. new object[] {1, new object[] {2}} becomes a collection of {1, 2} /// and computes accumulative hash code. Exact comparison is carried out in impl. /// public int GetHashCode(AttributeData attributeData) { var arguments = GetEffectiveTestArguments(attributeData); var flattened = GetFlattenedArgumentPrimitives(arguments); var hash = 17; foreach (var primitive in flattened) hash = (hash * 31) + (primitive?.GetHashCode() ?? 0); return hash; } static ImmutableArray GetFlattenedArgumentPrimitives(IEnumerable arguments) { var results = new List(); foreach (var argument in arguments) { switch (argument) { case TypedConstant argPrimitive when argPrimitive.Kind != TypedConstantKind.Array: results.Add(argPrimitive.Value); break; case IParameterSymbol methodParameterWithDefault: results.Add(methodParameterWithDefault.ExplicitDefaultValue); break; case TypedConstant argArray when argArray.Kind == TypedConstantKind.Array && !argArray.IsNull: results.AddRange(GetFlattenedArgumentPrimitives(argArray.Values.Cast())); break; case TypedConstant nullObjectArray when nullObjectArray.Kind == TypedConstantKind.Array && nullObjectArray.IsNull: results.Add(null); break; } } #pragma warning disable IDE0305 // Cannot convert this due to Roslyn 3.11 return results.ToImmutableArray(); #pragma warning restore IDE0305 } // Effective test arguments consist of InlineData argument typed constants concatenated // with default parameters of a test method providing such default parameters are not covered by InlineData arguments. ImmutableArray GetEffectiveTestArguments(AttributeData attributeData) { var inlineDataObjectArrayArgument = attributeData.ConstructorArguments.Single(); // special case InlineData(null): the compiler will treat the whole data array as being initialized to null var inlineDataArguments = inlineDataObjectArrayArgument.IsNull #pragma warning disable IDE0303 // Cannot convert this due to Roslyn 3.11 ? ImmutableArray.Create(inlineDataObjectArrayArgument) #pragma warning restore IDE0303 : inlineDataObjectArrayArgument.Values; var methodDefaultValuesNonCoveredByInlineData = methodParametersWithExplicitDefaults .Where(p => p.Ordinal >= inlineDataArguments.Length); var allMethodArguments = inlineDataArguments .Cast() .Concat(methodDefaultValuesNonCoveredByInlineData); return allMethodArguments.ToImmutableArray(); } } } ================================================ FILE: src/xunit.analyzers/X1000/LocalFunctionsCannotBeTestFunctions.cs ================================================ using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class LocalFunctionsCannotBeTestFunctions : XunitDiagnosticAnalyzer { public LocalFunctionsCannotBeTestFunctions() : base(Descriptors.X1029_LocalFunctionsCannotBeTestFunctions) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSyntaxNodeAction(context => { if (context.Node is not LocalFunctionStatementSyntax syntax) return; var attributeBaseTypes = new List(); if (xunitContext.Core.FactAttributeType is not null) attributeBaseTypes.Add(xunitContext.Core.FactAttributeType); if (xunitContext.Core.DataAttributeType is not null) attributeBaseTypes.Add(xunitContext.Core.DataAttributeType); if (attributeBaseTypes.Count == 0) return; foreach (var attributeList in syntax.AttributeLists) foreach (var attribute in attributeList.Attributes) { var symbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol; if (symbol is null) continue; var attributeType = symbol.ContainingType; if (attributeType is null) continue; foreach (var attributeBaseType in attributeBaseTypes) if (attributeBaseType.IsAssignableFrom(attributeType)) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1029_LocalFunctionsCannotBeTestFunctions, attribute.GetLocation(), $"[{attribute.GetText()}]" ) ); } }, SyntaxKind.LocalFunctionStatement); } } ================================================ FILE: src/xunit.analyzers/X1000/MemberDataShouldReferenceValidMember.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class MemberDataShouldReferenceValidMember : XunitDiagnosticAnalyzer { public MemberDataShouldReferenceValidMember() : base( Descriptors.X1014_MemberDataShouldUseNameOfOperator, Descriptors.X1015_MemberDataMustReferenceExistingMember, Descriptors.X1016_MemberDataMustReferencePublicMember, Descriptors.X1017_MemberDataMustReferenceStaticMember, Descriptors.X1018_MemberDataMustReferenceValidMemberKind, Descriptors.X1019_MemberDataMustReferenceMemberOfValidType, Descriptors.X1020_MemberDataPropertyMustHaveGetter, Descriptors.X1021_MemberDataNonMethodShouldNotHaveParameters, Descriptors.X1034_MemberDataArgumentsMustMatchMethodParameters_NullShouldNotBeUsedForIncompatibleParameter, Descriptors.X1035_MemberDataArgumentsMustMatchMethodParameters_IncompatibleValueType, Descriptors.X1036_MemberDataArgumentsMustMatchMethodParameters_ExtraValue, Descriptors.X1037_TheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters, Descriptors.X1038_TheoryDataTypeArgumentsMustMatchTestMethodParameters_ExtraTypeParameters, Descriptors.X1039_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleTypes, Descriptors.X1040_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleNullability, Descriptors.X1042_MemberDataTheoryDataIsRecommendedForStronglyTypedAnalysis, Descriptors.X1053_MemberDataMemberMustBeStaticallyWrittenTo ) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); if (xunitContext.Core.MemberDataAttributeType is null) return; var compilation = context.Compilation; var theoryDataTypes = TypeSymbolFactory.TheoryData_ByGenericArgumentCount(compilation); var theoryDataRowTypes = TypeSymbolFactory.TheoryDataRow_ByGenericArgumentCount_V3(compilation); context.RegisterSyntaxNodeAction(context => { if (context.Node is not MethodDeclarationSyntax testMethod) return; var attributeLists = testMethod.AttributeLists; foreach (var attributeSyntax in attributeLists.WhereNotNull().SelectMany(attList => attList.Attributes)) { context.CancellationToken.ThrowIfCancellationRequested(); // Only work against MemberDataAttribute var semanticModel = context.SemanticModel; if (!SymbolEqualityComparer.Default.Equals(semanticModel.GetTypeInfo(attributeSyntax, context.CancellationToken).Type, xunitContext.Core.MemberDataAttributeType)) continue; // Need the name of the member to do anything if (attributeSyntax.ArgumentList is null) continue; var memberNameArgument = attributeSyntax.ArgumentList.Arguments.FirstOrDefault(); if (memberNameArgument is null) continue; var constantValue = semanticModel.GetConstantValue(memberNameArgument.Expression, context.CancellationToken); if (constantValue.Value is not string memberName) continue; // Figure out which parameters are named property arguments var propertyAttributeParameters = attributeSyntax .ArgumentList .Arguments .Count(a => !string.IsNullOrEmpty(a.NameEquals?.Name.Identifier.ValueText)); // Everything else will be potential arguments (for the method-based MemberData) var paramsCount = attributeSyntax.ArgumentList.Arguments.Count - 1 - propertyAttributeParameters; // Determine what type and member name the MemberData targets var (testClassTypeSymbol, declaredMemberTypeSymbol) = GetClassTypesForAttribute(attributeSyntax.ArgumentList, semanticModel, context.CancellationToken); if (declaredMemberTypeSymbol is null || testClassTypeSymbol is null) continue; // Ensure we're pointing to something that exists var memberSymbol = FindMemberSymbol(memberName, declaredMemberTypeSymbol, paramsCount); if (memberSymbol is null) { ReportMissingMember(context, attributeSyntax, memberName, declaredMemberTypeSymbol); return; } // Ensure we pointing to a field, method, or property var memberReturnType = memberSymbol switch { IFieldSymbol field => field.Type, IMethodSymbol method => method.ReturnType, IPropertySymbol prop => prop.Type, _ => null, }; if (memberReturnType is null) { ReportIncorrectMemberType(context, attributeSyntax); return; } // Make sure they use nameof() instead of a string constant for the member name if (compilation is CSharpCompilation cSharpCompilation && cSharpCompilation.LanguageVersion >= LanguageVersion.CSharp6 && memberNameArgument.Expression.IsKind(SyntaxKind.StringLiteralExpression)) ReportUseNameof(context, memberNameArgument, memberName, testClassTypeSymbol, memberSymbol); // Every error we report will include at least these two properties var memberProperties = new Dictionary { { Constants.AttributeProperties.DeclaringType, declaredMemberTypeSymbol.ToDisplayString() }, { Constants.AttributeProperties.MemberName, memberName } }.ToImmutableDictionary(); // Make sure the member is public if (memberSymbol.DeclaredAccessibility != Accessibility.Public) ReportNonPublicAccessibility(context, attributeSyntax, memberProperties); // Make sure the member is static if (!memberSymbol.IsStatic) ReportNonStatic(context, attributeSyntax, memberProperties); if (!IsInitialized(memberSymbol, context)) ReportMemberMustBeWrittenTo(context, memberSymbol); // Unwrap Task or ValueTask, but only for v3 if (xunitContext.HasV3References && memberReturnType is INamedTypeSymbol namedMemberReturnType && namedMemberReturnType.IsGenericType) { var taskTypes = new[] { TypeSymbolFactory.TaskOfT(compilation)?.ConstructUnboundGenericType(), TypeSymbolFactory.ValueTaskOfT(compilation)?.ConstructUnboundGenericType(), }.WhereNotNull().ToArray(); var unboundGeneric = namedMemberReturnType.ConstructUnboundGenericType(); if (taskTypes.Any(taskType => SymbolEqualityComparer.Default.Equals(unboundGeneric, taskType))) memberReturnType = namedMemberReturnType.TypeArguments[0]; } // Make sure the member returns a compatible type var iEnumerableOfTheoryDataRowType = TypeSymbolFactory.IEnumerableOfITheoryDataRow(compilation); var iAsyncEnumerableOfTheoryDataRowType = TypeSymbolFactory.IAsyncEnumerableOfITheoryDataRow(compilation); var iEnumerableOfTupleType = TypeSymbolFactory.IEnumerableOfTuple(compilation); var iAsyncEnumerableOfTupleType = TypeSymbolFactory.IAsyncEnumerableOfTuple(compilation); var IsValidMemberReturnType = VerifyDataSourceReturnType(context, compilation, xunitContext, memberReturnType, memberProperties, attributeSyntax, iEnumerableOfTheoryDataRowType, iAsyncEnumerableOfTheoryDataRowType, iEnumerableOfTupleType, iAsyncEnumerableOfTupleType); // Make sure public properties have a public getter if (memberSymbol.Kind == SymbolKind.Property && memberSymbol.DeclaredAccessibility == Accessibility.Public) if (memberSymbol is IPropertySymbol propertySymbol) { var getMethod = propertySymbol.GetMethod; if (getMethod is null || getMethod.DeclaredAccessibility != Accessibility.Public) ReportNonPublicPropertyGetter(context, attributeSyntax); } // If the member returns TheoryData<> or TheoryDataRow<>, ensure that the types are compatible. // If the member does not return TheoryData<> or TheoryDataRow<>, gently suggest to the user // to switch for better type safety. if (IsTheoryDataType(memberReturnType, theoryDataTypes, out var theoryReturnType)) VerifyGenericArgumentTypes(semanticModel, context, testMethod, theoryDataTypes[0], theoryReturnType, memberName, declaredMemberTypeSymbol, attributeSyntax); else if (IsGenericTheoryDataRowType(memberReturnType, iEnumerableOfTheoryDataRowType, iAsyncEnumerableOfTheoryDataRowType, theoryDataRowTypes, out var theoryDataReturnType)) VerifyGenericArgumentTypes(semanticModel, context, testMethod, theoryDataRowTypes[0], theoryDataReturnType, memberName, declaredMemberTypeSymbol, attributeSyntax); else if (IsValidMemberReturnType) ReportMemberReturnsTypeUnsafeValue(context, attributeSyntax, xunitContext.HasV3References ? "TheoryData<> or IEnumerable>" : "TheoryData<>"); // Get the arguments that are to be passed to the method var extraArguments = attributeSyntax.ArgumentList.Arguments.Skip(1).TakeWhile(a => a.NameEquals is null).ToList(); if (memberSymbol.Kind == SymbolKind.Method) VerifyDataMethodParameterUsage(semanticModel, context, compilation, xunitContext, memberSymbol, memberName, extraArguments); else { // Make sure we only have arguments for method-based member data if (extraArguments.Count != 0) ReportIllegalNonMethodArguments(context, attributeSyntax, extraArguments); } } }, SyntaxKind.MethodDeclaration); } static bool IsInitialized( ISymbol memberSymbol, SyntaxNodeAnalysisContext context) { if (!memberSymbol.IsStatic || memberSymbol is IMethodSymbol) // assume initialized, if nonstatic or method to avoid spurious results return true; if (memberSymbol.DeclaringSyntaxReferences.IsEmpty) return true; var semantics = context.SemanticModel; var declarationReference = memberSymbol.DeclaringSyntaxReferences.First(); var declarationSyntax = declarationReference.GetSyntax(); if (declarationSyntax is PropertyDeclarationSyntax prop && (prop.Initializer != null || prop.AccessorList?.Accessors.FirstOrDefault(decl => decl.Kind() == SyntaxKind.GetAccessorDeclaration)?.Body != null || prop.ExpressionBody != null)) return true; if (declarationSyntax is VariableDeclaratorSyntax field && field.Initializer != null) return true; var declarationContainer = declarationSyntax.FirstAncestorOrSelf()!; var staticConstructors = declarationContainer .DescendantNodes() .OfType() .Where(ctor => ctor.Modifiers.Any(SyntaxKind.StaticKeyword)); foreach (var ctor in staticConstructors) { // Look for direct assignments to the member var assignments = ctor .DescendantNodes(descendIntoChildren: _ => true, descendIntoTrivia: false) .OfType() .Where(assignment => { var assignedSymbol = semantics.GetSymbolInfo(assignment.Left).Symbol; return SymbolEqualityComparer.Default.Equals(assignedSymbol?.OriginalDefinition, memberSymbol); }); if (assignments.Any()) return true; } return false; } static ISymbol? FindMemberSymbol( string memberName, ITypeSymbol? type, int paramsCount) { if (paramsCount > 0 && FindMethodSymbol(memberName, type, paramsCount) is ISymbol methodSymbol) return methodSymbol; while (type is not null) { var memberSymbol = type.GetMembers(memberName).FirstOrDefault(); if (memberSymbol is not null) return memberSymbol; type = type.BaseType; } return null; } public static ISymbol? FindMethodSymbol( string memberName, ITypeSymbol? type, int paramsCount) { Guard.ArgumentNotNull(memberName); while (type is not null) { var methodSymbol = type .GetMembers(memberName) .OfType() .FirstOrDefault(x => x.Parameters.Length == paramsCount); if (methodSymbol is not null) return methodSymbol; type = type.BaseType; } return null; } static ITypeSymbol? FindTypeArgumentFromIEnumerable( ITypeSymbol? start, Compilation compilation) { if (start is not INamedTypeSymbol namedStart) return null; var iEnumerableOfT = TypeSymbolFactory.IEnumerableOfT(compilation); if (iEnumerableOfT is null) return null; var current = namedStart; while (current != null) { // If this class implements IEnumerable, return T type argument foreach (var iface in current.AllInterfaces) if (SymbolEqualityComparer.Default.Equals(iface.OriginalDefinition, iEnumerableOfT)) return iface.TypeArguments.FirstOrDefault(); // Navigate to its Base types to investigate if they implement IEnumerable, // attempt to resolve the base type from the source syntax (handles `: ValidExamples`). var declaredBase = ResolveDeclaredBaseTypeFromSyntax(current, compilation) as INamedTypeSymbol; // Prefer declaredBase when available; otherwise fall back to the BaseType symbol. var next = declaredBase ?? current.BaseType; // Stop if we reached object if (next?.SpecialType == SpecialType.System_Object) break; current = next; } return null; } public static (INamedTypeSymbol? TestClass, ITypeSymbol? MemberClass) GetClassTypesForAttribute( AttributeArgumentListSyntax attributeList, SemanticModel semanticModel, CancellationToken cancellationToken) { Guard.ArgumentNotNull(attributeList); Guard.ArgumentNotNull(semanticModel); var memberTypeArgument = attributeList.Arguments.FirstOrDefault(a => a.NameEquals?.Name.Identifier.ValueText == Constants.AttributeProperties.MemberType); var memberTypeSymbol = default(ITypeSymbol); if (memberTypeArgument?.Expression is TypeOfExpressionSyntax typeofExpression) { var typeSyntax = typeofExpression.Type; memberTypeSymbol = semanticModel.GetTypeInfo(typeSyntax, cancellationToken).Type; } var classSyntax = attributeList.FirstAncestorOrSelf(); if (classSyntax is null) return (null, null); var testClassTypeSymbol = semanticModel.GetDeclaredSymbol(classSyntax, cancellationToken); return (testClassTypeSymbol, memberTypeSymbol ?? testClassTypeSymbol); } static IList? GetParameterExpressionsFromArrayArgument( List arguments, SemanticModel semanticModel) { if (arguments.Count > 1) return [.. arguments.Select(a => a.Expression)]; if (arguments.Count != 1) return null; var argumentExpression = arguments.Single().Expression; var kind = argumentExpression.Kind(); var initializer = kind switch { SyntaxKind.ArrayCreationExpression => ((ArrayCreationExpressionSyntax)argumentExpression).Initializer, SyntaxKind.ImplicitArrayCreationExpression => ((ImplicitArrayCreationExpressionSyntax)argumentExpression).Initializer, _ => null, }; if (initializer is null) return [argumentExpression]; // In the special case where the argument is an object[], treat like params var type = semanticModel.GetTypeInfo(argumentExpression).Type; if (type is IArrayTypeSymbol arrayType && arrayType.ElementType.SpecialType == SpecialType.System_Object) return [.. initializer.Expressions]; return [argumentExpression]; } static bool IsGenericTheoryDataRowType( ITypeSymbol? memberReturnType, INamedTypeSymbol? iEnumerableOfTheoryDataRowType, INamedTypeSymbol? iAsyncEnumerableOfTheoryDataRowType, Dictionary theoryDataRowTypes, [NotNullWhen(true)] out INamedTypeSymbol? theoryReturnType) { theoryReturnType = default; if (iEnumerableOfTheoryDataRowType is null) return false; var rowType = memberReturnType.UnwrapEnumerable(iEnumerableOfTheoryDataRowType.OriginalDefinition); if (rowType is null && iAsyncEnumerableOfTheoryDataRowType is not null) rowType = memberReturnType.UnwrapEnumerable(iAsyncEnumerableOfTheoryDataRowType.OriginalDefinition); if (rowType is null) return false; var working = rowType as INamedTypeSymbol; for (; working is not null; working = working.BaseType) { var returnTypeArguments = working.TypeArguments; if (returnTypeArguments.Length != 0 && theoryDataRowTypes.TryGetValue(returnTypeArguments.Length, out var theoryDataType) && SymbolEqualityComparer.Default.Equals(theoryDataType, working.OriginalDefinition)) break; } if (working is null) return false; theoryReturnType = working; return true; } static bool IsTheoryDataType( ITypeSymbol? memberReturnType, Dictionary theoryDataTypes, [NotNullWhen(true)] out INamedTypeSymbol? theoryReturnType) { theoryReturnType = default; if (memberReturnType is not INamedTypeSymbol namedReturnType) return false; var working = namedReturnType; while (working is not null) { var returnTypeArguments = working.TypeArguments; if (theoryDataTypes.TryGetValue(returnTypeArguments.Length, out var theoryDataType) && SymbolEqualityComparer.Default.Equals(theoryDataType, working.OriginalDefinition)) break; working = working.BaseType; } if (working is null) return false; theoryReturnType = working; return true; } static bool ShouldSuppressX1015BecauseAllDerivedConcreteTypesHaveMember( SyntaxNodeAnalysisContext context, ITypeSymbol declaredMemberTypeSymbol, string memberName) { if (declaredMemberTypeSymbol is not INamedTypeSymbol baseType) return false; // Only consider the special-case from the issue: // - the declared type is an abstract class if (baseType.TypeKind != TypeKind.Class || !baseType.IsAbstract) return false; var compilation = context.SemanticModel.Compilation; static IEnumerable GetAllTypesFromNamespace(INamespaceSymbol @namespace) { foreach (var member in @namespace.GetMembers()) if (member is INamespaceSymbol childNs) foreach (var t in GetAllTypesFromNamespace(childNs)) yield return t; else if (member is INamedTypeSymbol namedType) foreach (var t in GetAllTypesFromType(namedType)) yield return t; } static IEnumerable GetAllTypesFromType(INamedTypeSymbol type) { yield return type; foreach (var nested in type.GetTypeMembers()) foreach (var t in GetAllTypesFromType(nested)) yield return t; } static bool IsDerivedFrom(INamedTypeSymbol type, INamedTypeSymbol baseType) { for (var current = type.BaseType; current is not null; current = current.BaseType) if (SymbolEqualityComparer.Default.Equals(current, baseType)) return true; return false; } var derivedConcreteTypes = GetAllTypesFromNamespace(compilation.Assembly.GlobalNamespace) .Where(t => t.TypeKind == TypeKind.Class && !t.IsAbstract && IsDerivedFrom(t, baseType)) .ToList(); // If we can't find any derived concrete types in this compilation, // we can't prove it's safe - do not suppress. if (derivedConcreteTypes.Count == 0) return false; // Suppress only if ALL derived concrete types contain the member. foreach (var derived in derivedConcreteTypes) if (derived.GetMembers(memberName).Length == 0) return false; return true; } static void ReportIllegalNonMethodArguments( SyntaxNodeAnalysisContext context, AttributeSyntax attribute, List extraArguments) { var span = TextSpan.FromBounds(extraArguments.First().Span.Start, extraArguments.Last().Span.End); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1021_MemberDataNonMethodShouldNotHaveParameters, Location.Create(attribute.SyntaxTree, span) ) ); } static void ReportIncorrectMemberType( SyntaxNodeAnalysisContext context, AttributeSyntax attribute) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1018_MemberDataMustReferenceValidMemberKind, attribute.GetLocation() ) ); static void ReportIncorrectReturnType( SyntaxNodeAnalysisContext context, INamedTypeSymbol iEnumerableOfObjectArrayType, INamedTypeSymbol? iAsyncEnumerableOfObjectArrayType, INamedTypeSymbol? iEnumerableOfTheoryDataRowType, INamedTypeSymbol? iAsyncEnumerableOfTheoryDataRowType, INamedTypeSymbol? iEnumerableOfTupleType, INamedTypeSymbol? iAsyncEnumerableOfTupleType, AttributeSyntax attribute, ImmutableDictionary memberProperties, ITypeSymbol memberType) { var validSymbols = "'" + SymbolDisplay.ToDisplayString(iEnumerableOfObjectArrayType) + "'"; // Only want the extra types when we know ITheoryDataRow is valid if (iAsyncEnumerableOfObjectArrayType is not null && iEnumerableOfTheoryDataRowType is not null && iAsyncEnumerableOfTheoryDataRowType is not null && iEnumerableOfTupleType is not null && iAsyncEnumerableOfTupleType is not null) #pragma warning disable RS1035 // The suggested fix is not available in this context validSymbols += string.Format( CultureInfo.CurrentCulture, ", '{0}', '{1}', '{2}', '{3}', or '{4}'", SymbolDisplay.ToDisplayString(iAsyncEnumerableOfObjectArrayType), SymbolDisplay.ToDisplayString(iEnumerableOfTheoryDataRowType), SymbolDisplay.ToDisplayString(iAsyncEnumerableOfTheoryDataRowType), SymbolDisplay.ToDisplayString(iEnumerableOfTupleType), SymbolDisplay.ToDisplayString(iAsyncEnumerableOfTupleType) ); #pragma warning restore RS1035 context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1019_MemberDataMustReferenceMemberOfValidType, attribute.GetLocation(), memberProperties, validSymbols, SymbolDisplay.ToDisplayString(memberType) ) ); } static void ReportMemberMethodParametersDoNotMatchArgumentTypes( SyntaxNodeAnalysisContext context, ExpressionSyntax syntax, IParameterSymbol parameter, ITypeSymbol? paramsElementType, ImmutableDictionary.Builder builder) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1035_MemberDataArgumentsMustMatchMethodParameters_IncompatibleValueType, syntax.GetLocation(), builder.ToImmutable(), parameter.Name, SymbolDisplay.ToDisplayString(paramsElementType ?? parameter.Type) ) ); static void ReportMemberMethodParameterNullability( SyntaxNodeAnalysisContext context, ExpressionSyntax syntax, IParameterSymbol parameter, ITypeSymbol? paramsElementType, ImmutableDictionary.Builder builder) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1034_MemberDataArgumentsMustMatchMethodParameters_NullShouldNotBeUsedForIncompatibleParameter, syntax.GetLocation(), builder.ToImmutable(), parameter.Name, SymbolDisplay.ToDisplayString(paramsElementType ?? parameter.Type) ) ); static void ReportMemberMethodTheoryDataExtraTypeArguments( SyntaxNodeAnalysisContext context, Location location, ImmutableDictionary.Builder builder, INamedTypeSymbol theoryDataType) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1038_TheoryDataTypeArgumentsMustMatchTestMethodParameters_ExtraTypeParameters, location, builder.ToImmutable(), SymbolDisplay.ToDisplayString(theoryDataType) ) ); static void ReportMemberMethodTheoryDataIncompatibleType( SyntaxNodeAnalysisContext context, Location location, ITypeSymbol theoryDataTypeParameter, INamedTypeSymbol memberType, string memberName, IParameterSymbol parameter) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1039_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleTypes, location, SymbolDisplay.ToDisplayString(theoryDataTypeParameter), memberType.Name + "." + memberName, parameter.Name ) ); static void ReportMemberMethodTheoryDataNullability( SyntaxNodeAnalysisContext context, Location location, ITypeSymbol theoryDataTypeParameter, INamedTypeSymbol memberType, string memberName, IParameterSymbol parameter) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1040_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleNullability, location, SymbolDisplay.ToDisplayString(theoryDataTypeParameter), memberType.Name + "." + memberName, parameter.Name ) ); static void ReportMemberMethodTheoryDataTooFewTypeArguments( SyntaxNodeAnalysisContext context, Location location, ImmutableDictionary.Builder builder, INamedTypeSymbol theoryDataType) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1037_TheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters, location, builder.ToImmutable(), SymbolDisplay.ToDisplayString(theoryDataType) ) ); static void ReportMemberMustBeWrittenTo( SyntaxNodeAnalysisContext context, ISymbol memberSymbol) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1053_MemberDataMemberMustBeStaticallyWrittenTo, memberSymbol.Locations.First(), memberSymbol.Name ) ); static void ReportMissingMember( SyntaxNodeAnalysisContext context, AttributeSyntax attribute, string memberName, ITypeSymbol declaredMemberTypeSymbol) { // Special case: abstract base class references MemberData member that exists only on derived types. // If ALL derived concrete types in this compilation contain the member, don't warn. if (ShouldSuppressX1015BecauseAllDerivedConcreteTypesHaveMember(context, declaredMemberTypeSymbol, memberName)) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1015_MemberDataMustReferenceExistingMember, attribute.GetLocation(), memberName, SymbolDisplay.ToDisplayString(declaredMemberTypeSymbol) ) ); } static void ReportNonPublicAccessibility( SyntaxNodeAnalysisContext context, AttributeSyntax attribute, ImmutableDictionary memberProperties) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1016_MemberDataMustReferencePublicMember, attribute.GetLocation(), memberProperties ) ); static void ReportNonPublicPropertyGetter( SyntaxNodeAnalysisContext context, AttributeSyntax attribute) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1020_MemberDataPropertyMustHaveGetter, attribute.GetLocation() ) ); static void ReportMemberReturnsTypeUnsafeValue( SyntaxNodeAnalysisContext context, AttributeSyntax attribute, string suggestedAlternative) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1042_MemberDataTheoryDataIsRecommendedForStronglyTypedAnalysis, attribute.GetLocation(), suggestedAlternative ) ); static void ReportNonStatic( SyntaxNodeAnalysisContext context, AttributeSyntax attribute, ImmutableDictionary memberProperties) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1017_MemberDataMustReferenceStaticMember, attribute.GetLocation(), memberProperties ) ); static void ReportTooManyArgumentsProvided( SyntaxNodeAnalysisContext context, ExpressionSyntax syntax, object? value, ImmutableDictionary.Builder builder) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1036_MemberDataArgumentsMustMatchMethodParameters_ExtraValue, syntax.GetLocation(), builder.ToImmutable(), value?.ToString() ?? "null" ) ); static void ReportUseNameof( SyntaxNodeAnalysisContext context, AttributeArgumentSyntax memberNameArgument, string memberName, INamedTypeSymbol testClassTypeSymbol, ISymbol memberSymbol) { var builder = ImmutableDictionary.CreateBuilder(); if (!SymbolEqualityComparer.Default.Equals(memberSymbol.ContainingType, testClassTypeSymbol)) builder.Add("DeclaringType", memberSymbol.ContainingType.ToDisplayString()); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1014_MemberDataShouldUseNameOfOperator, memberNameArgument.Expression.GetLocation(), builder.ToImmutable(), memberName, memberSymbol.ContainingType.ToDisplayString() ) ); } /// /// Resolve declared base type and find the first base class that implements IEnumerable /// (e.g. "ValidExample" from "class SubtypeValidExample : ValidExample {}") /// static ITypeSymbol? ResolveDeclaredBaseTypeFromSyntax( ITypeSymbol type, Compilation compilation) { if (type is null) return null; // Look through source declarations for an explicit base type in the syntax. foreach (var declaringReference in type.DeclaringSyntaxReferences) { var syntax = declaringReference.GetSyntax(); if (syntax is ClassDeclarationSyntax cls && cls.BaseList is not null && cls.BaseList.Types.Count > 0) { var baseTypeSyntax = cls.BaseList.Types.First().Type; var baseTypeName = baseTypeSyntax.ToString(); // may be qualified // Try direct metadata lookup first (works for namespace-qualified names) var byMetadata = compilation.GetTypeByMetadataName(baseTypeName); if (byMetadata is not null) return byMetadata; // Fallback: search symbols by the simple name and try to match fully-qualified textual form. var simpleName = baseTypeName.Split('.').Last(); var candidates = compilation .GetSymbolsWithName(n => n == simpleName, SymbolFilter.Type) .OfType() .ToList(); // Prefer exact textual match of declared name (handles namespace-qualified baseTypeSyntax) var exact = candidates.FirstOrDefault(c => string.Equals(c.ToDisplayString(), baseTypeName, StringComparison.Ordinal)); if (exact is not null) return exact; // Otherwise return the first candidate in the current compilation with the simple name return candidates.FirstOrDefault(); } } return null; } static void VerifyDataMethodParameterUsage( SemanticModel semanticModel, SyntaxNodeAnalysisContext context, Compilation compilation, XunitContext xunitContext, ISymbol memberSymbol, string memberName, List extraArguments) { var argumentSyntaxList = GetParameterExpressionsFromArrayArgument(extraArguments, semanticModel); if (argumentSyntaxList is null) return; var dataMethodSymbol = (IMethodSymbol)memberSymbol; var dataMethodParameterSymbols = dataMethodSymbol.Parameters; int valueIdx = 0, paramIdx = 0; for (; valueIdx < argumentSyntaxList.Count && paramIdx < dataMethodParameterSymbols.Length; valueIdx++) { var parameter = dataMethodParameterSymbols[paramIdx]; var value = semanticModel.GetConstantValue(argumentSyntaxList[valueIdx], context.CancellationToken); // If the parameter type is object, everything is compatible, though we still need to check for nullability if (SymbolEqualityComparer.Default.Equals(parameter.Type, compilation.ObjectType) && (!value.HasValue || parameter.Type.NullableAnnotation != NullableAnnotation.NotAnnotated)) { paramIdx++; continue; } // If this is a params array (and we're using a version of xUnit.net that supports params arrays), // get the element type so we can compare it appropriately. var paramsElementType = xunitContext.Core.TheorySupportsParameterArrays && parameter.IsParams && parameter.Type is IArrayTypeSymbol arrayParam ? arrayParam.ElementType : null; // For params array of object, just consume everything that's left if (paramsElementType is not null && SymbolEqualityComparer.Default.Equals(paramsElementType, compilation.ObjectType) && paramsElementType.NullableAnnotation != NullableAnnotation.NotAnnotated) { valueIdx = extraArguments.Count; break; } if (value.HasValue && value.Value is null) { var isValueTypeParam = paramsElementType is not null ? paramsElementType.IsValueType && paramsElementType.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T : parameter.Type.IsValueType && parameter.Type.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T; var isNonNullableReferenceTypeParam = paramsElementType is not null ? paramsElementType.IsReferenceType && paramsElementType.NullableAnnotation == NullableAnnotation.NotAnnotated : parameter.Type.IsReferenceType && parameter.Type.NullableAnnotation == NullableAnnotation.NotAnnotated; if (isValueTypeParam || isNonNullableReferenceTypeParam) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ParameterIndex] = paramIdx.ToString(CultureInfo.InvariantCulture); builder[Constants.Properties.ParameterName] = parameter.Name; builder[Constants.Properties.MemberName] = memberName; ReportMemberMethodParameterNullability(context, argumentSyntaxList[valueIdx], parameter, paramsElementType, builder); } } else { var valueType = semanticModel.GetTypeInfo(argumentSyntaxList[valueIdx], context.CancellationToken).Type; if (valueType is null) continue; var isCompatible = ConversionChecker.IsConvertible(compilation, valueType, parameter.Type, xunitContext); if (!isCompatible && paramsElementType is not null) isCompatible = ConversionChecker.IsConvertible(compilation, valueType, paramsElementType, xunitContext); if (!isCompatible) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ParameterIndex] = paramIdx.ToString(CultureInfo.InvariantCulture); builder[Constants.Properties.ParameterName] = parameter.Name; builder[Constants.Properties.MemberName] = memberName; ReportMemberMethodParametersDoNotMatchArgumentTypes(context, argumentSyntaxList[valueIdx], parameter, paramsElementType, builder); } } if (!parameter.IsParams) { // Stop moving paramIdx forward if the argument is a parameter array, regardless of xunit's support for it paramIdx++; } } for (; valueIdx < argumentSyntaxList.Count; valueIdx++) { var value = semanticModel.GetConstantValue(argumentSyntaxList[valueIdx], context.CancellationToken); var valueTypeName = value.Value?.GetType().FullName; var valueType = compilation.GetTypeByMetadataName(valueTypeName ?? "System.Object"); var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ParameterIndex] = valueIdx.ToString(CultureInfo.InvariantCulture); builder[Constants.Properties.ParameterSpecialType] = valueType?.SpecialType.ToString() ?? string.Empty; builder[Constants.Properties.MemberName] = memberName; ReportTooManyArgumentsProvided(context, argumentSyntaxList[valueIdx], value.Value, builder); } } static bool VerifyDataSourceReturnType( SyntaxNodeAnalysisContext context, Compilation compilation, XunitContext xunitContext, ITypeSymbol memberType, ImmutableDictionary memberProperties, AttributeSyntax attributeSyntax, INamedTypeSymbol? iEnumerableOfTheoryDataRowType, INamedTypeSymbol? iAsyncEnumerableOfTheoryDataRowType, INamedTypeSymbol? iEnumerableOfTupleType, INamedTypeSymbol? iAsyncEnumerableOfTupleType) { var v3 = xunitContext.HasV3References; var iEnumerableOfObjectArrayType = TypeSymbolFactory.IEnumerableOfObjectArray(compilation); var iAsyncEnumerableOfObjectArrayType = TypeSymbolFactory.IAsyncEnumerableOfObjectArray(compilation); var valid = iEnumerableOfObjectArrayType.IsAssignableFrom(memberType); // Special-case handling for IEnumerable where T is not object[]. If T is any array type, it is assignable to object[] and therefore valid. var memberEnumerableType = memberType.GetEnumerableType(); if (!valid && memberEnumerableType is not null) valid = memberEnumerableType.TypeKind == TypeKind.Array; if (!valid && v3 && iAsyncEnumerableOfObjectArrayType is not null) valid = iAsyncEnumerableOfObjectArrayType.IsAssignableFrom(memberType); if (!valid && v3 && iEnumerableOfTheoryDataRowType is not null) valid = iEnumerableOfTheoryDataRowType.IsAssignableFrom(memberType); if (!valid && v3 && iAsyncEnumerableOfTheoryDataRowType is not null) valid = iAsyncEnumerableOfTheoryDataRowType.IsAssignableFrom(memberType); if (!valid && v3 && iEnumerableOfTupleType is not null) valid = iEnumerableOfTupleType.IsAssignableFrom(memberType); if (!valid && v3 && iAsyncEnumerableOfTupleType is not null) valid = iAsyncEnumerableOfTupleType.IsAssignableFrom(memberType); // If still invalid and the member is a class, check whether it implements IEnumerable and verify that T is an array type. // This ensures the data can be safely converted to IEnumerable. if (!valid && memberType.TypeKind == TypeKind.Class) { var resolvedIEnumerableTypeArgument = FindTypeArgumentFromIEnumerable(memberType, compilation); if (resolvedIEnumerableTypeArgument is not null) valid = (resolvedIEnumerableTypeArgument.TypeKind == TypeKind.Array); } if (!valid) ReportIncorrectReturnType( context, iEnumerableOfObjectArrayType, iAsyncEnumerableOfObjectArrayType, iEnumerableOfTheoryDataRowType, iAsyncEnumerableOfTheoryDataRowType, iEnumerableOfTupleType, iAsyncEnumerableOfTupleType, attributeSyntax, memberProperties, memberType ); return valid; } static void VerifyGenericArgumentTypes( SemanticModel semanticModel, SyntaxNodeAnalysisContext context, MethodDeclarationSyntax testMethod, INamedTypeSymbol theoryDataType, INamedTypeSymbol theoryReturnType, string memberName, ITypeSymbol memberType, AttributeSyntax attributeSyntax) { if (memberType is not INamedTypeSymbol namedMemberType) return; var returnTypeArguments = theoryReturnType.TypeArguments; var testMethodSymbol = semanticModel.GetDeclaredSymbol(testMethod, context.CancellationToken); if (testMethodSymbol is null) return; var testMethodParameterSymbols = testMethodSymbol.Parameters; var testMethodParameterSyntaxes = testMethod.ParameterList.Parameters; if (testMethodParameterSymbols.Length > returnTypeArguments.Length && testMethodParameterSymbols.Skip(returnTypeArguments.Length).Any(p => !p.IsOptional && !p.IsParams)) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MemberName] = memberName; ReportMemberMethodTheoryDataTooFewTypeArguments(context, attributeSyntax.GetLocation(), builder, theoryDataType); return; } int typeArgumentIdx = 0, parameterTypeIdx = 0; for (; typeArgumentIdx < returnTypeArguments.Length && parameterTypeIdx < testMethodParameterSymbols.Length; typeArgumentIdx++) { var parameterSyntax = testMethodParameterSyntaxes[parameterTypeIdx]; if (parameterSyntax.Type is null) continue; var parameter = testMethodParameterSymbols[parameterTypeIdx]; if (parameter.Type is null) continue; var parameterType = parameter.IsParams && parameter.Type is IArrayTypeSymbol paramsArraySymbol ? paramsArraySymbol.ElementType : parameter.Type; var typeArgument = returnTypeArguments[typeArgumentIdx]; if (typeArgument is null) continue; // We want to map T[] and IEnumerable in generic methods so long as the parameter type is itself an array. // This is a simplistic view, since at runtime multiple things competing for the same T may end up being // incompatible, but we'll leave that as an edge case that can be found at runtime, so we're not forced // to run the whole "generic resolver" in the context of an analyzer. var enumerable = parameterType.UnwrapEnumerable(semanticModel.Compilation, unwrapArray: true); if (enumerable is not null && enumerable.Kind == SymbolKind.TypeParameter && typeArgument is IArrayTypeSymbol) continue; if (parameterType.Kind != SymbolKind.TypeParameter && !parameterType.IsAssignableFrom(typeArgument)) { var report = true; // The user might be providing the full array for 'params'; if they do, we need to move // the parameter type index forward because it's been consumed by the array if (parameter.IsParams && parameter.Type.IsAssignableFrom(typeArgument)) { report = false; parameterTypeIdx++; } if (report) ReportMemberMethodTheoryDataIncompatibleType(context, parameterSyntax.Type.GetLocation(), typeArgument, namedMemberType, memberName, parameter); } // Nullability of value types is handled by the type compatibility test, // but nullability of reference types isn't if (parameterType.IsReferenceType && typeArgument.IsReferenceType && parameterType.NullableAnnotation == NullableAnnotation.NotAnnotated && typeArgument.NullableAnnotation == NullableAnnotation.Annotated) ReportMemberMethodTheoryDataNullability(context, parameterSyntax.Type.GetLocation(), typeArgument, namedMemberType, memberName, parameter); // Only move the parameter type index forward when the current parameter is not a 'params' if (!parameter.IsParams) parameterTypeIdx++; } if (typeArgumentIdx < returnTypeArguments.Length) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MemberName] = memberName; ReportMemberMethodTheoryDataExtraTypeArguments(context, attributeSyntax.GetLocation(), builder, theoryDataType); } } } ================================================ FILE: src/xunit.analyzers/X1000/PublicMethodShouldBeMarkedAsTest.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class PublicMethodShouldBeMarkedAsTest : XunitDiagnosticAnalyzer { public PublicMethodShouldBeMarkedAsTest() : base(Descriptors.X1013_PublicMethodShouldBeMarkedAsTest) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var taskType = TypeSymbolFactory.Task(context.Compilation); var configuredTaskAwaitableType = TypeSymbolFactory.ConfiguredTaskAwaitable(context.Compilation); var interfacesToIgnore = new List { TypeSymbolFactory.IDisposable(context.Compilation), TypeSymbolFactory.IAsyncLifetime(context.Compilation), }; context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null) return; if (context.Symbol is not INamedTypeSymbol type) return; var attributeUsageType = TypeSymbolFactory.AttributeUsageAttribute(context.Compilation); if (type.TypeKind != TypeKind.Class || type.DeclaredAccessibility != Accessibility.Public || type.IsAbstract) return; var methodsToIgnore = interfacesToIgnore .WhereNotNull() .Where(i => type.AllInterfaces.Contains(i)) .SelectMany(i => i.GetMembers()) .Select(m => type.FindImplementationForInterfaceMember(m)) .Where(s => s is not null) .ToList(); var hasTestMethods = false; var violations = new List(); foreach (var member in type.GetMembers().Where(m => m.Kind == SymbolKind.Method)) { context.CancellationToken.ThrowIfCancellationRequested(); // Check for method.IsAbstract and earlier for type.IsAbstract is done // twice to enable better diagnostics during code editing. It is useful with // incomplete code for abstract types - missing abstract keyword on type // or on abstract method if (member is not IMethodSymbol method) continue; if (method.MethodKind != MethodKind.Ordinary || method.IsAbstract) continue; var attributes = method.GetAttributesWithInheritance(attributeUsageType); var isTestMethod = attributes.ContainsAttributeType(xunitContext.Core.FactAttributeType); hasTestMethods = hasTestMethods || isTestMethod; if (isTestMethod || attributes.Any(attribute => attribute.AttributeClass is not null && attribute.AttributeClass.GetAttributes().Any(att => att.AttributeClass?.Name.EndsWith("IgnoreXunitAnalyzersRule1013Attribute", StringComparison.InvariantCulture) == true))) { continue; } if (method.DeclaredAccessibility == Accessibility.Public && (method.ReturnsVoid || (taskType is not null && SymbolEqualityComparer.Default.Equals(method.ReturnType, taskType)) || (configuredTaskAwaitableType is not null && SymbolEqualityComparer.Default.Equals(method.ReturnType, configuredTaskAwaitableType)))) { var shouldIgnore = false; while (!shouldIgnore || method.IsOverride) { if (methodsToIgnore.Any(m => SymbolEqualityComparer.Default.Equals(method, m)) || !method.ReceiverType.IsTestClass(xunitContext, strict: true)) shouldIgnore = true; if (!method.IsOverride) break; if (method.OverriddenMethod is null) { shouldIgnore = true; break; } method = method.OverriddenMethod; } if (method is not null && !shouldIgnore) violations.Add(method); } } if (hasTestMethods) foreach (var method in violations) { var testType = method.Parameters.Any() ? Constants.Attributes.Theory : Constants.Attributes.Fact; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1013_PublicMethodShouldBeMarkedAsTest, method.Locations.First(), method.Name, method.ContainingType.Name, testType ) ); } }, SymbolKind.NamedType); } } ================================================ FILE: src/xunit.analyzers/X1000/TestClassCannotBeNestedInGenericClass.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestClassCannotBeNestedInGenericClass : XunitDiagnosticAnalyzer { public TestClassCannotBeNestedInGenericClass() : base(Descriptors.X1032_TestClassCannotBeNestedInGenericClass) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null) return; if (context.Symbol is not INamedTypeSymbol classSymbol) return; if (classSymbol.ContainingType is null) return; if (!classSymbol.ContainingType.IsGenericType) return; var doesClassContainTests = DoesInheritenceTreeContainTests(classSymbol, xunitContext, depth: 3); if (!doesClassContainTests) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1032_TestClassCannotBeNestedInGenericClass, classSymbol.Locations.First() ) ); }, SymbolKind.NamedType); } static bool DoesInheritenceTreeContainTests( INamedTypeSymbol classSymbol, XunitContext xunitContext, int depth) { var doesClassContainTests = classSymbol .GetMembers() .OfType() .Any(m => m.GetAttributes().Any(a => xunitContext.Core.FactAttributeType.IsAssignableFrom(a.AttributeClass))); if (!doesClassContainTests && classSymbol.BaseType is not null && depth > 0) return DoesInheritenceTreeContainTests(classSymbol.BaseType, xunitContext, depth - 1); return doesClassContainTests; } } ================================================ FILE: src/xunit.analyzers/X1000/TestClassMustBePublic.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestClassMustBePublic : XunitDiagnosticAnalyzer { public TestClassMustBePublic() : base(Descriptors.X1000_TestClassMustBePublic) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null) return; if (context.Symbol.DeclaredAccessibility == Accessibility.Public) return; if (context.Symbol is not INamedTypeSymbol classSymbol) return; var doesClassContainTests = classSymbol .GetMembers() .OfType() .Any(m => m.GetAttributes().Any(a => xunitContext.Core.FactAttributeType.IsAssignableFrom(a.AttributeClass))); if (!doesClassContainTests) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1000_TestClassMustBePublic, classSymbol.Locations.First(), classSymbol.Locations.Skip(1), classSymbol.Name ) ); }, SymbolKind.NamedType); } } ================================================ FILE: src/xunit.analyzers/X1000/TestClassShouldHaveTFixtureArgument.cs ================================================ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestClassShouldHaveTFixtureArgument : XunitDiagnosticAnalyzer { public TestClassShouldHaveTFixtureArgument() : base(Descriptors.X1033_TestClassShouldHaveTFixtureArgument) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null || xunitContext.Core.IClassFixtureType is null || xunitContext.Core.ICollectionFixtureType is null) return; if (context.Symbol.DeclaredAccessibility != Accessibility.Public) return; if (context.Symbol is not INamedTypeSymbol classSymbol) return; var doesClassContainTests = classSymbol .GetMembers() .OfType() .Any(m => m.GetAttributes().Any(a => xunitContext.Core.FactAttributeType.IsAssignableFrom(a.AttributeClass))); if (!doesClassContainTests) return; foreach (var interfaceOnTestClass in classSymbol.AllInterfaces) { var isFixtureInterface = interfaceOnTestClass.OriginalDefinition.IsAssignableFrom(xunitContext.Core.IClassFixtureType) || interfaceOnTestClass.OriginalDefinition.IsAssignableFrom(xunitContext.Core.ICollectionFixtureType); if (isFixtureInterface && interfaceOnTestClass.TypeArguments[0] is INamedTypeSymbol tFixtureDataType) { var hasConstructorWithTFixtureArg = classSymbol .Constructors .Any(x => x.Parameters.Length > 0 && x.Parameters.Any(p => SymbolEqualityComparer.Default.Equals(p.Type, tFixtureDataType))); if (hasConstructorWithTFixtureArg) continue; var propertiesBuilder = ImmutableDictionary.CreateBuilder(); propertiesBuilder.Add(Constants.Properties.TFixtureDisplayName, tFixtureDataType.ToDisplayString()); propertiesBuilder.Add(Constants.Properties.TFixtureName, tFixtureDataType.Name); propertiesBuilder.Add(Constants.Properties.TestClassName, classSymbol.Name); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1033_TestClassShouldHaveTFixtureArgument, location: classSymbol.Locations.First(), properties: propertiesBuilder.ToImmutable(), classSymbol.ToDisplayString(), tFixtureDataType.ToDisplayString() ) ); } } }, SymbolKind.NamedType); } } ================================================ FILE: src/xunit.analyzers/X1000/TestMethodCannotHaveOverloads.cs ================================================ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestMethodCannotHaveOverloads : XunitDiagnosticAnalyzer { public TestMethodCannotHaveOverloads() : base(Descriptors.X1024_TestMethodCannotHaveOverloads) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null) return; if (context.Symbol is not INamedTypeSymbol typeSymbol) return; if (typeSymbol.TypeKind != TypeKind.Class) return; var methodsByName = typeSymbol .GetInheritedAndOwnMembers() .Where(s => s.Kind == SymbolKind.Method) .Cast() .Where(m => m.MethodKind == MethodKind.Ordinary) .GroupBy(m => m.Name); foreach (var grouping in methodsByName) { context.CancellationToken.ThrowIfCancellationRequested(); var methods = grouping.ToList(); var methodName = grouping.Key; if (methods.Count == 1 || !methods.Any(m => m.GetAttributes().ContainsAttributeType(xunitContext.Core.FactAttributeType))) continue; var methodsWithoutOverloads = new List(methods.Count); foreach (var method in methods) if (!methods.Any(m => m.IsOverride && SymbolEqualityComparer.Default.Equals(m.OverriddenMethod, method))) methodsWithoutOverloads.Add(method); if (methodsWithoutOverloads.Count == 1) continue; foreach (var method in methodsWithoutOverloads.Where(m => SymbolEqualityComparer.Default.Equals(m.ContainingType, typeSymbol))) { var otherType = methodsWithoutOverloads .Where(m => !SymbolEqualityComparer.Default.Equals(m, method)) .OrderBy(m => m.ContainingType, TypeHierarchyComparer.Instance) .First() .ContainingType; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1024_TestMethodCannotHaveOverloads, method.Locations.First(), methodName, method.ContainingType.ToDisplayString(), otherType.ToDisplayString() ) ); } } }, SymbolKind.NamedType); } } ================================================ FILE: src/xunit.analyzers/X1000/TestMethodMustNotHaveMultipleFactAttributes.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestMethodMustNotHaveMultipleFactAttributes : XunitDiagnosticAnalyzer { public TestMethodMustNotHaveMultipleFactAttributes() : base(Descriptors.X1002_TestMethodMustNotHaveMultipleFactAttributes) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.FactAttributeType is null) return; if (context.Symbol is not IMethodSymbol symbol) return; var attributeTypes = new HashSet(SymbolEqualityComparer.Default); var count = 0; foreach (var attribute in symbol.GetAttributes()) { var attributeType = attribute.AttributeClass; if (attributeType is not null && xunitContext.Core.FactAttributeType.IsAssignableFrom(attributeType)) { attributeTypes.Add(attributeType); count++; } } if (count > 1) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1002_TestMethodMustNotHaveMultipleFactAttributes, symbol.Locations.First(), properties: attributeTypes.ToImmutableDictionary(t => t.ToDisplayString(), t => (string?)string.Empty) ) ); }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/TestMethodShouldNotBeSkipped.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestMethodShouldNotBeSkipped : XunitDiagnosticAnalyzer { public TestMethodShouldNotBeSkipped() : base(Descriptors.X1004_TestMethodShouldNotBeSkipped) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSyntaxNodeAction(context => { if (xunitContext.Core.FactAttributeType is null) return; if (context.Node is not AttributeSyntax attribute) return; if (attribute.ArgumentList is null) return; var skipArgument = default(AttributeArgumentSyntax); foreach (var argument in attribute.ArgumentList.Arguments) { var valueText = argument.NameEquals?.Name?.Identifier.ValueText; if (valueText == "SkipWhen" || valueText == "SkipUnless") return; if (valueText == "Skip") skipArgument = argument; } if (skipArgument is null) return; var attributeType = context.SemanticModel.GetTypeInfo(attribute).Type; if (!xunitContext.Core.FactAttributeType.IsAssignableFrom(attributeType)) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1004_TestMethodShouldNotBeSkipped, skipArgument.GetLocation() ) ); }, SyntaxKind.Attribute); } } ================================================ FILE: src/xunit.analyzers/X1000/TestMethodSupportedReturnType.cs ================================================ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestMethodSupportedReturnType : XunitDiagnosticAnalyzer { public TestMethodSupportedReturnType() : base(Descriptors.X1028_TestMethodHasInvalidReturnType) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); if (xunitContext.Core.FactAttributeType is null || xunitContext.Core.TheoryAttributeType is null) return; context.RegisterSymbolAction(context => { if (context.Symbol is not IMethodSymbol method) return; if (!method.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(xunitContext.Core.FactAttributeType, a.AttributeClass) || SymbolEqualityComparer.Default.Equals(xunitContext.Core.TheoryAttributeType, a.AttributeClass))) return; var validReturnTypes = GetValidReturnTypes(context.Compilation, xunitContext); if (validReturnTypes.Any(t => SymbolEqualityComparer.Default.Equals(method.ReturnType, t))) return; var validReturnTypeDisplayNames = validReturnTypes.Select( t => SymbolDisplay.ToDisplayString( t, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ) ).ToArray(); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1028_TestMethodHasInvalidReturnType, method.Locations.FirstOrDefault(), [string.Join(", ", validReturnTypeDisplayNames)] ) ); }, SymbolKind.Method); } public static List GetValidReturnTypes( Compilation compilation, XunitContext xunitContext) { Guard.ArgumentNotNull(compilation); Guard.ArgumentNotNull(xunitContext); var result = new List(); void Add(INamedTypeSymbol? symbol) { if (symbol is not null) result!.Add(symbol); } Add(TypeSymbolFactory.Void(compilation)); Add(TypeSymbolFactory.Task(compilation)); if (xunitContext.HasV3References) Add(TypeSymbolFactory.ValueTask(compilation)); return result; } } ================================================ FILE: src/xunit.analyzers/X1000/TheoryDataRowArgumentsShouldBeSerializable.cs ================================================ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TheoryDataRowArgumentsShouldBeSerializable : XunitDiagnosticAnalyzer { public TheoryDataRowArgumentsShouldBeSerializable() : base( Descriptors.X1046_AvoidUsingTheoryDataRowArgumentsThatAreNotSerializable, Descriptors.X1047_AvoidUsingTheoryDataRowArgumentsThatMightNotBeSerializable ) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var theoryDataRowTypes = TypeSymbolFactory.TheoryDataRow_ByGenericArgumentCount_V3(context.Compilation); if (theoryDataRowTypes.Count == 0) return; if (SerializableTypeSymbols.Create(context.Compilation, xunitContext) is not SerializableTypeSymbols typeSymbols) return; var analyzer = new SerializabilityAnalyzer(typeSymbols); context.RegisterOperationAction(context => { if (context.Operation is not IObjectCreationOperation objectCreation) return; var creationType = objectCreation.Type as INamedTypeSymbol; if (creationType is not null && creationType.IsGenericType) creationType = creationType.OriginalDefinition; if (!theoryDataRowTypes.Values.Contains(creationType, SymbolEqualityComparer.Default)) return; var argumentOperations = GetConstructorArguments(objectCreation); if (argumentOperations is null) return; foreach (var argumentOperation in argumentOperations) { if (analyzer.TypeShouldBeIgnored(argumentOperation.Type)) continue; var serializability = analyzer.AnalayzeSerializability(argumentOperation.Type, xunitContext); if (serializability != Serializability.AlwaysSerializable) { var typeDisplayName = argumentOperation.SemanticModel is null ? argumentOperation.Type.Name : argumentOperation.Type.ToMinimalDisplayString(argumentOperation.SemanticModel, argumentOperation.Syntax.SpanStart); context.ReportDiagnostic( Diagnostic.Create( serializability == Serializability.NeverSerializable ? Descriptors.X1046_AvoidUsingTheoryDataRowArgumentsThatAreNotSerializable : Descriptors.X1047_AvoidUsingTheoryDataRowArgumentsThatMightNotBeSerializable, argumentOperation.Syntax.GetLocation(), argumentOperation.Syntax.ToFullString(), typeDisplayName ) ); } } }, OperationKind.ObjectCreation); } static IReadOnlyList? GetConstructorArguments(IObjectCreationOperation objectCreation) { // If this is the generic TheoryDataRow, then just return the arguments as-is if (objectCreation.Type is INamedTypeSymbol creationType && creationType.IsGenericType) { var result = new List(); for (var idx = 0; idx < objectCreation.Arguments.Length; ++idx) { #if ROSLYN_LATEST var elementValue = objectCreation.Arguments[idx].ChildOperations.FirstOrDefault(); #else var elementValue = objectCreation.Arguments[idx].Children.FirstOrDefault(); #endif while (elementValue is IConversionOperation conversion) elementValue = conversion.Operand; if (elementValue is not null) result.Add(elementValue); } return result; } // Non-generic TheoryDataRow, which means we should have a single argument // which is the params array of values, which we need to unpack. if (objectCreation.Arguments.FirstOrDefault() is not IArgumentOperation argumentOperation) return null; #if ROSLYN_LATEST var firstArgument = argumentOperation.ChildOperations.FirstOrDefault(); #else var firstArgument = argumentOperation.Children.FirstOrDefault(); #endif if (firstArgument is null) return null; // Common pattern: implicit array creation for the params array if (firstArgument is IArrayCreationOperation arrayCreation && #if ROSLYN_LATEST arrayCreation.ChildOperations.Skip(1).FirstOrDefault() is IArrayInitializerOperation arrayInitializer) #else arrayCreation.Children.Skip(1).FirstOrDefault() is IArrayInitializerOperation arrayInitializer) #endif { var result = new List(); for (var idx = 0; idx < arrayInitializer.ElementValues.Length; ++idx) { var elementValue = arrayInitializer.ElementValues[idx]; while (elementValue is IConversionOperation conversion) elementValue = conversion.Operand; result.Add(elementValue); } return result; } // TODO: Less common pattern: user created the array ahead of time, which shows up as ILocalReferenceOperation return null; } protected override bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).HasV3References; } ================================================ FILE: src/xunit.analyzers/X1000/TheoryDataShouldNotUseTheoryDataRow.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TheoryDataShouldNotUseTheoryDataRow() : XunitV3DiagnosticAnalyzer(Descriptors.X1052_TheoryDataShouldNotUseITheoryDataRow) { public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var iTheoryDataRowSymbol = TypeSymbolFactory.ITheoryDataRow_V3(context.Compilation); if (iTheoryDataRowSymbol is null) return; var theoryDataTypes = TypeSymbolFactory.TheoryData_ByGenericArgumentCount(context.Compilation); context.RegisterSyntaxNodeAction(context => { var genericName = (GenericNameSyntax)context.Node; if (context.SemanticModel.GetSymbolInfo(genericName).Symbol is not INamedTypeSymbol typeSymbol) return; if (!theoryDataTypes.TryGetValue(typeSymbol.TypeArguments.Length, out var expectedSymbol)) return; if (!SymbolEqualityComparer.Default.Equals(expectedSymbol, typeSymbol.OriginalDefinition)) return; foreach (var typeArg in typeSymbol.TypeArguments) if (IsOrImplementsITheoryDataRow(typeArg, iTheoryDataRowSymbol)) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1052_TheoryDataShouldNotUseITheoryDataRow, genericName.GetLocation() ) ); }, SyntaxKind.GenericName); } static bool IsOrImplementsITheoryDataRow( ITypeSymbol typeArg, INamedTypeSymbol iTheoryDataSymbol) { if (SymbolEqualityComparer.Default.Equals(typeArg, iTheoryDataSymbol) || typeArg.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, iTheoryDataSymbol))) return true; if (typeArg is ITypeParameterSymbol typeParameter) foreach (var constraint in typeParameter.ConstraintTypes) if (SymbolEqualityComparer.Default.Equals(constraint, iTheoryDataSymbol) || constraint.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, iTheoryDataSymbol))) return true; return false; } } ================================================ FILE: src/xunit.analyzers/X1000/TheoryDataTypeArgumentsShouldBeSerializable.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TheoryDataTypeArgumentsShouldBeSerializable : XunitDiagnosticAnalyzer { public TheoryDataTypeArgumentsShouldBeSerializable() : base( Descriptors.X1044_AvoidUsingTheoryDataTypeArgumentsThatAreNotSerializable, Descriptors.X1045_AvoidUsingTheoryDataTypeArgumentsThatMightNotBeSerializable ) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); if (SerializableTypeSymbols.Create(context.Compilation, xunitContext) is not SerializableTypeSymbols typeSymbols) return; var finder = new TheoryDataTypeArgumentFinder(typeSymbols); var analyzer = new SerializabilityAnalyzer(typeSymbols); context.RegisterSyntaxNodeAction(context => { var semanticModel = context.SemanticModel; var cancellationToken = context.CancellationToken; if (context.Node is not MethodDeclarationSyntax methodSyntax) return; if (semanticModel.GetDeclaredSymbol(methodSyntax, cancellationToken) is not IMethodSymbol method) return; if (method.ContainingType is not INamedTypeSymbol testClass) return; if (!method.GetAttributes().ContainsAttributeType(typeSymbols.TheoryAttribute)) return; if (DiscoveryEnumerationIsDisabled(method, typeSymbols)) return; var dataAttributes = methodSyntax .AttributeLists .SelectMany(list => list.Attributes) .Zip(method.GetAttributes(), (attributeSyntax, attribute) => (attributeSyntax, attribute)) .Where((tuple) => tuple.attribute.IsInstanceOf(typeSymbols.DataAttribute)); foreach ((var dataAttributeSyntax, var dataAttribute) in dataAttributes) { cancellationToken.ThrowIfCancellationRequested(); var types = finder.FindTypeArguments(dataAttribute, testClass); foreach (var type in types) { if (analyzer.TypeShouldBeIgnored(type)) continue; var serializability = analyzer.AnalayzeSerializability(type, xunitContext); if (serializability != Serializability.AlwaysSerializable) context.ReportDiagnostic( Diagnostic.Create( serializability == Serializability.NeverSerializable ? Descriptors.X1044_AvoidUsingTheoryDataTypeArgumentsThatAreNotSerializable : Descriptors.X1045_AvoidUsingTheoryDataTypeArgumentsThatMightNotBeSerializable, dataAttributeSyntax.GetLocation(), type.ToMinimalDisplayString(semanticModel, dataAttributeSyntax.SpanStart) ) ); } } }, SyntaxKind.MethodDeclaration); } static bool AttributeIsTheoryOrDataAttribute(AttributeData attribute, SerializableTypeSymbols typeSymbols) => attribute.IsInstanceOf(typeSymbols.TheoryAttribute, exactMatch: true) || attribute.IsInstanceOf(typeSymbols.DataAttribute); static bool DiscoveryEnumerationIsDisabled(IMethodSymbol method, SerializableTypeSymbols typeSymbols) => method .GetAttributes() .Where(attribute => AttributeIsTheoryOrDataAttribute(attribute, typeSymbols)) .SelectMany(attribute => attribute.NamedArguments) .Any(argument => argument.Key == "DisableDiscoveryEnumeration" && argument.Value.Value is true); sealed class TheoryDataTypeArgumentFinder(SerializableTypeSymbols typeSymbols) { const string MemberType = nameof(MemberType); /// /// Find all TheoryData type arguments for a data source referenced by the given data attribute /// in the given test class, if applicable. If the data source's type is not compatible with /// a generic TheoryData class, such as if it is a member with the type /// of [], then no type arguments will be found. /// public IEnumerable FindTypeArguments( AttributeData dataAttribute, INamedTypeSymbol testClass) => GetDataSourceType(dataAttribute, testClass) is INamedTypeSymbol type ? GetTheoryDataTypeArguments(type) : []; static IMethodSymbol? GetCompatibleMethod( ITypeSymbol type, string name, ImmutableArray arguments) { foreach (var currentType in GetTypesForMemberResolution(type, includeInterfaces: true)) { var methods = currentType .GetMembers(name) .Where(member => member.DeclaredAccessibility == Accessibility.Public) .Select(member => member as IMethodSymbol) .WhereNotNull() .Where(method => ParameterTypesAreCompatible(method.Parameters, arguments)) .ToArray(); if (methods.Length == 0) continue; if (methods.Length == 1) return methods[0]; return methods.Where(method => method.Parameters.Length == arguments.Length).FirstOrDefault(); } return null; } INamedTypeSymbol? GetDataSourceType( AttributeData dataAttribute, INamedTypeSymbol testClass) { if (dataAttribute.IsInstanceOf(typeSymbols.ClassDataAttribute, exactMatch: true)) return dataAttribute.ConstructorArguments.FirstOrDefault().Value as INamedTypeSymbol; if (dataAttribute.IsInstanceOf(typeSymbols.MemberDataAttribute, exactMatch: true)) return GetMemberType(dataAttribute, testClass) as INamedTypeSymbol; return null; } /// /// The logic in this method corresponds to the logic in MemberDataAttributeBase.GetFieldAccessor. /// static IFieldSymbol? GetField( ITypeSymbol type, string name) { var field = GetPublicMember(type, name, includeInterfaces: false); if (field is not null && field.IsStatic) return field; return null; } static ITypeSymbol? GetMemberContainingType(AttributeData memberDataAttribute) => memberDataAttribute .NamedArguments .Where(namedArgument => namedArgument.Key == MemberType) .Select(namedArgument => namedArgument.Value.Type) .WhereNotNull() .FirstOrDefault(); /// /// The logic in this method corresponds to the logic in MemberDataAttributeBase.GetData. /// static ITypeSymbol? GetMemberType( AttributeData memberDataAttribute, INamedTypeSymbol testClass) { var name = memberDataAttribute.ConstructorArguments.FirstOrDefault(); if (name.Value is string memberName) { var containingType = GetMemberContainingType(memberDataAttribute) ?? testClass; var member = GetProperty(containingType, memberName) ?? GetField(containingType, memberName) ?? GetMethod(containingType, memberName, memberDataAttribute) as ISymbol; return GetMemberType(member); } return null; } static ITypeSymbol? GetMemberType(ISymbol? member) => member switch { IPropertySymbol property => property.Type, IFieldSymbol field => field.Type, IMethodSymbol method when !method.ReturnsVoid => method.ReturnType, _ => null, }; /// /// The logic in this method corresponds to the logic in MemberDataAttributeBase.GetMethodAccessor. /// static IMethodSymbol? GetMethod( ITypeSymbol type, string name, AttributeData memberDataAttribute) { var arguments = memberDataAttribute.ConstructorArguments.Length > 1 ? memberDataAttribute.ConstructorArguments[1].Values : ImmutableArray.Empty; var method = GetCompatibleMethod(type, name, arguments); if (method is not null && method.IsStatic) return method; return null; } /// /// The logic in this method corresponds to the logic in MemberDataAttributeBase.GetPropertyAccessor. /// static IPropertySymbol? GetProperty( ITypeSymbol type, string name) { var property = GetPublicMember(type, name, includeInterfaces: true); if (property is not null && property.GetMethod is not null && property.GetMethod.IsStatic) return property; return null; } static TSymbol? GetPublicMember( ITypeSymbol type, string name, bool includeInterfaces) where TSymbol : class, ISymbol { foreach (var currentType in GetTypesForMemberResolution(type, includeInterfaces)) { var member = currentType .GetMembers(name) .Where(member => member.DeclaredAccessibility == Accessibility.Public) .Select(member => member as TSymbol) .WhereNotNull() .FirstOrDefault(); if (member is not null) return member; } return null; } IEnumerable GetTheoryDataTypeArguments(INamedTypeSymbol type) { if (type.TypeKind != TypeKind.Class || type.SpecialType != SpecialType.None) return []; if (typeSymbols.TheoryDataBaseType is not INamedTypeSymbol theoryDataBaseType) return []; // For v2 and early versions of v3, the base type is "TheoryData" (non-generic). // For later versions of v3, it's "TheoryDataBase". // We need to compare unbound to unbound when the type is generic. if (theoryDataBaseType.IsGenericType) theoryDataBaseType = theoryDataBaseType.ConstructUnboundGenericType(); for (var currentType = type; currentType is not null; currentType = currentType.BaseType) { var baseType = currentType.BaseType; if (baseType?.IsGenericType == true) baseType = baseType.ConstructUnboundGenericType(); if (theoryDataBaseType.Equals(baseType, SymbolEqualityComparer.Default)) { var theoryDataType = typeSymbols.TheoryData(currentType.Arity); if (currentType.ConstructedFrom.Equals(theoryDataType, SymbolEqualityComparer.Default)) return currentType.TypeArguments; } } return []; } /// /// The logic in this method corresponds to the logic in MemberDataAttributeBase.GetTypesForMemberResolution. /// static IEnumerable GetTypesForMemberResolution( ITypeSymbol type, bool includeInterfaces) { for (var currentType = type; currentType is not null; currentType = currentType.BaseType) yield return currentType; if (includeInterfaces) foreach (var @interface in type.AllInterfaces) yield return @interface; } /// /// The logic in this method corresponds to the logic in MemberDataAttributeBase.ParameterTypesCompatible. /// static bool ParameterTypesAreCompatible( ImmutableArray parameters, ImmutableArray arguments) { if (parameters.Length < arguments.Length) return false; var i = 0; for (; i < arguments.Length; i++) if (arguments[i].Type is ITypeSymbol argumentType && !parameters[i].Type.IsAssignableFrom(argumentType)) return false; for (; i < parameters.Length; i++) if (!parameters[i].IsOptional) return false; return true; } } } ================================================ FILE: src/xunit.analyzers/X1000/TheoryMethodCannotHaveDefaultParameter.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TheoryMethodCannotHaveDefaultParameter : XunitDiagnosticAnalyzer { public TheoryMethodCannotHaveDefaultParameter() : base(Descriptors.X1023_TheoryMethodCannotHaveDefaultParameter) { } protected override bool ShouldAnalyze(XunitContext xunitContext) { Guard.ArgumentNotNull(xunitContext); return base.ShouldAnalyze(xunitContext) && !xunitContext.Core.TheorySupportsDefaultParameterValues; } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.TheoryAttributeType is null) return; if (context.Symbol is not IMethodSymbol method) return; var attributes = method.GetAttributes(); if (!attributes.ContainsAttributeType(xunitContext.Core.TheoryAttributeType)) return; foreach (var parameter in method.Parameters) { context.CancellationToken.ThrowIfCancellationRequested(); if (parameter.HasExplicitDefaultValue) { var syntaxNode = parameter .DeclaringSyntaxReferences .First() .GetSyntax(context.CancellationToken) .FirstAncestorOrSelf(); if (syntaxNode is null) continue; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1023_TheoryMethodCannotHaveDefaultParameter, syntaxNode.Default?.GetLocation(), method.Name, method.ContainingType.ToDisplayString(), parameter.Name ) ); } } }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/TheoryMethodCannotHaveParamsArray.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TheoryMethodCannotHaveParamsArray : XunitDiagnosticAnalyzer { public TheoryMethodCannotHaveParamsArray() : base(Descriptors.X1022_TheoryMethodCannotHaveParameterArray) { } protected override bool ShouldAnalyze(XunitContext xunitContext) { Guard.ArgumentNotNull(xunitContext); return base.ShouldAnalyze(xunitContext) && !xunitContext.Core.TheorySupportsParameterArrays; } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.TheoryAttributeType is null) return; if (context.Symbol is not IMethodSymbol method) return; var parameter = method.Parameters.LastOrDefault(); if (!(parameter?.IsParams ?? false)) return; var attributes = method.GetAttributes(); if (attributes.ContainsAttributeType(xunitContext.Core.TheoryAttributeType)) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1022_TheoryMethodCannotHaveParameterArray, parameter.DeclaringSyntaxReferences.First().GetSyntax(context.CancellationToken).GetLocation(), method.Name, method.ContainingType.ToDisplayString(), parameter.Name ) ); }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/TheoryMethodMustHaveTestData.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TheoryMethodMustHaveTestData : XunitDiagnosticAnalyzer { public TheoryMethodMustHaveTestData() : base(Descriptors.X1003_TheoryMethodMustHaveTestData) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.TheoryAttributeType is null || xunitContext.Core.DataAttributeType is null) return; if (context.Symbol is not IMethodSymbol symbol) return; var attributes = symbol.GetAttributes(); if (!attributes.ContainsAttributeType(xunitContext.Core.TheoryAttributeType)) return; var hasData = attributes.ContainsAttributeType(xunitContext.Core.DataAttributeType); if (!hasData && xunitContext.V3Core?.IDataAttributeType is not null) hasData = attributes.ContainsAttributeType(xunitContext.V3Core.IDataAttributeType); if (!hasData) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1003_TheoryMethodMustHaveTestData, symbol.Locations.First() ) ); }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/TheoryMethodShouldHaveParameters.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TheoryMethodShouldHaveParameters : XunitDiagnosticAnalyzer { public TheoryMethodShouldHaveParameters() : base(Descriptors.X1006_TheoryMethodShouldHaveParameters) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (xunitContext.Core.TheoryAttributeType is null) return; if (context.Symbol is not IMethodSymbol symbol) return; if (symbol.Parameters.Length > 0) return; var attributes = symbol.GetAttributes(); if (attributes.ContainsAttributeType(xunitContext.Core.TheoryAttributeType)) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1006_TheoryMethodShouldHaveParameters, symbol.Locations.First() ) ); }, SymbolKind.Method); } } ================================================ FILE: src/xunit.analyzers/X1000/TheoryMethodShouldUseAllParameters.cs ================================================ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TheoryMethodShouldUseAllParameters : XunitDiagnosticAnalyzer { static readonly Regex discardRegex = new(@"^_\d*$"); public TheoryMethodShouldUseAllParameters() : base(Descriptors.X1026_TheoryMethodShouldUseAllParameters) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSyntaxNodeAction(context => { if (xunitContext.Core.TheoryAttributeType is null) return; if (context.Node is not MethodDeclarationSyntax methodSyntax) return; if (methodSyntax.ParameterList.Parameters.Count == 0) return; var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax); if (methodSymbol is null) return; var attributes = methodSymbol.GetAttributes(); if (!attributes.ContainsAttributeType(xunitContext.Core.TheoryAttributeType)) return; AnalyzeTheoryParameters(context, methodSyntax, methodSymbol); }, SyntaxKind.MethodDeclaration); } static void AnalyzeTheoryParameters( SyntaxNodeAnalysisContext context, MethodDeclarationSyntax methodSyntax, IMethodSymbol methodSymbol) { var methodBody = methodSyntax.Body as SyntaxNode ?? methodSyntax.ExpressionBody?.Expression; if (methodBody is null) return; var flowAnalysis = context.SemanticModel.AnalyzeDataFlow(methodBody); if (!flowAnalysis.Succeeded) return; var usedParameters = new HashSet(flowAnalysis.ReadInside.Concat(flowAnalysis.Captured).Distinct(SymbolEqualityComparer.Default), SymbolEqualityComparer.Default); for (var i = 0; i < methodSymbol.Parameters.Length; i++) { var parameterSymbol = methodSymbol.Parameters[i]; if (!usedParameters.Contains(parameterSymbol) && !discardRegex.IsMatch(parameterSymbol.Name)) { var parameterSyntax = methodSyntax.ParameterList.Parameters[i]; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1026_TheoryMethodShouldUseAllParameters, parameterSyntax.Identifier.GetLocation(), methodSymbol.Name, methodSymbol.ContainingType.Name, parameterSymbol.Name ) ); } } } } ================================================ FILE: src/xunit.analyzers/X1000/UseCancellationToken.cs ================================================ using System.Collections.Immutable; using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class UseCancellationToken : XunitDiagnosticAnalyzer { public UseCancellationToken() : base(Descriptors.X1051_UseCancellationToken) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var cancellationTokenType = TypeSymbolFactory.CancellationToken(context.Compilation); if (cancellationTokenType is null) return; var xunitContainerTypes = new[] { TypeSymbolFactory.Assert(context.Compilation), TypeSymbolFactory.Record(context.Compilation), }.WhereNotNull().ToImmutableHashSet(SymbolEqualityComparer.Default); var attributeUsageAttribute = TypeSymbolFactory.AttributeUsageAttribute(context.Compilation); var obsoleteAttribute = TypeSymbolFactory.ObsoleteAttribute(context.Compilation); context.RegisterOperationAction(context => { if (context.Operation is not IInvocationOperation invocationOperation) return; var (foundSymbol, lambdaOwner) = invocationOperation.IsInTestMethod(xunitContext); if (!foundSymbol || lambdaOwner is ILocalFunctionOperation or IAnonymousFunctionOperation) return; // We want to try to catch anything that's a lambda from Assert or Record, but // ignore all other lambdas, because we don't want to catch false positives from // things like mocking libraries. if (lambdaOwner is IInvocationOperation lambdaOwnerInvocation) if (!xunitContainerTypes.Contains(lambdaOwnerInvocation.TargetMethod.ContainingType)) return; var invokedMethod = invocationOperation.TargetMethod; var parameters = invokedMethod.Parameters; IArgumentOperation? argument = null; foreach (var parameter in parameters) if (SymbolEqualityComparer.Default.Equals(parameter.Type, cancellationTokenType)) { argument = invocationOperation.Arguments.FirstOrDefault(arg => SymbolEqualityComparer.Default.Equals(arg.Parameter, parameter)); break; } // The invoked method has the parameter we're looking for if (argument is not null) { // Default parameter value if (argument.ArgumentKind == ArgumentKind.DefaultValue) Report(context, invocationOperation.Syntax.GetLocation(), argument.Parameter!); // Explicit parameter value else if (argument.Syntax is ArgumentSyntax argumentSyntax) { var kind = argumentSyntax.Expression.Kind(); if (kind is SyntaxKind.DefaultExpression or SyntaxKind.DefaultLiteralExpression) Report(context, invocationOperation.Syntax.GetLocation(), argument.Parameter!); } } // Look for an overload with the exact same parameter types + a CancellationToken else { var targetParameterTypes = parameters.Select(p => p.Type).Concat([cancellationTokenType]).ToArray(); foreach (var member in invokedMethod.ContainingType.GetMembers(invokedMethod.Name)) if (member is IMethodSymbol method) { if (attributeUsageAttribute is not null && obsoleteAttribute is not null) { var attributes = method.GetAttributesWithInheritance(attributeUsageAttribute); if (attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, obsoleteAttribute))) continue; } var methodParameterTypes = method.Parameters.Select(p => p.Type).ToArray(); if (methodParameterTypes.Length != targetParameterTypes.Length) continue; var match = true; for (var idx = 0; idx < targetParameterTypes.Length; ++idx) if (!SymbolEqualityComparer.Default.Equals(targetParameterTypes[idx], methodParameterTypes[idx])) { match = false; break; } if (match) { Report(context, invocationOperation.Syntax.GetLocation(), method.Parameters.Last()); return; } } } }, OperationKind.Invocation); static void Report( OperationAnalysisContext context, Location location, IParameterSymbol parameter) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.ParameterName] = parameter.Name; builder[Constants.Properties.ParameterIndex] = parameter.Ordinal.ToString(CultureInfo.InvariantCulture); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X1051_UseCancellationToken, location, builder.ToImmutable() ) ); } } protected override bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).HasV3References; } ================================================ FILE: src/xunit.analyzers/X2000/AssertCollectionContainsShouldNotUseBoolCheck.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertCollectionContainsShouldNotUseBoolCheck : AssertUsageAnalyzerBase { static readonly HashSet linqContainsMethods = [ // Signatures without nullable variants "System.Linq.Enumerable.Contains(System.Collections.Generic.IEnumerable, TSource)", // Non-nullable signatures "System.Linq.Enumerable.Contains(System.Collections.Generic.IEnumerable, TSource, System.Collections.Generic.IEqualityComparer)", // Nullable signatures "System.Linq.Enumerable.Contains(System.Collections.Generic.IEnumerable, TSource, System.Collections.Generic.IEqualityComparer?)", ]; static readonly string[] targetMethods = [ Constants.Asserts.False, Constants.Asserts.True, ]; public AssertCollectionContainsShouldNotUseBoolCheck() : base(Descriptors.X2017_AssertCollectionContainsShouldNotUseBoolCheck, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length is < 1 or > 2) return; if (method.Parameters.Length > 1 && method.Parameters[1].Type.SpecialType == SpecialType.System_String) return; if (arguments.FirstOrDefault(arg => SymbolEqualityComparer.Default.Equals(arg.Parameter, method.Parameters[0]))?.Value is not IInvocationOperation invocationExpression) return; var methodSymbol = invocationExpression.TargetMethod; if (!IsLinqContainsMethod(methodSymbol) && !IsICollectionContainsMethod(context, methodSymbol)) return; var replacement = method.Name == Constants.Asserts.True ? Constants.Asserts.Contains : Constants.Asserts.DoesNotContain; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2017_AssertCollectionContainsShouldNotUseBoolCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } static bool IsLinqContainsMethod(IMethodSymbol methodSymbol) => methodSymbol.OriginalDefinition is not null && linqContainsMethods.Contains(SymbolDisplay.ToDisplayString(methodSymbol.OriginalDefinition)); static bool IsICollectionContainsMethod( OperationAnalysisContext context, IMethodSymbol methodSymbol) { var containingType = methodSymbol.ContainingType; var genericCollectionType = containingType.GetGenericInterfaceImplementation(TypeSymbolFactory.ICollectionOfT(context.Compilation)); if (genericCollectionType is null) return false; var genericCollectionContainsSymbol = genericCollectionType .GetMembers(nameof(ICollection.Contains)) .FirstOrDefault(); if (genericCollectionContainsSymbol is null) return false; var genericCollectionSymbolImplementation = containingType.FindImplementationForInterfaceMember(genericCollectionContainsSymbol); return SymbolEqualityComparer.Default.Equals(genericCollectionSymbolImplementation, methodSymbol); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEmptyCollectionCheckShouldNotBeUsed.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEmptyCollectionCheckShouldNotBeUsed : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Collection, Constants.Asserts.CollectionAsync, ]; public AssertEmptyCollectionCheckShouldNotBeUsed() : base(Descriptors.X2011_AssertEmptyCollectionCheckShouldNotBeUsed, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Syntax is not InvocationExpressionSyntax invocation) return; var arguments = invocation.ArgumentList.Arguments; if (arguments.Count != 1) return; var matchedType = false; var asyncEnumerable = TypeSymbolFactory.IAsyncEnumerableOfT(context.Compilation); if (asyncEnumerable != null) matchedType = SymbolEqualityComparer.Default.Equals(method.Parameters[0].Type.OriginalDefinition, asyncEnumerable); matchedType = matchedType || method.Parameters[0].Type.OriginalDefinition.SpecialType.Equals(SpecialType.System_Collections_Generic_IEnumerable_T); if (!matchedType) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2011_AssertEmptyCollectionCheckShouldNotBeUsed, invocationOperation.Syntax.GetLocation(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ) ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecks.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecks : AssertUsageAnalyzerBase { const string linqWhereMethod = "System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"; static readonly DiagnosticDescriptor[] targetDescriptors = [ Descriptors.X2029_AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck, Descriptors.X2030_AssertNotEmptyShouldNotBeUsedForCollectionContainsCheck, ]; static readonly string[] targetMethods = [ Constants.Asserts.Empty, Constants.Asserts.NotEmpty, ]; public AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecks() : base(targetDescriptors, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length != 1) return; var argument = arguments[0]; var value = argument.Value; if (value is IConversionOperation conversion) value = conversion.Operand; if (value is not IInvocationOperation innerInvocation) return; var originalMethod = SymbolDisplay.ToDisplayString(innerInvocation.TargetMethod.OriginalDefinition); if (originalMethod != linqWhereMethod) return; var descriptor = method.Name == Constants.Asserts.Empty ? targetDescriptors[0] : targetDescriptors[1]; context.ReportDiagnostic( Diagnostic.Create( descriptor, invocationOperation.Syntax.GetLocation(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ) ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck : AssertUsageAnalyzerBase { // Signature without nullable variant const string enumerableAnyExtensionMethod = "System.Linq.Enumerable.Any(System.Collections.Generic.IEnumerable, System.Func)"; static readonly string[] targetMethods = [ Constants.Asserts.False, Constants.Asserts.True, ]; public AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck() : base(Descriptors.X2012_AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length != 1) return; if (arguments[0].Value is not IInvocationOperation invocationExpression) return; var methodSymbol = invocationExpression.TargetMethod; if (SymbolDisplay.ToDisplayString(methodSymbol.OriginalDefinition) != enumerableAnyExtensionMethod) return; var replacement = method.Name == Constants.Asserts.True ? Constants.Asserts.Contains : Constants.Asserts.DoesNotContain; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.AssertMethodName] = method.Name; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2012_AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEqualGenericShouldNotBeUsedForStringValue.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualGenericShouldNotBeUsedForStringValue : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Equal, Constants.Asserts.StrictEqual, ]; public AssertEqualGenericShouldNotBeUsedForStringValue() : base(Descriptors.X2006_AssertEqualGenericShouldNotBeUsedForStringValue, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Arguments.Length != 2) return; if (!method.IsGenericMethod && method.Name == Constants.Asserts.Equal) return; if (method.IsGenericMethod && (!method.TypeArguments[0].SpecialType.Equals(SpecialType.System_String) || !method.Parameters[0].Type.SpecialType.Equals(SpecialType.System_String) || !method.Parameters[1].Type.SpecialType.Equals(SpecialType.System_String))) return; var invalidUsageDescription = method.Name == Constants.Asserts.Equal ? "Assert.Equal" : "Assert.StrictEqual"; var replacement = method.Name == Constants.Asserts.Equal ? "non-generic Assert.Equal" : "Assert.Equal"; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2006_AssertEqualGenericShouldNotBeUsedForStringValue, invocationOperation.Syntax.GetLocation(), invalidUsageDescription, replacement ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEqualLiteralValueShouldBeFirst.cs ================================================ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualLiteralValueShouldBeFirst : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Equal, Constants.Asserts.NotEqual, Constants.Asserts.NotStrictEqual, Constants.Asserts.StrictEqual, ]; public AssertEqualLiteralValueShouldBeFirst() : base(Descriptors.X2000_AssertEqualLiteralValueShouldBeFirst, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length < 2) return; var expectedArg = arguments.FirstOrDefault(arg => arg.Parameter?.Name == Constants.AssertArguments.Expected); var actualArg = arguments.FirstOrDefault(arg => arg.Parameter?.Name == Constants.AssertArguments.Actual); if (expectedArg is null || actualArg is null) return; if (IsLiteralOrConstant(actualArg.Value) && !IsLiteralOrConstant(expectedArg.Value)) { var parentMethod = context.ContainingSymbol; var parentType = parentMethod.ContainingType; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2000_AssertEqualLiteralValueShouldBeFirst, invocationOperation.Syntax.GetLocation(), actualArg.Value.Syntax.ToString(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithGenericsOptions(SymbolDisplayGenericsOptions.None) .WithParameterOptions(SymbolDisplayParameterOptions.IncludeName) ), parentMethod.Name, parentType?.Name ?? "" ) ); } } static bool IsLiteralOrConstant(IOperation operation) { if (operation.Kind == OperationKind.ObjectCreation && operation.Type?.SpecialType == SpecialType.System_String) return ((IObjectCreationOperation)operation).Arguments.All(arg => arg.Value.ConstantValue.HasValue); return operation.ConstantValue.HasValue || operation.Kind == OperationKind.TypeOf; } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEqualPrecisionShoulBeInRange.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualPrecisionShouldBeInRange : AssertUsageAnalyzerBase { static readonly Dictionary precisionMaxLimits = new() { { SpecialType.System_Double, 15 }, { SpecialType.System_Decimal, 28 }, }; static readonly string[] targetMethods = [ Constants.Asserts.Equal, Constants.Asserts.NotEqual, ]; static readonly Dictionary typeNames = new() { { SpecialType.System_Double, "double" }, { SpecialType.System_Decimal, "decimal" }, }; public AssertEqualPrecisionShouldBeInRange() : base(Descriptors.X2016_AssertEqualPrecisionShouldBeInRange, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var numericType = GetMethodNumericType(method); if (numericType is null) return; var precision = GetNumericLiteralValue(invocationOperation); if (precision is null) return; EnsurePrecisionInRange(context, precision.Value.precisionArgument.Value.Syntax.GetLocation(), numericType.Value, precision.Value.value); } // Gets double or decimal SpecialType if it is method's first argument type, null otherwise. // This has the semantic of: 1. Ensure the analysis applies, 2. Get data for further analysis. static SpecialType? GetMethodNumericType(IMethodSymbol method) { if (method.Parameters.Length != 3) return null; var type = method.Parameters[0].Type.SpecialType; if (type is not SpecialType.System_Double and not SpecialType.System_Decimal) return null; return type; } // Gets the value of precision used in Equal/NotEqual invocation or null if cannot be obtained. // This has the semantic of: 1. Ensure the analysis applies, 2. Get data for further analysis. static (IArgumentOperation precisionArgument, int value)? GetNumericLiteralValue(IInvocationOperation invocation) { if (invocation.Arguments.Length != 3) return null; var precisionParameter = invocation.TargetMethod.Parameters[2]; var precisionArgument = invocation.Arguments.FirstOrDefault(arg => SymbolEqualityComparer.Default.Equals(arg.Parameter, precisionParameter)); if (precisionArgument is null) return null; var constantValue = precisionArgument.Value.ConstantValue; if (!constantValue.HasValue || constantValue.Value is not int value) return null; return (precisionArgument, value); } static void EnsurePrecisionInRange( OperationAnalysisContext context, Location location, SpecialType numericType, int numericValue) { var precisionMax = precisionMaxLimits[numericType]; if (numericValue < 0 || numericValue > precisionMax) { var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.Replacement] = numericValue < 0 ? "0" : precisionMax.ToString(CultureInfo.InvariantCulture); context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2016_AssertEqualPrecisionShouldBeInRange, location, builder.ToImmutable(), $"[0..{precisionMax}]", typeNames[numericType] ) ); } } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEqualShouldNotBeUsedForBoolLiteralCheck.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualShouldNotBeUsedForBoolLiteralCheck : AssertUsageAnalyzerBase { static readonly HashSet equalMethods = [ Constants.Asserts.Equal, Constants.Asserts.StrictEqual, ]; static readonly HashSet notEqualMethods = [ Constants.Asserts.NotEqual, Constants.Asserts.NotStrictEqual, ]; static readonly string[] targetMethods = equalMethods.Union(notEqualMethods).ToArray(); public AssertEqualShouldNotBeUsedForBoolLiteralCheck() : base(Descriptors.X2004_AssertEqualShouldNotUsedForBoolLiteralCheck, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length is not 2 and not 3) return; // Match Assert.Equal(true, expression) but not e.g. Assert.Equal(true, expression). if (!method.IsGenericMethod || !method.TypeArguments[0].SpecialType.Equals(SpecialType.System_Boolean)) return; if (arguments.FirstOrDefault(arg => arg.Parameter?.Ordinal == 0)?.Value is not ILiteralOperation literalFirstArgument) return; var isTrue = literalFirstArgument.ConstantValue.HasValue && Equals(literalFirstArgument.ConstantValue.Value, true); var isFalse = literalFirstArgument.ConstantValue.HasValue && Equals(literalFirstArgument.ConstantValue.Value, false); if (!(isTrue ^ isFalse)) return; var replacement = GetReplacementMethodName(method.Name, isTrue); if (replacement is null) return; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.LiteralValue] = isTrue ? bool.TrueString : bool.FalseString; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2004_AssertEqualShouldNotUsedForBoolLiteralCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } static string? GetReplacementMethodName( string methodName, bool isTrue) { if (equalMethods.Contains(methodName)) return isTrue ? Constants.Asserts.True : Constants.Asserts.False; if (notEqualMethods.Contains(methodName)) return isTrue ? Constants.Asserts.False : Constants.Asserts.True; return null; } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEqualShouldNotBeUsedForCollectionSizeCheck.cs ================================================ using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualShouldNotBeUsedForCollectionSizeCheck : AssertUsageAnalyzerBase { static readonly HashSet allowedCollections = [ // ArraySegment.GetEnumerator() can throw "System.ArraySegment", // StringValues has an implicit string conversion that's preferred by the compiler, https://github.com/xunit/xunit/issues/2859 "Microsoft.Extensions.Primitives.StringValues", ]; static readonly HashSet sizeMethods = [ // Signatures without nullable variants "System.Array.Length", "System.Linq.Enumerable.Count(System.Collections.Generic.IEnumerable)", "System.Collections.Immutable.ImmutableArray.Length", ]; static readonly string[] targetMethods = [ Constants.Asserts.Equal, Constants.Asserts.NotEqual, ]; public AssertEqualShouldNotBeUsedForCollectionSizeCheck() : base(Descriptors.X2013_AssertEqualShouldNotBeUsedForCollectionSizeCheck, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (method.Parameters.Length != 2 || !method.Parameters[0].Type.SpecialType.Equals(SpecialType.System_Int32) || !method.Parameters[1].Type.SpecialType.Equals(SpecialType.System_Int32)) return; var sizeOperation = invocationOperation.Arguments.FirstOrDefault(arg => SymbolEqualityComparer.Default.Equals(arg.Parameter, method.Parameters[0]))?.Value; var sizeValue = sizeOperation?.ConstantValue ?? default; if (!sizeValue.HasValue) return; // Make sure the first parameter really is an int before checking its value. Could for example be a char. if (sizeValue.Value is not int size) return; if (size < 0 || size > 1 || (size == 1 && method.Name != Constants.Asserts.Equal)) return; var otherArgument = invocationOperation.Arguments.FirstOrDefault(arg => !SymbolEqualityComparer.Default.Equals(arg.Parameter, method.Parameters[0])); var symbol = otherArgument?.Value switch { IInvocationOperation o => o.TargetMethod, IPropertyReferenceOperation p => p.Property, _ => default(ISymbol), }; if (symbol is null) return; if (IsAllowedCollection(symbol) || (!IsWellKnownSizeMethod(symbol) && !IsICollectionCountProperty(context, symbol) && !IsICollectionOfTCountProperty(context, symbol) && !IsIReadOnlyCollectionOfTCountProperty(context, symbol))) return; var replacement = GetReplacementMethodName(method.Name, size); var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.SizeValue] = size.ToString(CultureInfo.InvariantCulture); builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2013_AssertEqualShouldNotBeUsedForCollectionSizeCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } static string GetReplacementMethodName( string methodName, int size) { if (size == 1) return Constants.Asserts.Single; return methodName == Constants.Asserts.Equal ? Constants.Asserts.Empty : Constants.Asserts.NotEmpty; } static bool IsAllowedCollection(ISymbol symbol) => allowedCollections.Contains(symbol.ContainingType.ConstructedFrom.ToDisplayString()); static bool IsWellKnownSizeMethod(ISymbol symbol) => sizeMethods.Contains(symbol.OriginalDefinition.ToDisplayString()); static bool IsICollectionCountProperty( OperationAnalysisContext context, ISymbol symbol) => IsCountPropertyOf(TypeSymbolFactory.ICollection(context.Compilation), symbol); static bool IsICollectionOfTCountProperty( OperationAnalysisContext context, ISymbol symbol) => IsCountPropertyOfGenericType(TypeSymbolFactory.ICollectionOfT(context.Compilation), symbol); static bool IsIReadOnlyCollectionOfTCountProperty( OperationAnalysisContext context, ISymbol symbol) => IsCountPropertyOfGenericType(TypeSymbolFactory.IReadOnlyCollectionOfT(context.Compilation), symbol); static bool IsCountPropertyOf( INamedTypeSymbol? collectionType, ISymbol symbol) { if (collectionType is null) return false; var memberSymbol = symbol; var containingType = memberSymbol.ContainingType; var countSymbol = collectionType.GetMember(nameof(ICollection.Count)); if (countSymbol is null) return false; if (SymbolEqualityComparer.Default.Equals(countSymbol, symbol)) return true; var countSymbolImplementation = containingType.FindImplementationForInterfaceMember(countSymbol); return SymbolEqualityComparer.Default.Equals(countSymbolImplementation, memberSymbol); } static bool IsCountPropertyOfGenericType( INamedTypeSymbol openCollectionType, ISymbol symbol) { var containingType = symbol.ContainingType; var concreteCollectionType = containingType.GetGenericInterfaceImplementation(openCollectionType); return concreteCollectionType is not null && IsCountPropertyOf(concreteCollectionType, symbol); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEqualShouldNotBeUsedForNullCheck.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualShouldNotBeUsedForNullCheck : AssertUsageAnalyzerBase { static readonly HashSet equalMethods = [ Constants.Asserts.Equal, Constants.Asserts.Same, Constants.Asserts.StrictEqual, ]; static readonly HashSet notEqualMethods = [ Constants.Asserts.NotEqual, Constants.Asserts.NotSame, Constants.Asserts.NotStrictEqual, ]; static readonly string[] targetMethods = equalMethods.Union(notEqualMethods).ToArray(); public AssertEqualShouldNotBeUsedForNullCheck() : base(Descriptors.X2003_AssertEqualShouldNotUsedForNullCheck, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Syntax is not InvocationExpressionSyntax invocation) return; var arguments = invocation.ArgumentList.Arguments; var literalFirstArgument = arguments.FirstOrDefault()?.Expression as LiteralExpressionSyntax; if (!literalFirstArgument?.IsKind(SyntaxKind.NullLiteralExpression) ?? true) return; var replacement = GetReplacementMethod(method.Name); if (replacement is null) return; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2003_AssertEqualShouldNotUsedForNullCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } static string? GetReplacementMethod(string methodName) { if (equalMethods.Contains(methodName)) return Constants.Asserts.Null; if (notEqualMethods.Contains(methodName)) return Constants.Asserts.NotNull; return null; } } ================================================ FILE: src/xunit.analyzers/X2000/AssertEqualsShouldNotBeUsed.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualsShouldNotBeUsed : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ nameof(Equals), nameof(ReferenceEquals), ]; public AssertEqualsShouldNotBeUsed() : base(Descriptors.X2001_AssertEqualsShouldNotBeUsed, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var replacement = method.Name switch { nameof(Equals) => Constants.Asserts.Equal, nameof(ReferenceEquals) => Constants.Asserts.Same, _ => null }; if (replacement is null) return; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2001_AssertEqualsShouldNotBeUsed, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) ), replacement ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertIsTypeShouldNotBeUsedForAbstractType.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertIsTypeShouldNotBeUsedForAbstractType : AssertUsageAnalyzerBase { public static readonly Dictionary ReplacementMethods = new() { { Constants.Asserts.IsType, Constants.Asserts.IsAssignableFrom }, { Constants.Asserts.IsNotType, Constants.Asserts.IsNotAssignableFrom }, }; const string abstractClass = "abstract class"; const string @interface = "interface"; public AssertIsTypeShouldNotBeUsedForAbstractType() : base(Descriptors.X2018_AssertIsTypeShouldNotBeUsedForAbstractType, ReplacementMethods.Keys) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var type = invocationOperation.TargetMethod.TypeArguments.FirstOrDefault(); if (type is null) return; var typeKind = type.TypeKind switch { TypeKind.Class => type.IsAbstract ? abstractClass : null, TypeKind.Interface => @interface, _ => null, }; if (typeKind is null) return; if (invocationOperation.Arguments.Length > 1) { if (invocationOperation.Arguments[1].Value is not ILiteralOperation operation) return; if (operation.ConstantValue.Value is not bool value) return; if (!value) return; } var typeName = SymbolDisplay.ToDisplayString(type); var builder = ImmutableDictionary.CreateBuilder(); string? replacement; if (xunitContext.Assert.SupportsInexactTypeAssertions) { replacement = "exactMatch: false"; builder[Constants.Properties.UseExactMatch] = bool.TrueString; } else { if (!ReplacementMethods.TryGetValue(invocationOperation.TargetMethod.Name, out replacement)) return; builder[Constants.Properties.UseExactMatch] = bool.FalseString; replacement = "Assert." + replacement; } context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2018_AssertIsTypeShouldNotBeUsedForAbstractType, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), typeKind, typeName, replacement ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertIsTypeShouldUseGenericOverloadType.cs ================================================ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertIsTypeShouldUseGenericOverloadType : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.IsAssignableFrom, Constants.Asserts.IsNotType, Constants.Asserts.IsType, ]; public AssertIsTypeShouldUseGenericOverloadType() : base(Descriptors.X2007_AssertIsTypeShouldUseGenericOverload, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (method.IsGenericMethod) return; var parameters = invocationOperation.TargetMethod.Parameters; if (parameters.Length < 2) return; var typeArgument = invocationOperation.Arguments.FirstOrDefault(arg => SymbolEqualityComparer.Default.Equals(arg.Parameter, parameters[0])); if (typeArgument?.Value is not ITypeOfOperation typeOfOperation) return; var type = typeOfOperation.TypeOperand; var typeName = SymbolDisplay.ToDisplayString(type); // Static abstract interface members can't be used as types in generics if (type.TypeKind == TypeKind.Interface) { var allInterfaces = (type as INamedTypeSymbol)?.AllInterfaces; if (allInterfaces is not null) { var allMembers = allInterfaces .Value .SelectMany(i => i.GetMembers()) .Concat(type.GetMembers()); if (allMembers.Any(m => m is { IsAbstract: true, IsStatic: true })) return; } } var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.TypeName] = typeName; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2007_AssertIsTypeShouldUseGenericOverload, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), typeName ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertNullShouldNotBeCalledOnValueTypes.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertNullShouldNotBeCalledOnValueTypes : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.NotNull, Constants.Asserts.Null, ]; public AssertNullShouldNotBeCalledOnValueTypes() : base(Descriptors.X2002_AssertNullShouldNotBeCalledOnValueTypes, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Arguments.Length != 1) return; var argumentValue = invocationOperation.Arguments[0].Value.WalkDownImplicitConversions(); var argumentType = argumentValue.Type; if (argumentType is null || IsArgumentTypeRecognizedAsReferenceType(argumentType)) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2002_AssertNullShouldNotBeCalledOnValueTypes, invocationOperation.Syntax.GetLocation(), GetDisplayString(method), GetDisplayString(argumentType) ) ); } static bool IsArgumentTypeRecognizedAsReferenceType(ITypeSymbol argumentType) { var isNullableOfT = argumentType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T; var isUnconstrainedGenericType = !argumentType.IsReferenceType && !argumentType.IsValueType; return argumentType.IsReferenceType || argumentType.TypeKind == TypeKind.Pointer || isNullableOfT || isUnconstrainedGenericType; } static string GetDisplayString(ISymbol method) { var displayFormat = SymbolDisplayFormat.CSharpShortErrorMessageFormat.WithParameterOptions(SymbolDisplayParameterOptions.None); return SymbolDisplay.ToDisplayString(method, displayFormat); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertRegexMatchShouldNotUseBoolLiteralCheck.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertRegexMatchShouldNotUseBoolLiteralCheck : AssertUsageAnalyzerBase { static readonly HashSet regexIsMatchSymbols = [ // Signatures without nullable variants "System.Text.RegularExpressions.Regex.IsMatch(string)", "System.Text.RegularExpressions.Regex.IsMatch(string, string)", ]; static readonly string[] targetMethods = [ Constants.Asserts.True, Constants.Asserts.False ]; public AssertRegexMatchShouldNotUseBoolLiteralCheck() : base(Descriptors.X2008_AssertRegexMatchShouldNotUseBoolLiteralCheck, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length != 1) return; if (arguments[0].Value is not IInvocationOperation invocationExpression) return; var methodSymbol = invocationExpression.TargetMethod; if (!regexIsMatchSymbols.Contains(SymbolDisplay.ToDisplayString(methodSymbol))) return; var replacement = method.Name == Constants.Asserts.True ? Constants.Asserts.Matches : Constants.Asserts.DoesNotMatch; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.IsStatic] = methodSymbol.IsStatic ? bool.TrueString : bool.FalseString; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2008_AssertRegexMatchShouldNotUseBoolLiteralCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertSameShouldNotBeCalledOnValueTypes.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertSameShouldNotBeCalledOnValueTypes : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Same, Constants.Asserts.NotSame ]; public AssertSameShouldNotBeCalledOnValueTypes() : base(Descriptors.X2005_AssertSameShouldNotBeCalledOnValueTypes, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Arguments.Length != 2) return; var firstArgumentType = invocationOperation.Arguments[0].Value.WalkDownImplicitConversions()?.Type; var secondArgumentType = invocationOperation.Arguments[1].Value.WalkDownImplicitConversions()?.Type; if (firstArgumentType is null && secondArgumentType is null) return; if (firstArgumentType?.IsReferenceType == true && secondArgumentType?.IsReferenceType == true) return; var typeToDisplay = firstArgumentType is null || firstArgumentType.IsReferenceType ? secondArgumentType : firstArgumentType; if (typeToDisplay is null) return; var replacement = method.Name switch { Constants.Asserts.Same => Constants.Asserts.Equal, Constants.Asserts.NotSame => Constants.Asserts.NotEqual, _ => null, }; if (replacement is null) return; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2005_AssertSameShouldNotBeCalledOnValueTypes, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) ), SymbolDisplay.ToDisplayString( typeToDisplay, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) ), replacement ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertSingleShouldBeUsedForSingleParameter.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertSingleShouldBeUsedForSingleParameter : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Collection, Constants.Asserts.CollectionAsync, ]; public AssertSingleShouldBeUsedForSingleParameter() : base(Descriptors.X2023_AssertSingleShouldBeUsedForSingleParameter, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Arguments.Length != 2) return; var secondParameter = invocationOperation.Arguments[1]; if (secondParameter.ArgumentKind != ArgumentKind.ParamArray) return; if (secondParameter.Value is not IArrayCreationOperation operation) return; if (operation.DimensionSizes.Length != 1 || (int)(operation.DimensionSizes[0].ConstantValue.Value ?? 0) != 1) return; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.AssertMethodName] = method.Name; builder[Constants.Properties.Replacement] = Constants.Asserts.Single; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2023_AssertSingleShouldBeUsedForSingleParameter, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), method.Name ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertSingleShouldUseTwoArgumentCall.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertSingleShouldUseTwoArgumentCall : AssertUsageAnalyzerBase { const string linqWhereMethod = "System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"; static readonly string[] targetMethods = [ Constants.Asserts.Single, ]; public AssertSingleShouldUseTwoArgumentCall() : base(Descriptors.X2031_AssertSingleShouldUseTwoArgumentCall, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length != 1) return; var argument = arguments[0]; var value = argument.Value; if (value is IConversionOperation conversion) value = conversion.Operand; if (value is not IInvocationOperation innerInvocation) return; var originalMethod = SymbolDisplay.ToDisplayString(innerInvocation.TargetMethod.OriginalDefinition); if (originalMethod != linqWhereMethod) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2031_AssertSingleShouldUseTwoArgumentCall, invocationOperation.Syntax.GetLocation(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ) ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertStringEqualityCheckShouldNotUseBoolCheck.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertStringEqualityCheckShouldNotUseBoolCheck : AssertUsageAnalyzerBase { static readonly HashSet stringEqualsMethods = [ // Non-nullable signatures "string.Equals(string)", "string.Equals(string, string)", "string.Equals(string, System.StringComparison)", "string.Equals(string, string, System.StringComparison)", // Nullable signatures "string.Equals(string?)", "string.Equals(string?, string?)", "string.Equals(string?, System.StringComparison)", "string.Equals(string?, string?, System.StringComparison)", ]; static readonly HashSet supportedStringComparisons = [ StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase, ]; static readonly string[] targetMethods = [ Constants.Asserts.False, Constants.Asserts.True, ]; public AssertStringEqualityCheckShouldNotUseBoolCheck() : base(Descriptors.X2010_AssertStringEqualityCheckShouldNotUseBoolCheckFixer, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length != 1) return; if (arguments[0].Value is not IInvocationOperation invocationExpression) return; var methodSymbol = invocationExpression.TargetMethod; if (!stringEqualsMethods.Contains(SymbolDisplay.ToDisplayString(methodSymbol))) return; var ignoreCase = string.Empty; if (methodSymbol.Parameters.Last().Type.TypeKind == TypeKind.Enum) { if (method.Name == Constants.Asserts.False) return; var stringComparisonExpression = invocationExpression.Arguments.FirstOrDefault(arg => SymbolEqualityComparer.Default.Equals(arg.Parameter, methodSymbol.Parameters.Last()))?.Value; var stringComparison = (StringComparison?)(stringComparisonExpression?.ConstantValue.Value as int?); if (!stringComparison.HasValue || !supportedStringComparisons.Contains(stringComparison.Value)) return; ignoreCase = stringComparison == StringComparison.OrdinalIgnoreCase ? bool.TrueString : bool.FalseString; } var replacement = method.Name == Constants.Asserts.True ? Constants.Asserts.Equal : Constants.Asserts.NotEqual; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.AssertMethodName] = method.Name; builder[Constants.Properties.IsStaticMethodCall] = methodSymbol.IsStatic ? bool.TrueString : bool.FalseString; builder[Constants.Properties.IgnoreCase] = ignoreCase; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2010_AssertStringEqualityCheckShouldNotUseBoolCheckFixer, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssertSubstringCheckShouldNotUseBoolCheck.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertSubstringCheckShouldNotUseBoolCheck : AssertUsageAnalyzerBase { private static readonly HashSet substringMethods = [ // Signatures without nullable variants "string.Contains(string)", "string.StartsWith(string)", "string.StartsWith(string, System.StringComparison)", "string.EndsWith(string)", "string.EndsWith(string, System.StringComparison)", ]; static readonly string[] targetMethods = [ Constants.Asserts.False, Constants.Asserts.True, ]; public AssertSubstringCheckShouldNotUseBoolCheck() : base(Descriptors.X2009_AssertSubstringCheckShouldNotUseBoolCheck, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length != 1) return; if (arguments[0].Value is not IInvocationOperation invocationExpression) return; var methodSymbol = invocationExpression.TargetMethod; if (!substringMethods.Contains(SymbolDisplay.ToDisplayString(methodSymbol))) return; if (methodSymbol.Name != Constants.Asserts.Contains && method.Name == Constants.Asserts.False) return; var replacement = GetReplacementMethodName(method.Name, methodSymbol.Name); var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.AssertMethodName] = method.Name; builder[Constants.Properties.SubstringMethodName] = methodSymbol.Name; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2009_AssertSubstringCheckShouldNotUseBoolCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } static string GetReplacementMethodName( string assertMethodName, string substringMethodName) { if (substringMethodName == nameof(string.Contains)) return assertMethodName == Constants.Asserts.True ? Constants.Asserts.Contains : Constants.Asserts.DoesNotContain; return substringMethodName; } } ================================================ FILE: src/xunit.analyzers/X2000/AssertThrowsShouldNotBeUsedForAsyncThrowsCheck.cs ================================================ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertThrowsShouldNotBeUsedForAsyncThrowsCheck : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Throws, Constants.Asserts.ThrowsAny, ]; public AssertThrowsShouldNotBeUsedForAsyncThrowsCheck() : base([Descriptors.X2014_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck], targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Arguments.Length is < 1 or > 2) return; var throwExpressionSymbol = GetThrowExpressionSymbol(invocationOperation); if (!ThrowExpressionReturnsTask(throwExpressionSymbol, context)) return; var replacement = method.Name + "Async"; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2014_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) ), replacement ) ); } static ISymbol? GetThrowExpressionSymbol(IInvocationOperation invocationOperation) { var argument = invocationOperation.Arguments.LastOrDefault()?.Value; if (argument is IDelegateCreationOperation delegateCreation) { if (delegateCreation.Target is IAnonymousFunctionOperation anonymousFunction) { IOperation? symbolOperation = null; if (anonymousFunction.Body.Operations.Length == 2 && anonymousFunction.Body.Operations[0] is IExpressionStatementOperation expressionStatement && anonymousFunction.Body.Operations[1] is IReturnOperation { ReturnedValue: null }) { symbolOperation = expressionStatement.Operation; } else if (anonymousFunction.Body.Operations.Length == 1 && anonymousFunction.Body.Operations[0] is IReturnOperation { ReturnedValue: { } returnedValue }) { symbolOperation = returnedValue.WalkDownImplicitConversions(); } if (symbolOperation is IAwaitOperation awaitOperation) symbolOperation = awaitOperation.Operation.WalkDownImplicitConversions(); if (symbolOperation is IInvocationOperation symbolInvoke) return symbolInvoke.TargetMethod; else if (symbolOperation is ILiteralOperation) return null; } else if (delegateCreation.Target is IMethodReferenceOperation methodReference) return methodReference.Method; } return null; } static bool ThrowExpressionReturnsTask( ISymbol? symbol, OperationAnalysisContext context) { if (symbol?.Kind != SymbolKind.Method) return false; if (symbol is not IMethodSymbol methodSymbol) return false; var returnType = methodSymbol.ReturnType; var taskType = TypeSymbolFactory.Task(context.Compilation); if (taskType.IsAssignableFrom(returnType)) return true; var configuredTaskAwaitableType = TypeSymbolFactory.ConfiguredTaskAwaitable(context.Compilation); if (configuredTaskAwaitableType.IsAssignableFrom(returnType)) return true; return false; } } ================================================ FILE: src/xunit.analyzers/X2000/AssertThrowsShouldUseGenericOverloadCheck.cs ================================================ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertThrowsShouldUseGenericOverloadCheck : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Throws, Constants.Asserts.ThrowsAsync, ]; public AssertThrowsShouldUseGenericOverloadCheck() : base(Descriptors.X2015_AssertThrowsShouldUseGenericOverload, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var parameters = invocationOperation.TargetMethod.Parameters; if (parameters.Length != 2) return; var typeArgument = invocationOperation.Arguments.FirstOrDefault(arg => SymbolEqualityComparer.Default.Equals(arg.Parameter, parameters[0]))?.Value; if (typeArgument is not ITypeOfOperation typeOfOperation) return; var type = typeOfOperation.TypeOperand; var typeName = SymbolDisplay.ToDisplayString(type); var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.TypeName] = typeName; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2015_AssertThrowsShouldUseGenericOverload, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), method.Name, typeName ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AssignableFromAssertionIsConfusinglyNamed.cs ================================================ using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssignableFromAssertionIsConfusinglyNamed : AssertUsageAnalyzerBase { public static readonly Dictionary ReplacementMethods = new() { { Constants.Asserts.IsAssignableFrom, Constants.Asserts.IsType }, { Constants.Asserts.IsNotAssignableFrom, Constants.Asserts.IsNotType }, }; public AssignableFromAssertionIsConfusinglyNamed() : base(Descriptors.X2032_AssignableFromAssertionIsConfusinglyNamed, ReplacementMethods.Keys) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (!xunitContext.Assert.SupportsInexactTypeAssertions) return; if (!ReplacementMethods.TryGetValue(invocationOperation.TargetMethod.Name, out var replacement)) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2032_AssignableFromAssertionIsConfusinglyNamed, invocationOperation.Syntax.GetLocation(), invocationOperation.TargetMethod.Name, replacement ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/AsyncAssertsShouldBeAwaited.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AsyncAssertsShouldBeAwaited : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.AllAsync, Constants.Asserts.CollectionAsync, Constants.Asserts.PropertyChangedAsync, Constants.Asserts.RaisesAnyAsync, Constants.Asserts.RaisesAsync, Constants.Asserts.ThrowsAnyAsync, Constants.Asserts.ThrowsAsync, ]; public AsyncAssertsShouldBeAwaited() : base(Descriptors.X2021_AsyncAssertionsShouldBeAwaited, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var taskType = TypeSymbolFactory.Task(context.Compilation); var taskOfTType = TypeSymbolFactory.TaskOfT(context.Compilation)?.ConstructUnboundGenericType(); for (IOperation? current = invocationOperation; current is not null; current = current.Parent) { // Stop looking when we've hit the enclosing block if (current is IBlockOperation) return; // Only interested in something that results in an expression to a named type if (current is not IExpressionStatementOperation expression || expression.Operation.Type is not INamedTypeSymbol namedReturnType) continue; if (namedReturnType.IsGenericType) { // Does it return Task? if (!SymbolEqualityComparer.Default.Equals(namedReturnType.ConstructUnboundGenericType(), taskOfTType)) continue; } else { // Does it return Task? if (!SymbolEqualityComparer.Default.Equals(namedReturnType, taskType)) continue; } context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2021_AsyncAssertionsShouldBeAwaited, invocationOperation.Syntax.GetLocation(), method.Name ) ); } } } ================================================ FILE: src/xunit.analyzers/X2000/BooleanAssertsShouldNotBeNegated.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class BooleanAssertsShouldNotBeNegated : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.False, Constants.Asserts.True, ]; public BooleanAssertsShouldNotBeNegated() : base(Descriptors.X2022_BooleanAssertionsShouldNotBeNegated, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Arguments.Length < 1) return; if (invocationOperation.Arguments[0].Value is not IUnaryOperation unaryOperation) return; if (!unaryOperation.Syntax.IsKind(SyntaxKind.LogicalNotExpression)) return; var suggestedAssertion = method.Name == Constants.Asserts.False ? Constants.Asserts.True : Constants.Asserts.False; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.Replacement] = suggestedAssertion; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2022_BooleanAssertionsShouldNotBeNegated, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), method.Name, suggestedAssertion ) ); } } ================================================ FILE: src/xunit.analyzers/X2000/BooleanAssertsShouldNotBeUsedForSimpleEqualityCheck.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class BooleanAssertsShouldNotBeUsedForSimpleEqualityCheck : AssertUsageAnalyzerBase { public BooleanAssertsShouldNotBeUsedForSimpleEqualityCheck() : base( [ Descriptors.X2024_BooleanAssertionsShouldNotBeUsedForSimpleEqualityCheck, Descriptors.X2025_BooleanAssertionCanBeSimplified ], [ Constants.Asserts.True, Constants.Asserts.False ] ) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); if (invocationOperation.Syntax is not InvocationExpressionSyntax invocation) return; var arguments = invocation.ArgumentList.Arguments; if (arguments.Count == 0) return; if (arguments[0].Expression is not BinaryExpressionSyntax binaryArgument) return; var semanticModel = context.Operation.SemanticModel; var trueMethod = method.Name == Constants.Asserts.True; var leftKind = LiteralReferenceKind(binaryArgument.Left, semanticModel); var rightKind = LiteralReferenceKind(binaryArgument.Right, semanticModel); var literalKind = leftKind ?? rightKind; if (literalKind is null) return; bool? isEqualsOperator = binaryArgument.Kind() switch { SyntaxKind.EqualsExpression => true, SyntaxKind.NotEqualsExpression => false, _ => null }; if (isEqualsOperator is null) return; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.LiteralValue] = leftKind is not null ? Constants.Asserts.True : Constants.Asserts.False; switch (literalKind) { case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: var booleanReplacement = (trueMethod == isEqualsOperator, literalKind) switch { (true, SyntaxKind.TrueLiteralExpression) or (false, SyntaxKind.FalseLiteralExpression) => Constants.Asserts.True, (_, _) => Constants.Asserts.False, }; builder[Constants.Properties.Replacement] = booleanReplacement; ReportShouldSimplifyBooleanOperation(context, invocationOperation, builder.ToImmutable(), method.Name); break; case SyntaxKind.NullLiteralExpression: // Can't rewrite exactly if there is a "message" (second) argument if (arguments.Count > 1) return; // Can't rewrite if we're using a pointer and don't support pointers in Null assertions if (!xunitContext.Assert.SupportsAssertNullWithPointers) if (binaryArgument.Left.IsPointer(semanticModel) || binaryArgument.Right.IsPointer(semanticModel)) return; var nullReplacement = trueMethod == isEqualsOperator ? Constants.Asserts.Null : Constants.Asserts.NotNull; builder[Constants.Properties.Replacement] = nullReplacement; ReportShouldReplaceBooleanOperationWithEquality(context, invocationOperation, builder.ToImmutable(), method.Name, nullReplacement); break; case SyntaxKind.CharacterLiteralExpression: case SyntaxKind.StringLiteralExpression: case SyntaxKind.NumericLiteralExpression: case SyntaxKind.SimpleMemberAccessExpression: // Can't rewrite exactly if there is a "message" (second) argument if (arguments.Count > 1) return; var equalsReplacement = trueMethod == isEqualsOperator ? Constants.Asserts.Equal : Constants.Asserts.NotEqual; builder[Constants.Properties.Replacement] = equalsReplacement; ReportShouldReplaceBooleanOperationWithEquality(context, invocationOperation, builder.ToImmutable(), method.Name, equalsReplacement); break; } } public static SyntaxKind? LiteralReferenceKind( ExpressionSyntax expression, SemanticModel? semanticModel) { Guard.ArgumentNotNull(expression); var kind = expression.Kind(); if (expression is LiteralExpressionSyntax) return kind; if (expression.Kind() != SyntaxKind.SimpleMemberAccessExpression) return null; var left = ((MemberAccessExpressionSyntax)expression).Expression; if (left.Kind() != SyntaxKind.IdentifierName) return null; var type = semanticModel?.GetTypeInfo(expression).Type; if (type is not INamedTypeSymbol namedType) return null; return namedType.EnumUnderlyingType is not null ? kind : null; } static void ReportShouldReplaceBooleanOperationWithEquality( OperationAnalysisContext context, IInvocationOperation invocationOperation, ImmutableDictionary properties, string currentMethodName, string replacement) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2024_BooleanAssertionsShouldNotBeUsedForSimpleEqualityCheck, invocationOperation.Syntax.GetLocation(), properties, currentMethodName, replacement ) ); static void ReportShouldSimplifyBooleanOperation( OperationAnalysisContext context, IInvocationOperation invocationOperation, ImmutableDictionary properties, string currentMethodName) => context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2025_BooleanAssertionCanBeSimplified, invocationOperation.Syntax.GetLocation(), properties, currentMethodName ) ); } ================================================ FILE: src/xunit.analyzers/X2000/DoNotUseAssertEmptyWithProblematicTypes.cs ================================================ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class DoNotUseAssertEmptyWithProblematicTypes : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Empty, Constants.Asserts.NotEmpty, ]; public DoNotUseAssertEmptyWithProblematicTypes() : base(Descriptors.X2028_DoNotUseAssertEmptyWithProblematicTypes, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var semanticModel = context.Operation.SemanticModel; if (semanticModel is null) return; var arguments = invocationOperation.Arguments; if (arguments.Length != 1) return; if (method.Parameters.Length != 1) return; if (semanticModel.GetTypeInfo(arguments[0].Value.Syntax).Type is not INamedTypeSymbol sourceType) return; var stringValuesType = TypeSymbolFactory.StringValues(context.Compilation); if (stringValuesType is not null && SymbolEqualityComparer.Default.Equals(sourceType, stringValuesType)) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2028_DoNotUseAssertEmptyWithProblematicTypes, invocationOperation.Syntax.GetLocation(), method.Name, sourceType.ToMinimalDisplayString(semanticModel, 0), "it is implicitly cast to a string, not a collection" ) ); if (sourceType.IsGenericType) { var arraySegmentType = TypeSymbolFactory.ArraySegmentOfT(context.Compilation)?.ConstructUnboundGenericType(); if (arraySegmentType is not null && SymbolEqualityComparer.Default.Equals(sourceType.ConstructUnboundGenericType(), arraySegmentType)) context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2028_DoNotUseAssertEmptyWithProblematicTypes, invocationOperation.Syntax.GetLocation(), method.Name, sourceType.ToMinimalDisplayString(semanticModel, 0), "its implementation of GetEnumerator() can throw" ) ); } } } ================================================ FILE: src/xunit.analyzers/X2000/SetEqualityAnalyzer.cs ================================================ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class SetEqualityAnalyzer : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.Equal, Constants.Asserts.NotEqual, ]; public SetEqualityAnalyzer() : base( [ Descriptors.X2026_SetsMustBeComparedWithEqualityComparer, Descriptors.X2027_SetsShouldNotBeComparedToLinearContainers, ], targetMethods ) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var semanticModel = context.Operation.SemanticModel; if (semanticModel == null) return; var setType = TypeSymbolFactory.ISetOfT(context.Compilation)?.ConstructUnboundGenericType(); var readOnlySetType = TypeSymbolFactory.IReadOnlySetOfT(context.Compilation)?.ConstructUnboundGenericType(); var setInterfaces = new HashSet(new[] { setType, readOnlySetType }.WhereNotNull(), SymbolEqualityComparer.Default); var arguments = invocationOperation.Arguments; if (arguments.Length < 2) return; if (semanticModel.GetTypeInfo(arguments[0].Value.Syntax).Type is not INamedTypeSymbol collection0Type) return; var interface0Type = collection0Type .AllInterfaces .Concat([collection0Type]) .Where(i => i.IsGenericType) .FirstOrDefault(i => setInterfaces.Contains(i.ConstructUnboundGenericType())); if (semanticModel.GetTypeInfo(arguments[1].Value.Syntax).Type is not INamedTypeSymbol collection1Type) return; var interface1Type = collection1Type .AllInterfaces .Concat([collection1Type]) .Where(i => i.IsGenericType) .FirstOrDefault(i => setInterfaces.Contains(i.ConstructUnboundGenericType())); // No sets if (interface0Type is null && interface1Type is null) return; // Both sets, make sure they don't use the comparer function override if (interface0Type is not null && interface1Type is not null) { if (arguments.Length != 3) return; if (arguments[2].Value is not IDelegateCreationOperation and not ILocalReferenceOperation) return; if (arguments[2].Value.Type is not INamedTypeSymbol funcTypeSymbol || funcTypeSymbol.DelegateInvokeMethod == null) return; var funcDelegate = funcTypeSymbol.DelegateInvokeMethod; var isFuncOverload = funcDelegate.ReturnType.SpecialType == SpecialType.System_Boolean && funcDelegate.Parameters.Length == 2 && funcDelegate.Parameters[0].Type.Equals(interface0Type.TypeArguments[0], SymbolEqualityComparer.Default) && funcDelegate.Parameters[1].Type.Equals(interface1Type.TypeArguments[0], SymbolEqualityComparer.Default); // Wrong method overload if (!isFuncOverload) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2026_SetsMustBeComparedWithEqualityComparer, invocationOperation.Syntax.GetLocation(), method.Name ) ); } // One set, one linear container else { // Make a special allowance for SortedSet<>, since we know it's sorted var sortedSet = TypeSymbolFactory.SortedSetOfT(context.Compilation); if (sortedSet is not null) { if (interface0Type is not null && sortedSet.Construct(interface0Type.TypeArguments[0]).IsAssignableFrom(collection0Type)) return; if (interface1Type is not null && sortedSet.Construct(interface1Type.TypeArguments[0]).IsAssignableFrom(collection1Type)) return; } context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2027_SetsShouldNotBeComparedToLinearContainers, invocationOperation.Syntax.GetLocation(), collection0Type.ToMinimalDisplayString(semanticModel, 0), collection1Type.ToMinimalDisplayString(semanticModel, 0) ) ); } } } ================================================ FILE: src/xunit.analyzers/X2000/UseAssertFailInsteadOfBooleanAssert.cs ================================================ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class UseAssertFailInsteadOfBooleanAssert : AssertUsageAnalyzerBase { static readonly string[] targetMethods = [ Constants.Asserts.False, Constants.Asserts.True, ]; static readonly Dictionary targetValues = new() { { Constants.Asserts.False, true }, { Constants.Asserts.True, false }, }; public UseAssertFailInsteadOfBooleanAssert() : base(Descriptors.X2020_UseAssertFailInsteadOfBooleanAssert, targetMethods) { } protected override void AnalyzeInvocation( OperationAnalysisContext context, XunitContext xunitContext, IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext); Guard.ArgumentNotNull(invocationOperation); Guard.ArgumentNotNull(method); var arguments = invocationOperation.Arguments; if (arguments.Length != 2) return; if (!targetValues.TryGetValue(method.Name, out var targetValue)) return; if (arguments.FirstOrDefault(arg => arg.Parameter?.Ordinal == 0)?.Value is not ILiteralOperation literalFirstArgument) return; if (!literalFirstArgument.ConstantValue.HasValue) return; if (!Equals(literalFirstArgument.ConstantValue.Value, targetValue)) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2020_UseAssertFailInsteadOfBooleanAssert, invocationOperation.Syntax.GetLocation(), method.Name, targetValue ? "true" : "false" ) ); } protected override bool ShouldAnalyze(XunitContext xunitContext) { Guard.ArgumentNotNull(xunitContext); return base.ShouldAnalyze(xunitContext) && xunitContext.Assert.SupportsAssertFail; } } ================================================ FILE: src/xunit.analyzers/X3000/CrossAppDomainClassesMustBeLongLivedMarshalByRefObject.cs ================================================ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class CrossAppDomainClassesMustBeLongLivedMarshalByRefObject : XunitV2DiagnosticAnalyzer { public CrossAppDomainClassesMustBeLongLivedMarshalByRefObject() : base(Descriptors.X3000_CrossAppDomainClassesMustBeLongLivedMarshalByRefObject) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (context.Symbol is not INamedTypeSymbol namedType) return; if (namedType.TypeKind != TypeKind.Class) return; if (xunitContext.V2Abstractions is null) return; var mbroInterfaces = new INamedTypeSymbol?[] { xunitContext.V2Abstractions.IAssemblyInfoType, xunitContext.V2Abstractions.IAttributeInfoType, xunitContext.V2Abstractions.IMessageSinkMessageType, xunitContext.V2Abstractions.IMessageSinkType, xunitContext.V2Abstractions.IMethodInfoType, xunitContext.V2Abstractions.IParameterInfoType, xunitContext.V2Abstractions.ISourceInformationProviderType, xunitContext.V2Abstractions.ISourceInformationType, xunitContext.V2Abstractions.ITestAssemblyType, xunitContext.V2Abstractions.ITestCaseType, xunitContext.V2Abstractions.ITestClassType, xunitContext.V2Abstractions.ITestCollectionType, xunitContext.V2Abstractions.ITestFrameworkDiscovererType, xunitContext.V2Abstractions.ITestFrameworkExecutorType, xunitContext.V2Abstractions.ITestFrameworkType, xunitContext.V2Abstractions.ITestMethodType, xunitContext.V2Abstractions.ITestType, xunitContext.V2Abstractions.ITypeInfoType, }; if (!mbroInterfaces.Any(t => t.IsAssignableFrom(namedType))) return; var hasMBRO = (xunitContext.V2Execution?.LongLivedMarshalByRefObjectType?.IsAssignableFrom(namedType) ?? false) || (xunitContext.V2RunnerUtility?.LongLivedMarshalByRefObjectType?.IsAssignableFrom(namedType) ?? false); if (hasMBRO) return; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.NewBaseType] = xunitContext.V2RunnerUtility is not null ? Constants.Types.Xunit.LongLivedMarshalByRefObject_RunnerUtility : xunitContext.V2Execution is not null ? Constants.Types.Xunit.LongLivedMarshalByRefObject_Execution_V2 : null; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X3000_CrossAppDomainClassesMustBeLongLivedMarshalByRefObject, namedType.Locations.First(), builder.ToImmutable(), namedType.Name ) ); }, SymbolKind.NamedType); } protected override bool ShouldAnalyze(XunitContext xunitContext) => Guard.ArgumentNotNull(xunitContext).V2Abstractions is not null; } ================================================ FILE: src/xunit.analyzers/X3000/DoNotTestForConcreteTypeOfJsonSerializableTypes.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class DoNotTestForConcreteTypeOfJsonSerializableTypes : XunitV3DiagnosticAnalyzer { public DoNotTestForConcreteTypeOfJsonSerializableTypes() : base(Descriptors.X3002_DoNotTestForConcreteTypeOfJsonSerializableTypes) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var assertType = xunitContext.Assert.AssertType; var jsonTypeIDAttributeType = xunitContext.V3Core?.JsonTypeIDAttributeType; if (jsonTypeIDAttributeType is null) return; // "value is Type" context.RegisterOperationAction(context => { if (context.Operation is not IIsTypeOperation isTypeOperation) return; var semanticModel = isTypeOperation.SemanticModel; if (semanticModel is null) return; reportIfMessageType(context.ReportDiagnostic, semanticModel, isTypeOperation.TypeOperand, isTypeOperation.Syntax); }, OperationKind.IsType); // "value is not Type" context.RegisterOperationAction(context => { if (context.Operation is not IIsPatternOperation isPatternOperation) return; var semanticModel = isPatternOperation.SemanticModel; if (semanticModel is null) return; if (isPatternOperation.Pattern is not INegatedPatternOperation negatedPatternOperation) return; #if ROSLYN_LATEST if (negatedPatternOperation.ChildOperations.FirstOrDefault() is not ITypePatternOperation typePatternOperation) #else if (negatedPatternOperation.Children.FirstOrDefault() is not ITypePatternOperation typePatternOperation) #endif return; reportIfMessageType(context.ReportDiagnostic, semanticModel, typePatternOperation.MatchedType, isPatternOperation.Syntax); }, OperationKind.IsPattern); // "value as Type" // "(Type)value" context.RegisterOperationAction(context => { if (context.Operation is not IConversionOperation conversionOperation) return; // We don't want to prohibit conversion that comes from "new(...)" if (conversionOperation.Syntax is ImplicitObjectCreationExpressionSyntax) return; var semanticModel = conversionOperation.SemanticModel; if (semanticModel is null) return; reportIfMessageType(context.ReportDiagnostic, semanticModel, conversionOperation.Type, conversionOperation.Syntax); }, OperationKind.Conversion); // "typeof(Type)" context.RegisterOperationAction(context => { if (context.Operation is not ITypeOfOperation typeOfOperation) return; var semanticModel = typeOfOperation.SemanticModel; if (semanticModel is null) return; reportIfMessageType(context.ReportDiagnostic, semanticModel, typeOfOperation.TypeOperand, typeOfOperation.Syntax); }, OperationKind.TypeOf); // "SomeMethod()" // "GenericType" context.RegisterSyntaxNodeAction(context => { if (context.Node.ChildNodes().FirstOrDefault() is not TypeArgumentListSyntax typeArgumentListSyntax) return; foreach (var identifierNameSyntax in typeArgumentListSyntax.ChildNodes().OfType()) reportIfMessageType(context.ReportDiagnostic, context.SemanticModel, context.SemanticModel.GetTypeInfo(identifierNameSyntax).ConvertedType, context.Node); }, SyntaxKind.GenericName); void reportIfMessageType( Action reportDiagnostic, SemanticModel semanticModel, ITypeSymbol? typeSymbol, SyntaxNode syntax) { if (typeSymbol is null) return; foreach (var attribute in typeSymbol.GetAttributes()) if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, jsonTypeIDAttributeType)) reportDiagnostic( Diagnostic.Create( Descriptors.X3002_DoNotTestForConcreteTypeOfJsonSerializableTypes, syntax.GetLocation(), typeSymbol.ToMinimalDisplayString(semanticModel, syntax.SpanStart) ) ); } } } ================================================ FILE: src/xunit.analyzers/X3000/FactAttributeDerivedClassesShouldProvideSourceInformationConstructor.cs ================================================ using System; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class FactAttributeDerivedClassesShouldProvideSourceInformationConstructor() : XunitV3DiagnosticAnalyzer(Descriptors.X3003_ProvideConstructorForFactAttributeOverride) { static readonly Version Version_3_0_0 = new(3, 0, 0); public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); var factAttributeType = xunitContext.Core.FactAttributeType; if (factAttributeType is null) return; var callerFilePathAttribute = TypeSymbolFactory.CallerFilePathAttribute(context.Compilation); if (callerFilePathAttribute is null) return; var callerLineNumberAttribute = TypeSymbolFactory.CallerLineNumberAttribute(context.Compilation); if (callerLineNumberAttribute is null) return; context.RegisterSymbolAction(context => { var type = context.Symbol as INamedTypeSymbol; if (type is null) return; var baseType = type.BaseType; while (true) { if (baseType is null) return; if (SymbolEqualityComparer.Default.Equals(factAttributeType, baseType)) break; baseType = baseType.BaseType; } if (type.Constructors.Any(hasSourceInformationParameters)) return; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X3003_ProvideConstructorForFactAttributeOverride, type.Locations.First(), SymbolDisplay.ToDisplayString(type) ) ); }, SymbolKind.NamedType); bool hasSourceInformationParameters(IMethodSymbol symbol) { var hasCallerFilePath = false; var hasCallerLineNumber = false; foreach (var parameter in symbol.Parameters) foreach (var attribute in parameter.GetAttributes().Select(a => a.AttributeClass)) { if (SymbolEqualityComparer.Default.Equals(callerFilePathAttribute, attribute)) hasCallerFilePath = true; if (SymbolEqualityComparer.Default.Equals(callerLineNumberAttribute, attribute)) hasCallerLineNumber = true; } return hasCallerFilePath && hasCallerLineNumber; } } protected override bool ShouldAnalyze(XunitContext xunitContext) => xunitContext?.V3Core is not null && xunitContext.V3Core.Version >= Version_3_0_0; } ================================================ FILE: src/xunit.analyzers/X3000/SerializableClassMustHaveParameterlessConstructor.cs ================================================ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class SerializableClassMustHaveParameterlessConstructor : XunitDiagnosticAnalyzer { public SerializableClassMustHaveParameterlessConstructor() : base(Descriptors.X3001_SerializableClassMustHaveParameterlessConstructor) { } public override void AnalyzeCompilation( CompilationStartAnalysisContext context, XunitContext xunitContext) { Guard.ArgumentNotNull(context); Guard.ArgumentNotNull(xunitContext); context.RegisterSymbolAction(context => { if (context.Symbol is not INamedTypeSymbol namedType) return; if (namedType.TypeKind != TypeKind.Class) return; if (namedType.IsAbstract) return; var serializableTargetDisplay = GetSerializableTargetDisplay(xunitContext, namedType); if (serializableTargetDisplay is null) return; var parameterlessCtor = namedType.InstanceConstructors.FirstOrDefault(c => c.Parameters.IsEmpty); if (parameterlessCtor is not null && parameterlessCtor.DeclaredAccessibility == Accessibility.Public) return; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.IsCtorObsolete] = serializableTargetDisplay.Value.IsCtorObsolete ? bool.TrueString : bool.FalseString; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X3001_SerializableClassMustHaveParameterlessConstructor, namedType.Locations.First(), builder.ToImmutable(), namedType.Name, serializableTargetDisplay.Value.DisplayName ) ); }, SymbolKind.NamedType); } static (string DisplayName, bool IsCtorObsolete)? GetSerializableTargetDisplay( XunitContext xunitContext, INamedTypeSymbol namedType) { // Types that implement IRunnerReporter (v3 only) if (xunitContext.V3RunnerCommon?.IRunnerReporterType?.IsAssignableFrom(namedType) == true) return (xunitContext.V3RunnerCommon.IRunnerReporterType.ToDisplayString(), false); // Types that implement IXunitSerializable if (xunitContext.Common.IXunitSerializableType?.IsAssignableFrom(namedType) == true) return (xunitContext.Common.IXunitSerializableType.ToDisplayString(), true); // Types that implement IXunitSerializer if (xunitContext.V3Common?.IXunitSerializerType?.IsAssignableFrom(namedType) == true) return (xunitContext.V3Common.IXunitSerializerType.ToDisplayString(), false); // Types that decorate with [JsonTypeID] if (xunitContext.V3Core?.JsonTypeIDAttributeType is INamedTypeSymbol jsonTypeIDAttributeType) if (namedType.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, jsonTypeIDAttributeType))) return (jsonTypeIDAttributeType.ToDisplayString(), true); return null; } } ================================================ FILE: src/xunit.analyzers/xunit.analyzers.csproj ================================================ Xunit.Analyzers netstandard2.0 ================================================ FILE: src/xunit.analyzers/xunit.analyzers.nuspec ================================================ xunit.analyzers $PackageVersion$ xUnit.net [Code Analyzers] jnewkirk,bradwilson,marcind false Apache-2.0 https://licenses.nuget.org/Apache-2.0 _content/logo-128-transparent.png xUnit.net is a developer testing framework, built to support Test Driven Development, with a design goal of extreme simplicity and alignment with framework features. Installing this package provides code analyzers to help developers find and fix frequent issues when writing tests and xUnit.net extensibility code. Copyright (C) .NET Foundation true xunit.analyzers, analyzers, roslyn, xunit, xunit.net _content/README.md https://xunit.net/releases/analyzers/$PackageVersion$ ================================================ FILE: src/xunit.analyzers.fixes/Properties/AssemblyInfo.cs ================================================ using System.Reflection; [assembly: AssemblyCompany(".NET Foundation")] [assembly: AssemblyProduct("xUnit.net Testing Framework")] [assembly: AssemblyCopyright("Copyright (C) .NET Foundation")] [assembly: AssemblyTitle("xUnit.net Code Analysis (Fixers)")] ================================================ FILE: src/xunit.analyzers.fixes/Utility/AsyncHelper.cs ================================================ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; public static class AsyncHelper { static TypeSyntax? ConvertActionTypeToAsyncFunctionType( INamedTypeSymbol declarationTypeSymbol, Compilation compilation, DocumentEditor editor) { var taskTypeSymbol = TypeSymbolFactory.Task(compilation); if (taskTypeSymbol is null) return null; var unboundFunctionTypeSymbol = TypeSymbolFactory.Func(compilation, declarationTypeSymbol.Arity + 1); if (unboundFunctionTypeSymbol is null) return null; var typeArgumentsLength = declarationTypeSymbol.TypeArguments.Length + 1; var typeArguments = new ITypeSymbol[typeArgumentsLength]; var returnTypeIndex = typeArgumentsLength - 1; declarationTypeSymbol.TypeArguments.CopyTo(typeArguments); typeArguments[returnTypeIndex] = taskTypeSymbol; var constructedFunctionTypeSymbol = unboundFunctionTypeSymbol .Construct([.. typeArguments]) .WithNullableAnnotation(declarationTypeSymbol.NullableAnnotation); return editor.Generator.TypeExpression(constructedFunctionTypeSymbol) as TypeSyntax; } static TypeSyntax? ConvertFunctionTypeToAsyncFunctionType( INamedTypeSymbol declarationTypeSymbol, Compilation compilation, DocumentEditor editor) { var taskTypeSymbol = TypeSymbolFactory.Task(compilation); if (taskTypeSymbol is null) return null; var unboundTaskTypeSymbol = TypeSymbolFactory.TaskOfT(compilation); if (unboundTaskTypeSymbol is null) return null; var unboundFunctionTypeSymbol = TypeSymbolFactory.Func(compilation, declarationTypeSymbol.Arity); if (unboundFunctionTypeSymbol is null) return null; var returnTypeIndex = declarationTypeSymbol.TypeArguments.Length - 1; var returnTypeSymbol = declarationTypeSymbol.TypeArguments[returnTypeIndex]; // Function return type is already a task. if (taskTypeSymbol.IsAssignableFrom(returnTypeSymbol)) return null; var typeArguments = declarationTypeSymbol.TypeArguments.ToArray(); var constructedTaskTypeSymbol = unboundTaskTypeSymbol.Construct(returnTypeSymbol); typeArguments[returnTypeIndex] = constructedTaskTypeSymbol; var constructedFunctionTypeSymbol = unboundFunctionTypeSymbol .Construct(typeArguments) .WithNullableAnnotation(declarationTypeSymbol.NullableAnnotation); return editor.Generator.TypeExpression(constructedFunctionTypeSymbol) as TypeSyntax; } /// /// Get a list of modifiers with the keyword added if not already present. /// public static SyntaxTokenList GetModifiersWithAsyncKeywordAdded(SyntaxTokenList modifiers) { return modifiers.Any(SyntaxKind.AsyncKeyword) ? modifiers : modifiers.Add(Token(SyntaxKind.AsyncKeyword)); } /// /// Convert a return type to the corresponding async return type, if possible. /// /// If the return type is already a or , then is returned. /// If the return type is , then a return type is returned. /// If the return type is another type, then a of that type is returned. /// However, if symbols cannot be accessed or created through the semantic model, then is returned. /// /// public static async Task GetAsyncReturnType( TypeSyntax returnType, DocumentEditor editor, CancellationToken cancellationToken) { Guard.ArgumentNotNull(returnType); Guard.ArgumentNotNull(editor); var semanticModel = await editor.OriginalDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (semanticModel is not null && semanticModel.GetSymbolInfo(returnType, cancellationToken).Symbol is ITypeSymbol returnTypeSymbol && TypeSymbolFactory.Task(semanticModel.Compilation) is INamedTypeSymbol taskTypeSymbol) { if (returnType is PredefinedTypeSyntax predefinedReturnType && predefinedReturnType.Keyword.IsKind(SyntaxKind.VoidKeyword)) return editor.Generator.TypeExpression(taskTypeSymbol) as TypeSyntax; // Return type is already a task. if (taskTypeSymbol.IsAssignableFrom(returnTypeSymbol)) return null; if (TypeSymbolFactory.TaskOfT(semanticModel.Compilation) is INamedTypeSymbol unboundTaskTypeSymbol) { var constructedTaskTypeSymbol = unboundTaskTypeSymbol.Construct(returnTypeSymbol); return editor.Generator.TypeExpression(constructedTaskTypeSymbol) as TypeSyntax; } } return null; } /// /// Convert an anonymous function's declaration type to the corresponding async system delegate type, if possible. /// /// If the anonymous function's declaration type is not , and if it is a delegate type, /// and if it is equal to the anonymous function's inferred converted type (a system delegate type such as /// , , , etc.), then the converted /// async system delegate type is returned. Otherwise, or if symbols cannot be accessed or created through /// the semantic model, then is returned. /// /// /// is converted to a returning , /// and is converted to a returning , etc. /// /// /// is converted to a returning , /// if it is not already, and is converted to a /// returning , if it is not already, etc. /// /// public static async Task GetAsyncSystemDelegateType( VariableDeclarationSyntax declaration, AnonymousFunctionExpressionSyntax anonymousFunction, DocumentEditor editor, CancellationToken cancellationToken) { Guard.ArgumentNotNull(declaration); Guard.ArgumentNotNull(anonymousFunction); Guard.ArgumentNotNull(editor); var semanticModel = await editor.OriginalDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (semanticModel is not null && semanticModel.GetTypeInfo(declaration.Type, cancellationToken).Type is INamedTypeSymbol declarationTypeSymbol && !declaration.Type.IsVar && declarationTypeSymbol.TypeKind == TypeKind.Delegate && semanticModel.GetTypeInfo(anonymousFunction, cancellationToken).ConvertedType is ITypeSymbol functionTypeSymbol && editor.Generator.TypeExpression(declarationTypeSymbol) is TypeSyntax declaredType && editor.Generator.TypeExpression(functionTypeSymbol) is TypeSyntax functionType && declaredType.IsEquivalentTo(functionType)) { var compilation = semanticModel.Compilation; if (IsSystemActionType(declarationTypeSymbol, compilation)) return ConvertActionTypeToAsyncFunctionType(declarationTypeSymbol, compilation, editor); if (IsSystemFunctionType(declarationTypeSymbol, compilation)) return ConvertFunctionTypeToAsyncFunctionType(declarationTypeSymbol, compilation, editor); } return null; } static bool IsSystemActionType( INamedTypeSymbol typeSymbol, Compilation compilation) { var arity = typeSymbol.Arity; if (typeSymbol.Name == "Action") { if (arity == 0) return SymbolEqualityComparer.Default.Equals(typeSymbol.ConstructedFrom, TypeSymbolFactory.Action(compilation)); if (arity is >= 1 and <= 16) return SymbolEqualityComparer.Default.Equals(typeSymbol.ConstructedFrom, TypeSymbolFactory.Action(compilation, arity)); } return false; } static bool IsSystemFunctionType( INamedTypeSymbol typeSymbol, Compilation compilation) { var arity = typeSymbol.Arity; if (typeSymbol.Name == "Func" && arity >= 1 && arity <= 17) return SymbolEqualityComparer.Default.Equals(typeSymbol.ConstructedFrom, TypeSymbolFactory.Func(compilation, arity)); return false; } } ================================================ FILE: src/xunit.analyzers.fixes/Utility/CodeAnalysisExtensions.cs ================================================ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; public static class CodeAnalysisExtensions { public static async Task AddConstructor( this Document document, ClassDeclarationSyntax declaration, string typeDisplayName, string typeName, CancellationToken cancellationToken) { Guard.ArgumentNotNull(document); Guard.ArgumentNotNull(declaration); Guard.ArgumentNotNull(typeDisplayName); Guard.ArgumentNotNull(typeName); #pragma warning disable CA1308 // These are display names, not normalizations for comparison // TODO: Make this respect the user's preferences on identifier name style var fieldName = "_" + typeName.Substring(0, 1).ToLowerInvariant() + typeName.Substring(1); var constructorArgName = typeName.Substring(0, 1).ToLowerInvariant() + typeName.Substring(1); var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); #pragma warning restore CA1308 var fieldDeclaration = FieldDeclaration( VariableDeclaration(ParseTypeName(typeDisplayName)) .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(fieldName)))) ) .WithModifiers(TokenList(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ReadOnlyKeyword))); var constructor = ConstructorDeclaration(Identifier(declaration.Identifier.Text)) .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) .WithParameterList( ParameterList( SingletonSeparatedList( Parameter(Identifier(constructorArgName)) .WithType(ParseTypeName(typeDisplayName)) ) ) ) .WithBody( Block( SingletonList( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(fieldName), IdentifierName(constructorArgName) ) ) ) ) ); editor.InsertMembers(declaration, 0, [fieldDeclaration, constructor]); return editor.GetChangedDocument(); } public static async Task ChangeAccessibility( this Document document, SyntaxNode declaration, Accessibility accessibility, CancellationToken cancellationToken) { Guard.ArgumentNotNull(document); Guard.ArgumentNotNull(declaration); var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.SetAccessibility(declaration, accessibility); return editor.GetChangedDocument(); } public static async Task ChangeMemberAccessibility( this Solution solution, ISymbol memberSymbol, Accessibility accessibility, CancellationToken cancellationToken) { Guard.ArgumentNotNull(solution); Guard.ArgumentNotNull(memberSymbol); var editor = SymbolEditor.Create(solution); await editor.EditAllDeclarationsAsync( memberSymbol, (docEditor, syntaxNode) => docEditor.SetAccessibility(syntaxNode, accessibility), cancellationToken ).ConfigureAwait(false); return editor.ChangedSolution; } public static async Task ChangeMemberStaticModifier( this Solution solution, ISymbol memberSymbol, bool isStatic, CancellationToken cancellationToken) { Guard.ArgumentNotNull(solution); Guard.ArgumentNotNull(memberSymbol); var editor = SymbolEditor.Create(solution); await editor.EditAllDeclarationsAsync( memberSymbol, (docEditor, syntaxNode) => { var newMods = DeclarationModifiers.From(memberSymbol).WithIsStatic(isStatic); if (memberSymbol is IPropertySymbol propertySymbol && propertySymbol.IsReadOnly) { // Looks like there's a bug in Roslyn where SetModifiers applies the 'readonly' // keyword to a get-only property, producing illegal syntax. newMods = newMods.WithIsReadOnly(false); } docEditor.SetModifiers(syntaxNode, newMods); }, cancellationToken ).ConfigureAwait(false); return editor.ChangedSolution; } public static async Task ChangeMemberType( this Solution solution, ISymbol memberSymbol, ITypeSymbol type, CancellationToken cancellationToken) { Guard.ArgumentNotNull(solution); Guard.ArgumentNotNull(memberSymbol); Guard.ArgumentNotNull(type); var editor = SymbolEditor.Create(solution); await editor.EditAllDeclarationsAsync( memberSymbol, (docEditor, syntaxNode) => docEditor.SetType(syntaxNode, docEditor.Generator.TypeExpression(type)), cancellationToken ).ConfigureAwait(false); return editor.ChangedSolution; } public static async Task RemoveNode( this Document document, SyntaxNode node, CancellationToken cancellationToken) { Guard.ArgumentNotNull(document); Guard.ArgumentNotNull(node); var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.RemoveNode(node); return editor.GetChangedDocument(); } public static async Task ExtractNodeFromParent( this Document document, SyntaxNode node, CancellationToken cancellationToken) { Guard.ArgumentNotNull(document); Guard.ArgumentNotNull(node); var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var parent = node.Parent; if (parent is not null) { editor.RemoveNode(node); var formattedNode = node .WithLeadingTrivia(ElasticMarker) .WithTrailingTrivia(ElasticMarker) .WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation); editor.InsertAfter(parent, formattedNode); } return editor.GetChangedDocument(); } public static async Task SetBaseClass( this Document document, ClassDeclarationSyntax declaration, string baseType, CancellationToken cancellationToken) { Guard.ArgumentNotNull(document); Guard.ArgumentNotNull(declaration); Guard.ArgumentNotNull(baseType); var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var generator = editor.Generator; var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (semanticModel is not null) { var baseTypeMetadata = semanticModel.Compilation.GetTypeByMetadataName(baseType); if (baseTypeMetadata is not null) { var baseTypeNode = generator.TypeExpression(baseTypeMetadata); var baseTypes = generator.GetBaseAndInterfaceTypes(declaration); var updatedDeclaration = baseTypes is null || baseTypes.Count == 0 || semanticModel.GetTypeInfo(baseTypes[0], cancellationToken).Type?.TypeKind != TypeKind.Class ? generator.AddBaseType(declaration, baseTypeNode) : generator.ReplaceNode(declaration, baseTypes[0], baseTypeNode); editor.ReplaceNode(declaration, updatedDeclaration); } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/Utility/ConvertAttributeCodeAction.cs ================================================ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; namespace Xunit.Analyzers.Fixes; public class ConvertAttributeCodeAction( string title, string equivalenceKey, Document document, SyntaxList attributeLists, string fromTypeName, string toTypeName) : CodeAction { readonly Document document = Guard.ArgumentNotNull(document); readonly string fromTypeName = Guard.ArgumentNotNull(fromTypeName); readonly string toTypeName = Guard.ArgumentNotNull(toTypeName); public override string EquivalenceKey { get; } = Guard.ArgumentNotNull(equivalenceKey); public override string Title { get; } = Guard.ArgumentNotNull(title); protected override async Task GetChangedDocumentAsync(CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (semanticModel is not null) { var fromTypeSymbol = semanticModel.Compilation.GetTypeByMetadataName(fromTypeName); if (fromTypeSymbol is not null) foreach (var attributeList in attributeLists) foreach (var attribute in attributeList.Attributes) { cancellationToken.ThrowIfCancellationRequested(); var currentType = semanticModel.GetTypeInfo(attribute, cancellationToken).Type; if (SymbolEqualityComparer.Default.Equals(currentType, fromTypeSymbol)) editor.SetName(attribute, toTypeName); } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/Utility/RemoveAttributesOfTypeCodeAction.cs ================================================ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; namespace Xunit.Analyzers.Fixes; public class RemoveAttributesOfTypeCodeAction( string title, string equivalenceKey, Document document, SyntaxList attributeLists, string attributeType, bool exactMatch = false) : CodeAction { readonly string attributeType = Guard.ArgumentNotNull(attributeType); readonly Document document = Guard.ArgumentNotNull(document); public override string EquivalenceKey { get; } = Guard.ArgumentNotNull(equivalenceKey); public override string Title { get; } = Guard.ArgumentNotNull(title); protected override async Task GetChangedDocumentAsync(CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (semanticModel is not null) { var dataAttributeType = semanticModel.Compilation.GetTypeByMetadataName(attributeType); if (dataAttributeType is not null) foreach (var attributeList in attributeLists) foreach (var attribute in attributeList.Attributes) if (dataAttributeType.IsAssignableFrom(semanticModel.GetTypeInfo(attribute, cancellationToken).Type, exactMatch)) editor.RemoveNode(attribute); } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/Utility/XunitCodeAction.cs ================================================ using System; using System.Globalization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; namespace Microsoft.CodeAnalysis.CodeActions; internal static class XunitCodeAction { public static CodeAction Create( Func> createChangedDocument, string equivalenceKey, string title) => CodeAction.Create( title, createChangedDocument, equivalenceKey ); #pragma warning disable RS1035 // The prohibiton on CultureInfo.CurrentCulture does not apply to fixers public static CodeAction Create( Func> createChangedDocument, string equivalenceKey, string titleFormat, object? arg0) => CodeAction.Create( string.Format(CultureInfo.CurrentCulture, titleFormat, arg0), createChangedDocument, equivalenceKey ); public static CodeAction Create( Func> createChangedDocument, string equivalenceKey, string titleFormat, object? arg0, object? arg1) => CodeAction.Create( string.Format(CultureInfo.CurrentCulture, titleFormat, arg0, arg1), createChangedDocument, equivalenceKey ); public static CodeAction Create( Func> createChangedDocument, string equivalenceKey, string titleFormat, object? arg0, object? arg1, object? arg2) => CodeAction.Create( string.Format(CultureInfo.CurrentCulture, titleFormat, arg0, arg1, arg2), createChangedDocument, equivalenceKey ); public static CodeAction Create( Func> createChangedDocument, string equivalenceKey, string titleFormat, params object?[] args) => CodeAction.Create( string.Format(CultureInfo.CurrentCulture, titleFormat, args), createChangedDocument, equivalenceKey ); #pragma warning restore RS1035 public static CodeAction UseDifferentAssertMethod( string equivalenceKey, Document document, InvocationExpressionSyntax invocation, string replacementMethod) => CodeAction.Create( "Use Assert." + replacementMethod, async ct => { var editor = await DocumentEditor.CreateAsync(document, ct).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (editor.Generator.IdentifierName(replacementMethod) is SimpleNameSyntax replacementNameSyntax) editor.ReplaceNode(memberAccess, memberAccess.WithName(replacementNameSyntax)); return editor.GetChangedDocument(); }, equivalenceKey ); } ================================================ FILE: src/xunit.analyzers.fixes/Utility/XunitCodeFixProvider.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeFixes; namespace Xunit.Analyzers.Fixes; public abstract class XunitCodeFixProvider(params string[] diagnostics) : CodeFixProvider { #pragma warning disable IDE0305 // Cannot convert this due to Roslyn 3.11 public sealed override ImmutableArray FixableDiagnosticIds { get; } = diagnostics.ToImmutableArray(); #pragma warning restore IDE0305 public override FixAllProvider? GetFixAllProvider() => null; } ================================================ FILE: src/xunit.analyzers.fixes/Utility/XunitMemberFixProvider.cs ================================================ using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; namespace Xunit.Analyzers.Fixes; public abstract class XunitMemberFixProvider(params string[] diagnostics) : XunitCodeFixProvider(diagnostics) { public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.DeclaringType, out var declaringTypeName)) return; if (declaringTypeName is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.MemberName, out var memberName)) return; if (memberName is null) return; var declaringType = semanticModel.Compilation.GetTypeByMetadataName(declaringTypeName); if (declaringType is null) return; var member = declaringType.GetMembers(memberName).FirstOrDefault(); if (member is null) return; if (member.Locations.FirstOrDefault()?.IsInMetadata ?? true) return; await RegisterCodeFixesAsync(context, member).ConfigureAwait(false); } public abstract Task RegisterCodeFixesAsync( CodeFixContext context, ISymbol member); } ================================================ FILE: src/xunit.analyzers.fixes/X1000/ClassDataAttributeMustPointAtValidClassFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class ClassDataAttributeMustPointAtValidClassFixer : XunitCodeFixProvider { public const string Key_FixDataClass = "xUnit1007_FixDataClass"; public ClassDataAttributeMustPointAtValidClassFixer() : base(Descriptors.X1007_ClassDataAttributeMustPointAtValidClass.Id) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is null) return; // The context wraps "ClassData(typeof(T))", which is an attribute syntax var attribute = root.FindNode(context.Span); // Dig in to find the attribute arguments var attributeArgumentList = attribute.ChildNodes().OfType().FirstOrDefault(); if (attributeArgumentList is null) return; // Dig into that to get the attribute argument var attributeArgument = attributeArgumentList.ChildNodes().OfType().FirstOrDefault(); if (attributeArgument is null) return; // And finally, dig in to get the typeof expression var typeOfExpression = attributeArgument.ChildNodes().OfType().FirstOrDefault(); if (typeOfExpression is null) return; if (semanticModel.GetTypeInfo(typeOfExpression.Type, context.CancellationToken).Type is INamedTypeSymbol typeSymbol) if (typeSymbol.TypeKind == TypeKind.Class && typeSymbol.Locations.Any(l => l.IsInSource)) context.RegisterCodeFix( CodeAction.Create( "Fix data class", ct => FixClass(context.Document.Project.Solution, typeSymbol, ct), Key_FixDataClass ), context.Diagnostics ); } static async Task FixClass( Solution solution, INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) { var symbolEditor = SymbolEditor.Create(solution); await symbolEditor.EditOneDeclarationAsync(typeSymbol, async (editor, declaration, ct) => { var classDeclaration = (ClassDeclarationSyntax)declaration; var compilation = editor.SemanticModel.Compilation; var generator = editor.Generator; if (typeSymbol.IsAbstract) editor.SetModifiers(declaration, DeclarationModifiers.From(typeSymbol).WithIsAbstract(false)); var ctor = typeSymbol.InstanceConstructors.FirstOrDefault(c => c.Parameters.Length == 0); if (ctor is null) editor.AddMember(classDeclaration, generator.ConstructorDeclaration(accessibility: Accessibility.Public)); else if (ctor.DeclaredAccessibility != Accessibility.Public && !(ctor.IsImplicitlyDeclared && typeSymbol.IsAbstract)) { // Make constructor public unless it's implicit and the class was abstract. Making the class non-abstract will make the implicit constructor public var ctorSyntaxRef = ctor.DeclaringSyntaxReferences.FirstOrDefault(); if (ctorSyntaxRef is not null) editor.SetAccessibility(await ctorSyntaxRef.GetSyntaxAsync(ct).ConfigureAwait(false), Accessibility.Public); } var iEnumerableOfObjectArray = TypeSymbolFactory.IEnumerableOfObjectArray(compilation); if (!iEnumerableOfObjectArray.IsAssignableFrom(typeSymbol)) editor.AddInterfaceType(classDeclaration, generator.TypeExpression(iEnumerableOfObjectArray)); }, cancellationToken).ConfigureAwait(false); return symbolEditor.ChangedSolution; } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/CollectionDefinitionClassesMustBePublicFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class CollectionDefinitionClassesMustBePublicFixer : XunitCodeFixProvider { public const string Key_MakeCollectionDefinitionClassPublic = "xUnit1027_MakeCollectionDefinitionClassPublic"; public CollectionDefinitionClassesMustBePublicFixer() : base(Descriptors.X1027_CollectionDefinitionClassMustBePublic.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var classDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (classDeclaration is null) return; context.RegisterCodeFix( CodeAction.Create( "Make collection definition class public", ct => context.Document.ChangeAccessibility(classDeclaration, Accessibility.Public, ct), Key_MakeCollectionDefinitionClassPublic ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/ConvertToFactFix.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class ConvertToFactFix : XunitCodeFixProvider { public const string Key_ConvertToFact = "xUnit1003_xUnit1006_ConvertToFact"; public ConvertToFactFix() : base( Descriptors.X1003_TheoryMethodMustHaveTestData.Id, Descriptors.X1006_TheoryMethodShouldHaveParameters.Id ) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (methodDeclaration is null) return; context.RegisterCodeFix( new ConvertAttributeCodeAction( "Convert to [Fact]", Key_ConvertToFact, context.Document, methodDeclaration.AttributeLists, fromTypeName: Constants.Types.Xunit.TheoryAttribute, toTypeName: Constants.Types.Xunit.FactAttribute ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/ConvertToTheoryFix.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class ConvertToTheoryFix : XunitCodeFixProvider { public const string Key_ConvertToTheory = "xUnit1001_xUnit1005_ConvertToTheory"; public ConvertToTheoryFix() : base( Descriptors.X1001_FactMethodMustNotHaveParameters.Id, Descriptors.X1005_FactMethodShouldNotHaveTestData.Id ) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (methodDeclaration is null) return; context.RegisterCodeFix( new ConvertAttributeCodeAction( "Convert to [Theory]", Key_ConvertToTheory, context.Document, methodDeclaration.AttributeLists, fromTypeName: Constants.Types.Xunit.FactAttribute, toTypeName: Constants.Types.Xunit.TheoryAttribute ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/DataAttributeShouldBeUsedOnATheoryFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class DataAttributeShouldBeUsedOnATheoryFixer : XunitCodeFixProvider { public const string Key_MarkAsTheory = "xUnit1008_MarkAsTheory"; public const string Key_RemoveDataAttributes = "xUnit1008_RemoveDataAttributes"; public DataAttributeShouldBeUsedOnATheoryFixer() : base(Descriptors.X1008_DataAttributeShouldBeUsedOnATheory.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (methodDeclaration is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.DataAttributeTypeName, out var dataAttributeTypeName) || dataAttributeTypeName is null) return; context.RegisterCodeFix( CodeAction.Create( "Mark as [Theory]", ct => MarkAsTheoryAsync(context.Document, methodDeclaration, ct), Key_MarkAsTheory ), context.Diagnostics ); context.RegisterCodeFix( new RemoveAttributesOfTypeCodeAction( "Remove data attributes", Key_RemoveDataAttributes, context.Document, methodDeclaration.AttributeLists, dataAttributeTypeName ), context.Diagnostics ); } static async Task MarkAsTheoryAsync( Document document, MethodDeclarationSyntax methodDeclaration, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.ReplaceNode( methodDeclaration, (node, generator) => generator.InsertAttributes(node, 0, generator.Attribute(Constants.Types.Xunit.TheoryAttribute)) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/DoNotUseAsyncVoidForTestMethodsFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class DoNotUseAsyncVoidForTestMethodsFixer : XunitMemberFixProvider { public const string Key_ConvertToTask = "xUnit1048_xUnit1049_ConvertToTask"; public const string Key_ConvertToValueTask = "xUnit1049_ConvertToValueTask"; public DoNotUseAsyncVoidForTestMethodsFixer() : base( Descriptors.X1048_DoNotUseAsyncVoidForTestMethods_V2.Id, Descriptors.X1049_DoNotUseAsyncVoidForTestMethods_V3.Id ) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public override async Task RegisterCodeFixesAsync( CodeFixContext context, ISymbol member) { var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is null) return; var taskReturnType = TypeSymbolFactory.Task(semanticModel.Compilation); if (taskReturnType is null) return; var valueTaskReturnType = TypeSymbolFactory.ValueTask(semanticModel.Compilation); foreach (var diagnostic in context.Diagnostics) { context.RegisterCodeFix( CodeAction.Create( "Change return type to Task", ct => context.Document.Project.Solution.ChangeMemberType(member, taskReturnType, ct), Key_ConvertToTask ), diagnostic ); if (valueTaskReturnType is not null && diagnostic.Id == Descriptors.X1049_DoNotUseAsyncVoidForTestMethods_V3.Id) context.RegisterCodeFix( CodeAction.Create( "Change return type to ValueTask", ct => context.Document.Project.Solution.ChangeMemberType(member, valueTaskReturnType, ct), Key_ConvertToValueTask ), diagnostic ); } } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/DoNotUseConfigureAwaitFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class DoNotUseConfigureAwaitFixer : XunitCodeFixProvider { public const string Key_RemoveConfigureAwait = "xUnit1030_RemoveConfigureAwait"; public const string Key_ReplaceArgumentValue = "xUnit1030_ReplaceArgumentValue"; public DoNotUseConfigureAwaitFixer() : base(Descriptors.X1030_DoNotUseConfigureAwait.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; // Get the original and replacement values if (!diagnostic.Properties.TryGetValue(Constants.Properties.ArgumentValue, out var original)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; // The syntax node (the invocation) will include "(any preceding trivia)(any preceding code).ConfigureAwait(args)" despite // the context.Span only covering "ConfigureAwait(args)". So we need to replace the whole invocation // with an invocation that does not include the ConfigureAwait call. var syntaxNode = root.FindNode(context.Span); var syntaxText = syntaxNode.ToFullString(); // Remove the context span (plus the preceding .) var removeConfigureAwaitText = syntaxText.Substring(0, context.Span.Start - syntaxNode.FullSpan.Start - 1); var removeConfigureAwaitNode = SyntaxFactory.ParseExpression(removeConfigureAwaitText); // Only offer the removal fix if the replacement value is 'true', because anybody using ConfigureAwaitOptions // will want to just add the extra value, not remove the call entirely. if (replacement == "true") context.RegisterCodeFix( CodeAction.Create( "Remove ConfigureAwait call", async ct => { var editor = await DocumentEditor.CreateAsync(context.Document, ct).ConfigureAwait(false); editor.ReplaceNode(syntaxNode, removeConfigureAwaitNode); return editor.GetChangedDocument(); }, Key_RemoveConfigureAwait ), context.Diagnostics ); // Offer the replacement fix var replaceConfigureAwaitText = removeConfigureAwaitText + ".ConfigureAwait(" + replacement + ")"; var replaceConfigureAwaitNode = SyntaxFactory.ParseExpression(replaceConfigureAwaitText); context.RegisterCodeFix( XunitCodeAction.Create( async ct => { var editor = await DocumentEditor.CreateAsync(context.Document, ct).ConfigureAwait(false); editor.ReplaceNode(syntaxNode, replaceConfigureAwaitNode); return editor.GetChangedDocument(); }, Key_ReplaceArgumentValue, "Replace ConfigureAwait({0}) with ConfigureAwait({1})", original, replacement ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/FactMethodMustNotHaveParametersFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class FactMethodMustNotHaveParametersFixer : XunitCodeFixProvider { public const string Key_RemoveParameters = "xUnit1001_RemoveParameters"; public FactMethodMustNotHaveParametersFixer() : base(Descriptors.X1001_FactMethodMustNotHaveParameters.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (methodDeclaration is null) return; context.RegisterCodeFix( CodeAction.Create( "Remove parameters", ct => RemoveParameters(context.Document, methodDeclaration.ParameterList, ct), Key_RemoveParameters ), context.Diagnostics ); } static async Task RemoveParameters( Document document, ParameterListSyntax parameterListSyntax, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); foreach (var parameter in parameterListSyntax.Parameters) editor.RemoveNode(parameter); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/FactMethodShouldNotHaveTestDataFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class FactMethodShouldNotHaveTestDataFixer : XunitCodeFixProvider { public const string Key_RemoveDataAttributes = "xUnit1005_RemoveDataAttributes"; public FactMethodShouldNotHaveTestDataFixer() : base(Descriptors.X1005_FactMethodShouldNotHaveTestData.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (methodDeclaration is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.DataAttributeTypeName, out var dataAttributeTypeName) || dataAttributeTypeName is null) return; context.RegisterCodeFix( new RemoveAttributesOfTypeCodeAction( "Remove data attributes", Key_RemoveDataAttributes, context.Document, methodDeclaration.AttributeLists, dataAttributeTypeName ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/InlineDataMustMatchTheoryParameters_ExtraValueFixer.cs ================================================ using System; using System.Collections.Immutable; using System.Composition; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class InlineDataMustMatchTheoryParameters_ExtraValueFixer : XunitCodeFixProvider { public override FixAllProvider? GetFixAllProvider() => BatchFixer; public const string Key_AddTheoryParameter = "xUnit1011_AddTheoryParameter"; public const string Key_RemoveExtraDataValue = "xUnit1011_RemoveExtraDataValue"; public InlineDataMustMatchTheoryParameters_ExtraValueFixer() : base(Descriptors.X1011_InlineDataMustMatchTheoryParameters_ExtraValue.Id) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var node = root.FindNode(context.Span); var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; // Fix #1: remove the extra data from the inline data attribute context.RegisterCodeFix( CodeAction.Create( "Remove extra data value", ct => context.Document.RemoveNode(node, ct), Key_RemoveExtraDataValue ), context.Diagnostics ); // Fix #2: add a parameter to the theory for the extra data // (only valid for the first item after the supported parameters are exhausted) var method = node.FirstAncestorOrSelf(); if (method is null) return; var parameterIndexText = diagnostic.Properties[Constants.Properties.ParameterIndex]; if (parameterIndexText is not null) { var parameterIndex = int.Parse(parameterIndexText, CultureInfo.InvariantCulture); if (!Enum.TryParse(diagnostic.Properties[Constants.Properties.ParameterSpecialType], out var parameterSpecialType)) return; var existingParameters = method.ParameterList.Parameters.Select(p => p.Identifier.Text).ToImmutableHashSet(); var parameterName = "p"; var nextIndex = 2; while (existingParameters.Contains(parameterName)) parameterName = $"p_{nextIndex++}"; if (method.ParameterList.Parameters.Count == parameterIndex) context.RegisterCodeFix( CodeAction.Create( "Add theory parameter", ct => AddTheoryParameter(context.Document, method, parameterSpecialType, parameterName, ct), Key_AddTheoryParameter ), context.Diagnostics ); } } static async Task AddTheoryParameter( Document document, MethodDeclarationSyntax method, SpecialType parameterSpecialType, string parameterName, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var parameterTypeExpression = parameterSpecialType != SpecialType.None ? editor.Generator.TypeExpression(parameterSpecialType) : editor.Generator.TypeExpression(SpecialType.System_Object); editor.AddParameter( method, editor.Generator.ParameterDeclaration(parameterName, parameterTypeExpression) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameterFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameterFixer : XunitCodeFixProvider { public const string Key_MakeParameterNullable = "xUnit1012_MakeParameterNullable"; public InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameterFixer() : base(Descriptors.X1012_InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameter.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var node = root.FindNode(context.Span); var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.ParameterIndex, out var parameterIndexText)) return; if (!int.TryParse(parameterIndexText, out var parameterIndex)) return; var diagnosticId = diagnostic.Id; var method = node.FirstAncestorOrSelf(); if (method is null) return; context.RegisterCodeFix( CodeAction.Create( "Make parameter nullable", ct => MakeParameterNullable(context.Document, method, parameterIndex, ct), Key_MakeParameterNullable ), context.Diagnostics ); } static async Task MakeParameterNullable( Document document, MethodDeclarationSyntax method, int parameterIndex, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (method.ParameterList.Parameters.Count > parameterIndex) { var param = method.ParameterList.Parameters[parameterIndex]; var semanticModel = editor.SemanticModel; if (semanticModel is not null && param.Type is not null) { var paramTypeSymbol = semanticModel.GetTypeInfo(param.Type, cancellationToken).Type; if (paramTypeSymbol is not null) { var nullableT = paramTypeSymbol.IsReferenceType ? paramTypeSymbol.WithNullableAnnotation(NullableAnnotation.Annotated) : TypeSymbolFactory.NullableOfT(semanticModel.Compilation).Construct(paramTypeSymbol); editor.SetType(param, editor.Generator.TypeExpression(nullableT)); } } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/InlineDataMustMatchTheoryParameters_TooFewValuesFixer.cs ================================================ using System; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class InlineDataMustMatchTheoryParameters_TooFewValuesFixer : XunitCodeFixProvider { public const string Key_AddDefaultValues = "xUnit1009_AddDefaultValues"; public InlineDataMustMatchTheoryParameters_TooFewValuesFixer() : base(Descriptors.X1009_InlineDataMustMatchTheoryParameters_TooFewValues.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var node = root.FindNode(context.Span); if (node is not AttributeSyntax attribute) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.ParameterArrayStyle, out var arrayStyleText)) return; var diagnosticId = diagnostic.Id; var method = node.FirstAncestorOrSelf(); if (method is null) return; if (!Enum.TryParse(arrayStyleText, out var arrayStyle)) return; context.RegisterCodeFix( CodeAction.Create( "Add default values", ct => AddDefaultValues(context.Document, attribute, method, arrayStyle, ct), Key_AddDefaultValues ), context.Diagnostics ); } static async Task AddDefaultValues( Document document, AttributeSyntax attribute, MethodDeclarationSyntax method, InlineDataMustMatchTheoryParameters.ParameterArrayStyleType arrayStyle, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var arrayInitializer = default(InitializerExpressionSyntax); if (arrayStyle == InlineDataMustMatchTheoryParameters.ParameterArrayStyleType.Initializer) arrayInitializer = attribute.DescendantNodes().First(n => n.IsKind(SyntaxKind.ArrayInitializerExpression)) as InitializerExpressionSyntax; var originalInitializer = arrayInitializer; var i = originalInitializer?.Expressions.Count ?? attribute.ArgumentList?.Arguments.Count ?? 0; for (; i < method.ParameterList.Parameters.Count; i++) if (CreateDefaultValueSyntax(editor, method.ParameterList.Parameters[i].Type) is ExpressionSyntax defaultExpression) { if (arrayInitializer is not null) arrayInitializer = arrayInitializer.AddExpressions(defaultExpression); else editor.AddAttributeArgument(attribute, defaultExpression); } if (originalInitializer is not null && arrayInitializer is not null) editor.ReplaceNode(originalInitializer, arrayInitializer); return editor.GetChangedDocument(); } static SyntaxNode CreateDefaultValueSyntax( DocumentEditor editor, TypeSyntax? type) { if (type is null) return editor.Generator.NullLiteralExpression(); var t = editor.SemanticModel.GetTypeInfo(type).Type; if (t is null) return editor.Generator.NullLiteralExpression(); switch (t.SpecialType) { case SpecialType.System_Boolean: return editor.Generator.FalseLiteralExpression(); case SpecialType.System_Char: return editor.Generator.LiteralExpression(default(char)); case SpecialType.System_Double: return editor.Generator.LiteralExpression(default(double)); case SpecialType.System_Single: return editor.Generator.LiteralExpression(default(float)); case SpecialType.System_UInt32: case SpecialType.System_UInt64: return editor.Generator.LiteralExpression(default(uint)); case SpecialType.System_Byte: case SpecialType.System_Decimal: case SpecialType.System_Int16: case SpecialType.System_Int32: case SpecialType.System_Int64: case SpecialType.System_SByte: case SpecialType.System_UInt16: return editor.Generator.LiteralExpression(default(int)); case SpecialType.System_String: return editor.Generator.LiteralExpression(string.Empty); } if (t.TypeKind == TypeKind.Enum) return editor.Generator.DefaultExpression(t); return editor.Generator.NullLiteralExpression(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/InlineDataShouldBeUniqueWithinTheoryFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class InlineDataShouldBeUniqueWithinTheoryFixer : XunitCodeFixProvider { public const string Key_RemoveDuplicateInlineData = "xUnit1025_RemoveDuplicateInlineData"; public InlineDataShouldBeUniqueWithinTheoryFixer() : base(Descriptors.X1025_InlineDataShouldBeUniqueWithinTheory.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var reportedNode = root.FindNode(context.Span); if (reportedNode is AttributeSyntax attributeDuplicate) context.RegisterCodeFix( CodeAction.Create( "Remove duplicate InlineData", ct => RemoveInlineDataDuplicate(context.Document, attributeDuplicate, ct), Key_RemoveDuplicateInlineData ), context.Diagnostics ); } static async Task RemoveInlineDataDuplicate( Document document, AttributeSyntax attributeDuplicate, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.RemoveNode(attributeDuplicate); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/LocalFunctionsCannotBeTestFunctionsFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class LocalFunctionsCannotBeTestFunctionsFixer : XunitCodeFixProvider { public const string Key_RemoveAttribute = "xUnit1029_RemoveAttribute"; public LocalFunctionsCannotBeTestFunctionsFixer() : base(Descriptors.X1029_LocalFunctionsCannotBeTestFunctions.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var localFunctionDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (localFunctionDeclaration is null) return; context.RegisterCodeFix( new RemoveAttributesOfTypeCodeAction( "Remove attribute", Key_RemoveAttribute, context.Document, localFunctionDeclaration.AttributeLists, Constants.Types.Xunit.FactAttribute ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/MemberDataShouldReferenceValidMember_ExtraValueFixer.cs ================================================ using System; using System.Collections.Immutable; using System.Composition; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class MemberDataShouldReferenceValidMember_ExtraValueFixer : XunitCodeFixProvider { public override FixAllProvider? GetFixAllProvider() => BatchFixer; public const string Key_AddMethodParameter = "xUnit1036_AddMethodParameter"; public const string Key_RemoveExtraDataValue = "xUnit1036_RemoveExtraDataValue"; public MemberDataShouldReferenceValidMember_ExtraValueFixer() : base(Descriptors.X1036_MemberDataArgumentsMustMatchMethodParameters_ExtraValue.Id) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var node = root.FindNode(context.Span); var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; // Fix #1: remove the extra data from the member data attribute context.RegisterCodeFix( CodeAction.Create( "Remove extra data value", ct => context.Document.RemoveNode(node, ct), Key_RemoveExtraDataValue ), context.Diagnostics ); // Fix #2: add a parameter to the theory for the extra data // (only valid for the first item after the supported parameters are exhausted) var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is null) return; var attributeList = node.FirstAncestorOrSelf(); if (attributeList is null) return; var propertyAttributeParameters = attributeList .Arguments .Count(a => !string.IsNullOrEmpty(a.NameEquals?.Name.Identifier.ValueText)); var paramsCount = attributeList.Arguments.Count - 1 - propertyAttributeParameters; (_, var declaredMemberTypeSymbol) = MemberDataShouldReferenceValidMember.GetClassTypesForAttribute( attributeList, semanticModel, context.CancellationToken); if (declaredMemberTypeSymbol is null) return; var memberName = diagnostic.Properties[Constants.Properties.MemberName]; if (memberName is null) return; IMethodSymbol? methodSymbol = null; while (paramsCount >= 0) { var memberSymbol = MemberDataShouldReferenceValidMember.FindMethodSymbol(memberName, declaredMemberTypeSymbol, paramsCount); methodSymbol = memberSymbol as IMethodSymbol; if (methodSymbol is not null) break; paramsCount--; } if (methodSymbol is null) return; var methodSyntaxes = methodSymbol.DeclaringSyntaxReferences; if (methodSyntaxes.Length != 1) return; if (await methodSyntaxes[0].GetSyntaxAsync().ConfigureAwait(false) is not MethodDeclarationSyntax method) return; var parameterIndexText = diagnostic.Properties[Constants.Properties.ParameterIndex]; if (parameterIndexText is not null) { var parameterIndex = int.Parse(parameterIndexText, CultureInfo.InvariantCulture); if (!Enum.TryParse(diagnostic.Properties[Constants.Properties.ParameterSpecialType], out var parameterSpecialType)) return; var existingParameters = method.ParameterList.Parameters.Select(p => p.Identifier.Text).ToImmutableHashSet(); var parameterName = "p"; var nextIndex = 2; while (existingParameters.Contains(parameterName)) parameterName = $"p_{nextIndex++}"; if (method.ParameterList.Parameters.Count == parameterIndex) context.RegisterCodeFix( CodeAction.Create( "Add method parameter", ct => AddMethodParameter(context.Document, method, parameterSpecialType, parameterName, ct), Key_AddMethodParameter ), context.Diagnostics ); } } static async Task AddMethodParameter( Document document, MethodDeclarationSyntax method, SpecialType parameterSpecialType, string parameterName, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var parameterTypeExpression = parameterSpecialType != SpecialType.None ? editor.Generator.TypeExpression(parameterSpecialType) : editor.Generator.TypeExpression(SpecialType.System_Object); editor.AddParameter( method, editor.Generator.ParameterDeclaration(parameterName, parameterTypeExpression) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/MemberDataShouldReferenceValidMember_NameOfFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class MemberDataShouldReferenceValidMember_NameOfFixer : XunitCodeFixProvider { public const string Key_UseNameof = "xUnit1014_UseNameof"; public MemberDataShouldReferenceValidMember_NameOfFixer() : base(Descriptors.X1014_MemberDataShouldUseNameOfOperator.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; var diagnosticId = diagnostic.Id; var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var attributeArgument = root.FindNode(context.Span).FirstAncestorOrSelf(); if (attributeArgument is null) return; if (attributeArgument.Expression is LiteralExpressionSyntax memberNameExpression) { var memberType = default(INamedTypeSymbol); if (diagnostic.Properties.TryGetValue(Constants.AttributeProperties.DeclaringType, out var memberTypeName) && memberTypeName is not null) { var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is not null) memberType = semanticModel.Compilation.GetTypeByMetadataName(memberTypeName); } context.RegisterCodeFix( CodeAction.Create( "Use nameof", ct => UseNameOf(context.Document, memberNameExpression, memberType, ct), Key_UseNameof ), context.Diagnostics ); } } static async Task UseNameOf( Document document, LiteralExpressionSyntax memberNameExpression, INamedTypeSymbol? memberType, CancellationToken cancellationToken) { var documentEditor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); documentEditor.ReplaceNode( memberNameExpression, (node, generator) => { var nameofParam = generator.IdentifierName(memberNameExpression.Token.ValueText); if (memberType is not null) nameofParam = generator.MemberAccessExpression(generator.TypeExpression(memberType), nameofParam); return generator.InvocationExpression(generator.IdentifierName("nameof"), nameofParam); } ); return documentEditor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixer : XunitCodeFixProvider { public override FixAllProvider? GetFixAllProvider() => BatchFixer; public const string Key_MakeParameterNullable = "xUnit1034_MakeParameterNullable"; public MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixer() : base(Descriptors.X1034_MemberDataArgumentsMustMatchMethodParameters_NullShouldNotBeUsedForIncompatibleParameter.Id) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var node = root.FindNode(context.Span); var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.ParameterIndex, out var parameterIndexText)) return; if (!int.TryParse(parameterIndexText, out var parameterIndex)) return; var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is null) return; var attributeList = node.FirstAncestorOrSelf(); if (attributeList is null) return; var propertyAttributeParameters = attributeList .Arguments .Count(a => !string.IsNullOrEmpty(a.NameEquals?.Name.Identifier.ValueText)); var paramsCount = attributeList.Arguments.Count - 1 - propertyAttributeParameters; (_, var declaredMemberTypeSymbol) = MemberDataShouldReferenceValidMember.GetClassTypesForAttribute( attributeList, semanticModel, context.CancellationToken); if (declaredMemberTypeSymbol is null) return; var memberName = diagnostic.Properties[Constants.Properties.MemberName]; if (memberName is null) return; var memberSymbol = MemberDataShouldReferenceValidMember.FindMethodSymbol(memberName, declaredMemberTypeSymbol, paramsCount); if (memberSymbol is not IMethodSymbol methodSymbol) return; var methodSyntaxes = methodSymbol.DeclaringSyntaxReferences; if (methodSyntaxes.Length != 1) return; if (await methodSyntaxes[0].GetSyntaxAsync().ConfigureAwait(false) is not MethodDeclarationSyntax method) return; context.RegisterCodeFix( CodeAction.Create( "Make parameter nullable", ct => MakeParameterNullable(context.Document, method, parameterIndex, ct), Key_MakeParameterNullable ), context.Diagnostics ); } static async Task MakeParameterNullable( Document document, MethodDeclarationSyntax method, int parameterIndex, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (method.ParameterList.Parameters.Count > parameterIndex) { var param = method.ParameterList.Parameters[parameterIndex]; var semanticModel = editor.SemanticModel; if (semanticModel is not null && param.Type is not null) { var paramTypeSymbol = semanticModel.GetTypeInfo(param.Type, cancellationToken).Type; if (paramTypeSymbol is not null) { var nullableT = paramTypeSymbol.IsReferenceType ? paramTypeSymbol.WithNullableAnnotation(NullableAnnotation.Annotated) : TypeSymbolFactory.NullableOfT(semanticModel.Compilation).Construct(paramTypeSymbol); editor.SetType(param, editor.Generator.TypeExpression(nullableT)); } } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/MemberDataShouldReferenceValidMember_ParamsForNonMethodFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Text; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class MemberDataShouldReferenceValidMember_ParamsForNonMethodFixer : XunitCodeFixProvider { public const string Key_RemoveArgumentsFromMemberData = "xUnit1021_RemoveArgumentsFromMemberData"; public MemberDataShouldReferenceValidMember_ParamsForNonMethodFixer() : base(Descriptors.X1021_MemberDataNonMethodShouldNotHaveParameters.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; var diagnosticId = diagnostic.Id; var attribute = root.FindNode(context.Span).FirstAncestorOrSelf(); if (attribute is null) return; context.RegisterCodeFix( CodeAction.Create( "Remove arguments from MemberData", ct => RemoveUnneededArguments(context.Document, attribute, context.Span, ct), Key_RemoveArgumentsFromMemberData ), context.Diagnostics ); } static async Task RemoveUnneededArguments( Document document, AttributeSyntax attribute, TextSpan span, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (attribute.ArgumentList is not null) foreach (var argument in attribute.ArgumentList.Arguments) if (argument.Span.OverlapsWith(span)) editor.RemoveNode(argument); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/MemberDataShouldReferenceValidMember_ReturnTypeFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public sealed class MemberDataShouldReferenceValidMember_ReturnTypeFixer : XunitMemberFixProvider { public override FixAllProvider? GetFixAllProvider() => BatchFixer; public const string Key_ChangeMemberReturnType_ObjectArray = "xUnit1019_ChangeMemberReturnType_ObjectArray"; public const string Key_ChangeMemberReturnType_ITheoryDataRow = "xUnit1019_ChangeMemberReturnType_ITheoryDataRow"; public MemberDataShouldReferenceValidMember_ReturnTypeFixer() : base(Descriptors.X1019_MemberDataMustReferenceMemberOfValidType.Id) { } public override async Task RegisterCodeFixesAsync( CodeFixContext context, ISymbol member) { var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is null) return; var objectArrayType = TypeSymbolFactory.IEnumerableOfObjectArray(semanticModel.Compilation); context.RegisterCodeFix( CodeAction.Create( "Change return type to IEnumerable", ct => context.Document.Project.Solution.ChangeMemberType(member, objectArrayType, ct), Key_ChangeMemberReturnType_ObjectArray ), context.Diagnostics ); var theoryDataRowType = TypeSymbolFactory.IEnumerableOfITheoryDataRow(semanticModel.Compilation); if (theoryDataRowType is not null) context.RegisterCodeFix( CodeAction.Create( "Change return type to IEnumerable", ct => context.Document.Project.Solution.ChangeMemberType(member, theoryDataRowType, ct), Key_ChangeMemberReturnType_ITheoryDataRow ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/MemberDataShouldReferenceValidMember_StaticFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public sealed class MemberDataShouldReferenceValidMember_StaticFixer : XunitMemberFixProvider { public override FixAllProvider? GetFixAllProvider() => BatchFixer; public const string Key_MakeMemberStatic = "xUnit1017_MakeMemberStatic"; public MemberDataShouldReferenceValidMember_StaticFixer() : base(Descriptors.X1017_MemberDataMustReferenceStaticMember.Id) { } public override Task RegisterCodeFixesAsync( CodeFixContext context, ISymbol member) { context.RegisterCodeFix( CodeAction.Create( "Make member static", ct => context.Document.Project.Solution.ChangeMemberStaticModifier(member, true, ct), Key_MakeMemberStatic ), context.Diagnostics ); return Task.CompletedTask; } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/MemberDataShouldReferenceValidMember_VisibilityFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public sealed class MemberDataShouldReferenceValidMember_VisibilityFixer : XunitMemberFixProvider { public override FixAllProvider? GetFixAllProvider() => BatchFixer; public const string Key_MakeMemberPublic = "xUnit1016_MakeMemberPublic"; public MemberDataShouldReferenceValidMember_VisibilityFixer() : base(Descriptors.X1016_MemberDataMustReferencePublicMember.Id) { } public override Task RegisterCodeFixesAsync( CodeFixContext context, ISymbol member) { context.RegisterCodeFix( CodeAction.Create( "Make member public", ct => context.Document.Project.Solution.ChangeMemberAccessibility(member, Accessibility.Public, ct), Key_MakeMemberPublic ), context.Diagnostics ); return Task.CompletedTask; } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/PublicMethodShouldBeMarkedAsTestFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class PublicMethodShouldBeMarkedAsTestFixer : XunitCodeFixProvider { public const string Key_ConvertToFact = "xUnit1013_ConvertToFact"; public const string Key_ConvertToTheory = "xUnit1013_ConvertToTheory"; public const string Key_MakeMethodInternal = "xUnit1013_MakeMethodInternal"; public PublicMethodShouldBeMarkedAsTestFixer() : base(Descriptors.X1013_PublicMethodShouldBeMarkedAsTest.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (methodDeclaration is null) return; // Fix #1: Offer to convert it to a theory if it has parameters... if (methodDeclaration.ParameterList.Parameters.Any()) context.RegisterCodeFix( CodeAction.Create( "Add [Theory]", ct => AddAttribute(context.Document, methodDeclaration, Constants.Types.Xunit.TheoryAttribute, ct), Key_ConvertToTheory ), context.Diagnostics ); // ...otherwise, offer to convert it to a fact else context.RegisterCodeFix( CodeAction.Create( "Add [Fact]", ct => AddAttribute(context.Document, methodDeclaration, Constants.Types.Xunit.FactAttribute, ct), Key_ConvertToFact ), context.Diagnostics ); // Fix #2: Offer to mark the method as internal context.RegisterCodeFix( CodeAction.Create( "Make method internal", ct => context.Document.ChangeAccessibility(methodDeclaration, Accessibility.Internal, ct), Key_MakeMethodInternal ), context.Diagnostics ); } static async Task AddAttribute( Document document, MethodDeclarationSyntax methodDeclaration, string type, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.AddAttribute(methodDeclaration, editor.Generator.Attribute(type)); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/RemoveMethodParameterFix.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class RemoveMethodParameterFix : XunitCodeFixProvider { public const string Key_RemoveParameter = "xUnit1022_xUnit1026_RemoveParameter"; public RemoveMethodParameterFix() : base( Descriptors.X1022_TheoryMethodCannotHaveParameterArray.Id, Descriptors.X1026_TheoryMethodShouldUseAllParameters.Id ) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var parameter = root.FindNode(context.Span).FirstAncestorOrSelf(); if (parameter is null) return; var parameterName = parameter.Identifier.Text; context.RegisterCodeFix( XunitCodeAction.Create( ct => context.Document.RemoveNode(parameter, ct), Key_RemoveParameter, "Remove parameter '{0}'", parameterName ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/TestClassCannotBeNestedInGenericClassFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class TestClassCannotBeNestedInGenericClassFixer : XunitCodeFixProvider { public const string Key_ExtractTestClass = "xUnit1032_TestClassCannotBeNestedInGenericClass"; public TestClassCannotBeNestedInGenericClassFixer() : base(Descriptors.X1032_TestClassCannotBeNestedInGenericClass.Id) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var classDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (classDeclaration is null) return; context.RegisterCodeFix( CodeAction.Create( "Extract test class from parent class", ct => context.Document.ExtractNodeFromParent(classDeclaration, ct), Key_ExtractTestClass ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/TestClassMustBePublicFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class TestClassMustBePublicFixer : XunitCodeFixProvider { public const string Key_MakeTestClassPublic = "xUnit1000_MakeTestClassPublic"; public TestClassMustBePublicFixer() : base(Descriptors.X1000_TestClassMustBePublic.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var classDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (classDeclaration is null) return; context.RegisterCodeFix( CodeAction.Create( "Make test class public", ct => context.Document.ChangeAccessibility(classDeclaration, Accessibility.Public, ct), Key_MakeTestClassPublic ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/TestClassShouldHaveTFixtureArgumentFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class TestClassShouldHaveTFixtureArgumentFixer : XunitCodeFixProvider { public const string Key_GenerateConstructor = "xUnit1033_GenerateConstructor"; public TestClassShouldHaveTFixtureArgumentFixer() : base(Descriptors.X1033_TestClassShouldHaveTFixtureArgument.Id) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var classDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (classDeclaration is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.TestClassName, out var testClassName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.TFixtureName, out var tFixtureName)) return; if (tFixtureName is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.TFixtureDisplayName, out var tFixtureDisplayName)) return; if (tFixtureDisplayName is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => context.Document.AddConstructor( classDeclaration, typeDisplayName: tFixtureDisplayName, typeName: tFixtureName, ct ), Key_GenerateConstructor, "Generate constructor '{0}({1})'", testClassName, tFixtureName ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/TestMethodMustNotHaveMultipleFactAttributesFixer.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class TestMethodMustNotHaveMultipleFactAttributesFixer : XunitCodeFixProvider { public TestMethodMustNotHaveMultipleFactAttributesFixer() : base(Descriptors.X1002_TestMethodMustNotHaveMultipleFactAttributes.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public static string Key_KeepAttribute(string simpleTypeName) => string.Format(CultureInfo.InvariantCulture, "xUnit1002_KeepAttribute_{0}", simpleTypeName); public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (methodDeclaration is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; var attributeTypes = diagnostic.Properties.Keys.ToList(); foreach (var attributeType in attributeTypes) { var simpleName = GetAttributeSimpleName(attributeType); context.RegisterCodeFix( XunitCodeAction.Create( ct => RemoveAttributes(context.Document, methodDeclaration, attributeTypes, attributeType, ct), Key_KeepAttribute(simpleName), "Keep '{0}' attribute", simpleName ), context.Diagnostics ); } } static string GetAttributeSimpleName(string attributeType) { var simpleName = attributeType; if (simpleName.Contains(".")) simpleName = simpleName.Substring(attributeType.LastIndexOf('.') + 1); const string nameSuffix = "Attribute"; if (simpleName.EndsWith(nameSuffix, System.StringComparison.Ordinal)) simpleName = simpleName.Substring(0, simpleName.Length - nameSuffix.Length); return simpleName; } static async Task RemoveAttributes( Document document, MethodDeclarationSyntax methodDeclaration, IReadOnlyList attributeTypesToConsider, string attributeTypeToKeep, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (semanticModel is not null) { var oneKept = false; foreach (var attributeList in methodDeclaration.AttributeLists) foreach (var attribute in attributeList.Attributes) { var attributeType = semanticModel.GetTypeInfo(attribute, cancellationToken).Type; if (attributeType is null) continue; var attributeTypeDisplay = attributeType.ToDisplayString(); if (attributeTypesToConsider.Contains(attributeTypeDisplay)) { if (attributeTypeDisplay == attributeTypeToKeep && !oneKept) { oneKept = true; continue; } editor.RemoveNode(attribute); } } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/TestMethodShouldNotBeSkippedFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class TestMethodShouldNotBeSkippedFixer : XunitCodeFixProvider { public const string Key_RemoveSkipArgument = "xUnit1004_RemoveSkipArgument"; public TestMethodShouldNotBeSkippedFixer() : base(Descriptors.X1004_TestMethodShouldNotBeSkipped.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var argument = root.FindNode(context.Span).FirstAncestorOrSelf(); if (argument is null) return; context.RegisterCodeFix( CodeAction.Create( "Remove Skip argument", ct => RemoveArgument(context.Document, argument, ct), Key_RemoveSkipArgument ), context.Diagnostics ); } static async Task RemoveArgument( Document document, AttributeArgumentSyntax argument, CancellationToken ct) { var editor = await DocumentEditor.CreateAsync(document, ct).ConfigureAwait(false); editor.RemoveNode(argument); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/TheoryDataShouldNotUseTheoryDataRowFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class TheoryDataShouldNotUseTheoryDataRowFixer() : XunitCodeFixProvider(Descriptors.X1052_TheoryDataShouldNotUseITheoryDataRow.Id) { public const string Key_UseIEnumerable = "xUnit1052_UseIEnumerable"; public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; foreach (var diagnostic in context.Diagnostics) { var span = diagnostic.Location.SourceSpan; var node = root.FindNode(span); if (node is not GenericNameSyntax genericNameNode) return; if (genericNameNode.TypeArgumentList.Arguments.Count != 1) return; if (!IsPartOfOnlyTypeDeclaration(genericNameNode)) return; context.RegisterCodeFix( CodeAction.Create( "Use IEnumerable instead of TheoryData", ct => ConvertToIEnumerable(context.Document, genericNameNode, ct), Key_UseIEnumerable ), diagnostic ); } } static bool IsPartOfOnlyTypeDeclaration(GenericNameSyntax genericName) { var parent = genericName.Parent; if (parent is VariableDeclarationSyntax variableDeclaration) return variableDeclaration.Variables.All(v => v.Initializer is null); if (parent is PropertyDeclarationSyntax propertyDeclaration) return propertyDeclaration.Initializer is null; return parent is ParameterSyntax or MethodDeclarationSyntax; } static async Task ConvertToIEnumerable( Document document, GenericNameSyntax node, CancellationToken ct) { var editor = await DocumentEditor.CreateAsync(document, ct).ConfigureAwait(false); var token = SyntaxFactory.IdentifierName("IEnumerable").Identifier; var newGenericName = SyntaxFactory.GenericName(token, node.TypeArgumentList); editor.ReplaceNode(node, newGenericName); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/TheoryMethodCannotHaveDefaultParameterFixer.cs ================================================ using System.Composition; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class TheoryMethodCannotHaveDefaultParameterFixer : XunitCodeFixProvider { public const string Key_RemoveParameterDefault = "xUnit1023_RemoveParameterDefault"; public TheoryMethodCannotHaveDefaultParameterFixer() : base(Descriptors.X1023_TheoryMethodCannotHaveDefaultParameter.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var parameter = root.FindNode(context.Span).FirstAncestorOrSelf(); if (parameter is null || parameter.Default is null) return; var parameterName = parameter.Identifier.Text; context.RegisterCodeFix( XunitCodeAction.Create( ct => context.Document.RemoveNode(parameter.Default, ct), Key_RemoveParameterDefault, "Remove parameter '{0}' default", parameterName ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X1000/UseCancellationTokenFixer.cs ================================================ using System.Collections.Generic; using System.Composition; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Operations; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class UseCancellationTokenFixer : XunitCodeFixProvider { public const string Key_UseCancellationTokenArgument = "xUnit1051_UseCancellationTokenArgument"; public UseCancellationTokenFixer() : base(Descriptors.X1051_UseCancellationToken.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is null) return; var testContextType = TypeSymbolFactory.TestContext_V3(semanticModel.Compilation); if (testContextType is null) return; var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.ParameterName, out var parameterName)) return; if (parameterName is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.ParameterIndex, out var parameterIndexText)) return; if (!int.TryParse(parameterIndexText, out var parameterIndex)) return; if (root.FindNode(diagnostic.Location.SourceSpan) is not InvocationExpressionSyntax invocation) return; if (semanticModel.GetOperation(invocation, context.CancellationToken) is not IInvocationOperation invocationOperation) return; var arguments = invocation.ArgumentList.Arguments; for (var argumentIndex = 0; argumentIndex < arguments.Count; argumentIndex++) if (arguments[argumentIndex].NameColon?.Name.Identifier.Text == parameterName) { parameterIndex = argumentIndex; break; } context.RegisterCodeFix( XunitCodeAction.Create( async ct => { var editor = await DocumentEditor.CreateAsync(context.Document, ct).ConfigureAwait(false); var testContextCancellationTokenExpression = (ExpressionSyntax)editor.Generator.MemberAccessExpression( editor.Generator.MemberAccessExpression( editor.Generator.TypeExpression(testContextType), "Current" ), "CancellationToken" ); var args = new List(arguments); if (invocationOperation.Arguments.FirstOrDefault(arg => arg.ArgumentKind == ArgumentKind.ParamArray) is { } paramsArgument) TransformParamsArgument(args, paramsArgument, editor.Generator); if (parameterIndex < args.Count) args[parameterIndex] = args[parameterIndex].WithExpression(testContextCancellationTokenExpression); else { var argument = Argument(testContextCancellationTokenExpression); if (parameterIndex > args.Count || args.Any(arg => arg.NameColon is not null)) argument = argument.WithNameColon(NameColon(parameterName)); args.Add(argument); } editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(args))) ); return editor.GetChangedDocument(); }, Key_UseCancellationTokenArgument, "{0} TestContext.Current.CancellationToken", parameterIndex < arguments.Count ? "Use" : "Add" ), context.Diagnostics ); } static void TransformParamsArgument( List arguments, IArgumentOperation paramsOperation, SyntaxGenerator generator) { if (paramsOperation is not { Value: IArrayCreationOperation { Type: IArrayTypeSymbol arrayTypeSymbol, Initializer: { } initializer } }) return; // We know that the params arguments occupy the end of the list because the language // does not allow regular arguments after params. int paramsCount = initializer.ElementValues.Length; int paramsStart = arguments.Count - paramsCount; arguments.RemoveRange(paramsStart, paramsCount); ExpressionSyntax arrayCreation = (ExpressionSyntax)generator.ArrayCreationExpression( generator.TypeExpression(arrayTypeSymbol.ElementType), initializer.ElementValues.Select(x => x.Syntax) ); arguments.Add(Argument(arrayCreation)); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertCollectionContainsShouldNotUseBoolCheckFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertCollectionContainsShouldNotUseBoolCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2017_UseAlternateAssert"; public AssertCollectionContainsShouldNotUseBoolCheckFixer() : base(Descriptors.X2017_AssertCollectionContainsShouldNotUseBoolCheck.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.MethodName, out var methodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseContainsCheck(context.Document, invocation, replacement, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseContainsCheck( Document document, InvocationExpressionSyntax invocation, string replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments[0].Expression is InvocationExpressionSyntax invocationExpressionSyntax) if (invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax anyMethodInvocation) { var anyTarget = anyMethodInvocation.Expression; var isTrailingNull = invocationExpressionSyntax.ArgumentList.Arguments.Count == 2 && invocationExpressionSyntax.ArgumentList.Arguments[1].Expression.Kind() == SyntaxKind.NullLiteralExpression; var existingArguments = isTrailingNull ? SeparatedList([invocationExpressionSyntax.ArgumentList.Arguments[0]]) : invocationExpressionSyntax.ArgumentList.Arguments; editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(existingArguments.Insert(1, Argument(anyTarget))))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEmptyCollectionCheckShouldNotBeUsedFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEmptyCollectionCheckShouldNotBeUsedFixer : XunitCodeFixProvider { public const string Key_AddElementInspector = "xUnit2011_AddElementInspector"; public const string Key_UseAssertEmpty = "xUnit2011_UseAssertEmpty"; public AssertEmptyCollectionCheckShouldNotBeUsedFixer() : base(Descriptors.X2011_AssertEmptyCollectionCheckShouldNotBeUsed.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; context.RegisterCodeFix( CodeAction.Create( "Use Assert.Empty", ct => UseEmptyCheck(context.Document, invocation, ct), Key_UseAssertEmpty ), context.Diagnostics ); context.RegisterCodeFix( CodeAction.Create( "Add element inspector", ct => AddElementInspector(context.Document, invocation, ct), Key_AddElementInspector ), context.Diagnostics ); } static async Task UseEmptyCheck( Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) editor.ReplaceNode( invocation, invocation.WithExpression(memberAccess.WithName(IdentifierName("Empty"))) ); return editor.GetChangedDocument(); } static async Task AddElementInspector( Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.ReplaceNode( invocation, invocation.WithArgumentList(invocation.ArgumentList.AddArguments(Argument(SimpleLambdaExpression(Parameter(Identifier("x")), Block())))) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixer : XunitCodeFixProvider { public const string Key_UseDoesNotContain = "xUnit2029_UseDoesNotContain"; public const string Key_UseContains = "xUnit2030_UseContains"; public override FixAllProvider? GetFixAllProvider() => BatchFixer; static readonly string[] targetDiagnostics = [ Descriptors.X2029_AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.Id, Descriptors.X2030_AssertNotEmptyShouldNotBeUsedForCollectionContainsCheck.Id, ]; public AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixer() : base(targetDiagnostics) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; if (context.Diagnostics.Length != 1) return; var diagnostic = context.Diagnostics.Single(); string replaceAssert; string equivalenceKey; string title; if (diagnostic.Id == targetDiagnostics[0]) { replaceAssert = Constants.Asserts.DoesNotContain; equivalenceKey = Key_UseDoesNotContain; title = "Use DoesNotContain"; } else { replaceAssert = Constants.Asserts.Contains; equivalenceKey = Key_UseContains; title = "Use Contains"; } context.RegisterCodeFix( XunitCodeAction.Create( c => UseCheck(context.Document, invocation, replaceAssert, c), equivalenceKey, title ), context.Diagnostics ); } static async Task UseCheck( Document document, InvocationExpressionSyntax invocation, string replaceAssert, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var arguments = invocation.ArgumentList.Arguments; if (arguments.Count == 1 && arguments[0].Expression is InvocationExpressionSyntax innerInvocationSyntax) if (invocation.Expression is MemberAccessExpressionSyntax outerMemberAccess && innerInvocationSyntax.Expression is MemberAccessExpressionSyntax memberAccess) if (innerInvocationSyntax.ArgumentList.Arguments[0].Expression is ExpressionSyntax innerArgument) editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList([Argument(memberAccess.Expression), Argument(innerArgument)]))) .WithExpression(outerMemberAccess.WithName(IdentifierName(replaceAssert))) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2012_UseAlternateAssert"; public AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer() : base(Descriptors.X2012_AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.AssertMethodName, out var assertMethodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseContainsCheck(context.Document, invocation, replacement, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseContainsCheck( Document document, InvocationExpressionSyntax invocation, string replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments[0].Expression is InvocationExpressionSyntax invocationExpressionSyntax) if (invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax anyMethodInvocation) { var anyTarget = anyMethodInvocation.Expression; editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(invocationExpressionSyntax.ArgumentList.Arguments.Insert(0, Argument(anyTarget))))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEqualGenericShouldNotBeUsedForStringValueFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEqualGenericShouldNotBeUsedForStringValueFixer : XunitCodeFixProvider { public const string Key_UseStringAssertEqual = "xUnit2006_UseStringAssertEqual"; public AssertEqualGenericShouldNotBeUsedForStringValueFixer() : base(Descriptors.X2006_AssertEqualGenericShouldNotBeUsedForStringValue.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var syntaxNode = root.FindNode(context.Span); var invocation = syntaxNode.FirstAncestorOrSelf(); if (invocation is null) return; if (invocation.Expression is MemberAccessExpressionSyntax) context.RegisterCodeFix( CodeAction.Create( "Use string Assert.Equal", ct => UseNonGenericStringEqualCheck(context.Document, invocation, ct), Key_UseStringAssertEqual ), context.Diagnostics ); } static async Task UseNonGenericStringEqualCheck( Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) editor.ReplaceNode( memberAccess, memberAccess.WithName(IdentifierName(Constants.Asserts.Equal)) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEqualLiteralValueShouldBeFirstFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEqualLiteralValueShouldBeFirstFixer : XunitCodeFixProvider { public const string Key_SwapArguments = "xUnit2000_SwapArguments"; public AssertEqualLiteralValueShouldBeFirstFixer() : base(Descriptors.X2000_AssertEqualLiteralValueShouldBeFirst.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; context.RegisterCodeFix( CodeAction.Create( "Swap arguments", ct => SwapArguments(context.Document, invocation, ct), Key_SwapArguments ), context.Diagnostics ); } static async Task SwapArguments( Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation is not null) { var arguments = invocation.ArgumentList.Arguments; if (arguments.Count >= 2) { ArgumentSyntax expectedArg, actualArg; if (arguments.All(x => x.NameColon is not null)) { expectedArg = arguments.Single(x => x.NameColon?.Name.Identifier.ValueText == Constants.AssertArguments.Expected); actualArg = arguments.Single(x => x.NameColon?.Name.Identifier.ValueText == Constants.AssertArguments.Actual); } else { expectedArg = arguments[0]; actualArg = arguments[1]; } editor.ReplaceNode(expectedArg, expectedArg.WithExpression(actualArg.Expression)); editor.ReplaceNode(actualArg, actualArg.WithExpression(expectedArg.Expression)); } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEqualPrecisionShouldBeInRangeFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEqualPrecisionShouldBeInRangeFixer : XunitCodeFixProvider { public const string Key_UsePrecision = "xUnit2016_UsePrecision"; public AssertEqualPrecisionShouldBeInRangeFixer() : base(Descriptors.X2016_AssertEqualPrecisionShouldBeInRange.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var precisionArgument = root.FindNode(context.Span).FirstAncestorOrSelf(); if (precisionArgument is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; if (!int.TryParse(replacement, out var replacementInt)) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseRecommendedPrecision(context.Document, precisionArgument, replacementInt, ct), Key_UsePrecision, "Use precision {0}", replacementInt ), context.Diagnostics ); } static async Task UseRecommendedPrecision( Document document, ArgumentSyntax precisionArgument, int replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.ReplaceNode( precisionArgument, Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(replacement))) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2004_UseAlterateAssert"; public AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer() : base(Descriptors.X2004_AssertEqualShouldNotUsedForBoolLiteralCheck.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.MethodName, out var methodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.LiteralValue, out var literalValue)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; if (invocation.Expression is MemberAccessExpressionSyntax) context.RegisterCodeFix( XunitCodeAction.Create( ct => UseBoolCheckAsync(context.Document, invocation, replacement, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseBoolCheckAsync( Document document, InvocationExpressionSyntax invocation, string replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.ArgumentList.Arguments.Count > 1 && invocation.Expression is MemberAccessExpressionSyntax memberAccess) editor.ReplaceNode( invocation, invocation .WithArgumentList(invocation.ArgumentList.WithArguments(SingletonSeparatedList(invocation.ArgumentList.Arguments[1]))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2013_UseAlterateAssert"; public AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer() : base(Descriptors.X2013_AssertEqualShouldNotBeUsedForCollectionSizeCheck.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.MethodName, out var methodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.SizeValue, out var sizeValue)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseCollectionSizeAssertionAsync(context.Document, invocation, replacement, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseCollectionSizeAssertionAsync( Document document, InvocationExpressionSyntax invocation, string replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) { var expression = GetExpressionSyntax(invocation); if (expression is not null) editor.ReplaceNode( invocation, invocation .WithArgumentList(invocation.ArgumentList.WithArguments(SingletonSeparatedList(Argument(expression)))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } return editor.GetChangedDocument(); } static ExpressionSyntax? GetExpressionSyntax(InvocationExpressionSyntax invocation) { if (invocation.ArgumentList.Arguments.Count < 2) return null; if (invocation.ArgumentList.Arguments[1].Expression is InvocationExpressionSyntax sizeInvocation) return (sizeInvocation.Expression as MemberAccessExpressionSyntax)?.Expression; var sizeMemberAccess = invocation.ArgumentList.Arguments[1].Expression as MemberAccessExpressionSyntax; return sizeMemberAccess?.Expression; } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEqualShouldNotBeUsedForNullCheckFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEqualShouldNotBeUsedForNullCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2003_UseAlternateAssert"; public AssertEqualShouldNotBeUsedForNullCheckFixer() : base(Descriptors.X2003_AssertEqualShouldNotUsedForNullCheck.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.MethodName, out var methodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; if (invocation.Expression is MemberAccessExpressionSyntax) context.RegisterCodeFix( XunitCodeAction.Create( ct => UseNullCheckAsync(context.Document, invocation, replacement, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseNullCheckAsync( Document document, InvocationExpressionSyntax invocation, string replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.ArgumentList.Arguments.Count > 1 && invocation.Expression is MemberAccessExpressionSyntax memberAccess) editor.ReplaceNode( invocation, invocation .WithArgumentList(invocation.ArgumentList.WithArguments(SingletonSeparatedList(invocation.ArgumentList.Arguments[1]))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertEqualsShouldNotBeUsedFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEqualsShouldNotBeUsedFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2001_UseAlternateAssert"; public AssertEqualsShouldNotBeUsedFixer() : base(Descriptors.X2001_AssertEqualsShouldNotBeUsed.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; if (invocation.Expression is MemberAccessExpressionSyntax) context.RegisterCodeFix( XunitCodeAction.UseDifferentAssertMethod( Key_UseAlternateAssert, context.Document, invocation, replacement ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertIsTypeShouldNotBeUsedForAbstractTypeFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertIsTypeShouldNotBeUsedForAbstractTypeFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2018_UseAlternateAssert"; public AssertIsTypeShouldNotBeUsedForAbstractTypeFixer() : base(Descriptors.X2018_AssertIsTypeShouldNotBeUsedForAbstractType.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.UseExactMatch, out var useExactMatch)) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; if (useExactMatch == bool.TrueString) { context.RegisterCodeFix( XunitCodeAction.Create( ct => UseExactMatchFalse(context.Document, invocation, ct), Key_UseAlternateAssert, "Use 'exactMatch: false'" ), context.Diagnostics ); } else { var simpleNameSyntax = invocation.GetSimpleName(); if (simpleNameSyntax is null) return; var methodName = simpleNameSyntax.Identifier.Text; if (!AssertIsTypeShouldNotBeUsedForAbstractType.ReplacementMethods.TryGetValue(methodName, out var replacementName)) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseIsAssignableFrom(context.Document, simpleNameSyntax, replacementName, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacementName ), context.Diagnostics ); } } static async Task UseExactMatchFalse( Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var falseArgument = ParseArgumentList("false") .Arguments[0] .WithNameColon(NameColon("exactMatch")); var argumentList = invocation.ArgumentList; argumentList = argumentList.Arguments.Count == 2 ? argumentList.ReplaceNode(argumentList.Arguments[1], falseArgument) : argumentList.AddArguments(falseArgument); editor.ReplaceNode(invocation.ArgumentList, argumentList); return editor.GetChangedDocument(); } static async Task UseIsAssignableFrom( Document document, SimpleNameSyntax simpleName, string replacementName, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.ReplaceNode( simpleName, simpleName.WithIdentifier(Identifier(replacementName)) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertNullShouldNotBeCalledOnValueTypesFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertNullShouldNotBeCalledOnValueTypesFixer : XunitCodeFixProvider { public const string Key_RemoveAssert = "xUnit2002_RemoveAssert"; public AssertNullShouldNotBeCalledOnValueTypesFixer() : base(Descriptors.X2002_AssertNullShouldNotBeCalledOnValueTypes.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var call = root.FindNode(context.Span).FirstAncestorOrSelf(); if (call is null) return; context.RegisterCodeFix( CodeAction.Create( "Remove unnecessary assertion", ct => RemoveCall(context.Document, call, ct), Key_RemoveAssert ), context.Diagnostics ); } static async Task RemoveCall( Document document, ExpressionStatementSyntax call, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var containsLeadingComment = call .GetLeadingTrivia() .Any(t => t.IsKind(SyntaxKind.MultiLineCommentTrivia) || t.IsKind(SyntaxKind.SingleLineCommentTrivia)); var removeOptions = containsLeadingComment ? SyntaxRemoveOptions.KeepLeadingTrivia | SyntaxRemoveOptions.AddElasticMarker : SyntaxRemoveOptions.KeepNoTrivia; editor.RemoveNode(call, removeOptions); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertRegexMatchShouldNotUseBoolLiteralCheckFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertRegexMatchShouldNotUseBoolLiteralCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2008_UseAlternateAssert"; public AssertRegexMatchShouldNotUseBoolLiteralCheckFixer() : base(Descriptors.X2008_AssertRegexMatchShouldNotUseBoolLiteralCheck.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.MethodName, out var methodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.IsStatic, out var isStatic)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseRegexCheckAsync(context.Document, invocation, replacement, isStatic == bool.TrueString, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseRegexCheckAsync( Document document, InvocationExpressionSyntax invocation, string replacement, bool isStatic, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments[0].Expression is InvocationExpressionSyntax regexIsMatchInvocation) { if (isStatic) { editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(regexIsMatchInvocation.ArgumentList.Arguments.Reverse()))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } else if (regexIsMatchInvocation.ArgumentList.Arguments.Count > 0 && regexIsMatchInvocation.Expression is MemberAccessExpressionSyntax regexMemberAccess) { var regexMember = regexMemberAccess.Expression; editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList([Argument(regexMember), regexIsMatchInvocation.ArgumentList.Arguments[0]]))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertSameShouldNotBeCalledOnValueTypesFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertSameShouldNotBeCalledOnValueTypesFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2005_UseAlternateAssert"; public AssertSameShouldNotBeCalledOnValueTypesFixer() : base(Descriptors.X2005_AssertSameShouldNotBeCalledOnValueTypes.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; if (invocation.Expression is MemberAccessExpressionSyntax) context.RegisterCodeFix( XunitCodeAction.UseDifferentAssertMethod( Key_UseAlternateAssert, context.Document, invocation, replacement ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertSingleShouldBeUsedForSingleParameterFixer.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertSingleShouldBeUsedForSingleParameterFixer : XunitCodeFixProvider { const string DefaultParameterName = "item"; public const string Key_UseSingleMethod = "xUnit2023_UseSingleMethod"; public AssertSingleShouldBeUsedForSingleParameterFixer() : base(Descriptors.X2023_AssertSingleShouldBeUsedForSingleParameter.Id) { } static string GetSafeVariableName( string targetParameterName, ImmutableHashSet localSymbols) { var idx = 2; var result = targetParameterName; while (localSymbols.Contains(result)) result = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", targetParameterName, idx++); return result; } static IEnumerable GetLambdaStatements(SimpleLambdaExpressionSyntax lambdaExpression) { if (lambdaExpression.ExpressionBody is InvocationExpressionSyntax lambdaBody) yield return ExpressionStatement(lambdaBody).WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation); else if (lambdaExpression.Block is not null && lambdaExpression.Block.Statements.Count != 0) foreach (var statement in lambdaExpression.Block.Statements) yield return statement.WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation); } static SyntaxNode GetMethodInvocation( IdentifierNameSyntax methodExpression, string parameterName, bool needAwait) { ExpressionSyntax invocation = InvocationExpression( methodExpression, ArgumentList(SingletonSeparatedList(Argument(IdentifierName(parameterName)))) ); if (needAwait) invocation = AwaitExpression(invocation); return ExpressionStatement(invocation); } static LocalDeclarationStatementSyntax OneItemVariableStatement( string parameterName, InvocationExpressionSyntax replacementNode) { var equalsToReplacementNode = EqualsValueClause(replacementNode); var oneItemVariableDeclaration = VariableDeclaration( ParseTypeName("var"), SingletonSeparatedList( VariableDeclarator(Identifier(parameterName)) .WithInitializer(equalsToReplacementNode) ) ).NormalizeWhitespace(); return LocalDeclarationStatement(oneItemVariableDeclaration); } public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; if (context.Diagnostics.FirstOrDefault() is not Diagnostic diagnostic) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.AssertMethodName, out var assertMethodName) || assertMethodName is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement) || replacement is null) return; // We want to remove the 'await' in front of 'Assert.CollectionAsync' since 'Assert.Single' doesn't need it var nodeToReplace = invocation.Parent; if (assertMethodName == Constants.Asserts.CollectionAsync && nodeToReplace is AwaitExpressionSyntax) nodeToReplace = nodeToReplace.Parent; // Can't replace something that's not a standlone expression if (nodeToReplace is not ExpressionStatementSyntax) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseSingleMethod(context.Document, invocation, nodeToReplace, assertMethodName, replacement, ct), Key_UseSingleMethod, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseSingleMethod( Document document, InvocationExpressionSyntax invocation, SyntaxNode nodeToReplace, string assertMethodName, string replacementMethod, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (semanticModel is not null && invocation.Parent is not null) { var statements = new List(); var startLocation = invocation.GetLocation().SourceSpan.Start; var localSymbols = semanticModel.LookupSymbols(startLocation).OfType().Select(s => s.Name).ToImmutableHashSet(); var replacementNode = invocation .WithArgumentList(ArgumentList(SeparatedList([Argument(invocation.ArgumentList.Arguments[0].Expression)]))) .WithExpression(memberAccess.WithName(IdentifierName(replacementMethod))); if (invocation.ArgumentList.Arguments[1].Expression is SimpleLambdaExpressionSyntax lambdaExpression) { var originalParameterName = lambdaExpression.Parameter.Identifier.Text; var parameterName = GetSafeVariableName(originalParameterName, localSymbols); if (parameterName != originalParameterName) { var body = lambdaExpression.Body; var tokens = body .DescendantTokens() .Where(t => t.IsKind(SyntaxKind.IdentifierToken) && t.Text == originalParameterName) .ToArray(); body = body.ReplaceTokens(tokens, (t1, t2) => Identifier(t2.LeadingTrivia, parameterName, t2.TrailingTrivia)); lambdaExpression = lambdaExpression.WithBody(body); } statements.Add(OneItemVariableStatement(parameterName, replacementNode).WithTriviaFrom(nodeToReplace)); statements.AddRange(GetLambdaStatements(lambdaExpression)); } else if (invocation.ArgumentList.Arguments[1].Expression is IdentifierNameSyntax identifierExpression) { var isMethod = semanticModel.GetSymbolInfo(identifierExpression, cancellationToken).Symbol?.Kind == SymbolKind.Method; if (isMethod) { var parameterName = GetSafeVariableName(DefaultParameterName, localSymbols); statements.Add(OneItemVariableStatement(parameterName, replacementNode).WithTriviaFrom(nodeToReplace)); statements.Add(GetMethodInvocation(identifierExpression, parameterName, needAwait: assertMethodName == Constants.Asserts.CollectionAsync)); } } editor.InsertBefore(nodeToReplace, statements); editor.RemoveNode(nodeToReplace); } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertSingleShouldUseTwoArgumentCallFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertSingleShouldUseTwoArgumentCallFixer : XunitCodeFixProvider { public const string Key_UseTwoArguments = "xUnit2031_UseSingleTwoArgumentCall"; public AssertSingleShouldUseTwoArgumentCallFixer() : base(Descriptors.X2031_AssertSingleShouldUseTwoArgumentCall.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; context.RegisterCodeFix( XunitCodeAction.Create( c => UseCheck(context.Document, invocation, c), Key_UseTwoArguments, "Use two-argument call" ), context.Diagnostics ); } static async Task UseCheck( Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var arguments = invocation.ArgumentList.Arguments; if (arguments.Count == 1 && arguments[0].Expression is InvocationExpressionSyntax innerInvocationSyntax) if (innerInvocationSyntax.Expression is MemberAccessExpressionSyntax memberAccess) if (innerInvocationSyntax.ArgumentList.Arguments[0].Expression is ExpressionSyntax innerArgument) editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList([Argument(memberAccess.Expression), Argument(innerArgument)]))) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertStringEqualityCheckShouldNotUseBoolCheckFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertStringEqualityCheckShouldNotUseBoolCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2010_UseAlternateAssert"; public AssertStringEqualityCheckShouldNotUseBoolCheckFixer() : base(Descriptors.X2010_AssertStringEqualityCheckShouldNotUseBoolCheckFixer.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.AssertMethodName, out var assertMethodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.IsStaticMethodCall, out var isStaticMethodCall)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.IgnoreCase, out var ignoreCaseText)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; var ignoreCase = ignoreCaseText switch { "True" => true, "False" => false, _ => default(bool?) }; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseEqualCheck(context.Document, invocation, replacement, isStaticMethodCall == bool.TrueString, ignoreCase, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseEqualCheck( Document document, InvocationExpressionSyntax invocation, string replacement, bool isStaticMethodCall, bool? ignoreCase, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments[0].Expression is InvocationExpressionSyntax equalsInvocation) if (equalsInvocation.Expression is MemberAccessExpressionSyntax equalsMethodInvocation) { var equalsTarget = equalsMethodInvocation.Expression; var arguments = isStaticMethodCall ? equalsInvocation.ArgumentList.Arguments : equalsInvocation.ArgumentList.Arguments.Insert(0, Argument(equalsTarget)); if (ignoreCase == true) arguments = arguments.Replace( arguments[arguments.Count - 1], Argument( NameColon(IdentifierName(Constants.AssertArguments.IgnoreCase)), arguments[arguments.Count - 1].RefOrOutKeyword, LiteralExpression(SyntaxKind.TrueLiteralExpression) ) ); else if (ignoreCase == false) arguments = arguments.RemoveAt(arguments.Count - 1); editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(arguments))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertSubstringCheckShouldNotUseBoolCheckFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertSubstringCheckShouldNotUseBoolCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2009_UseAlternateAssert"; public AssertSubstringCheckShouldNotUseBoolCheckFixer() : base(Descriptors.X2009_AssertSubstringCheckShouldNotUseBoolCheck.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.AssertMethodName, out var assertMethodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.SubstringMethodName, out var substringMethodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseSubstringCheckAsync(context.Document, invocation, replacement, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseSubstringCheckAsync( Document document, InvocationExpressionSyntax invocation, string replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments[0].Expression is InvocationExpressionSyntax substringInvocation) if (substringInvocation.Expression is MemberAccessExpressionSyntax substringMethodInvocation) { var substringTarget = substringMethodInvocation.Expression; editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(substringInvocation.ArgumentList.Arguments.Insert(1, Argument(substringTarget))))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer.cs ================================================ using System.Collections.Generic; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Operations; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2014_UseAlternateAssert"; public AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer() : base(Descriptors.X2014_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck.Id) { } static ExpressionSyntax GetAsyncAssertionInvocation( InvocationExpressionSyntax invocation, MemberAccessExpressionSyntax memberAccess, string replacement) { var asyncAssertionInvocation = invocation .WithExpression(memberAccess.WithName(GetAsyncAssertionMethodName(memberAccess, replacement))) .WithArgumentList(invocation.ArgumentList); if (invocation.Parent.IsKind(SyntaxKind.AwaitExpression)) return asyncAssertionInvocation; return AwaitExpression(asyncAssertionInvocation.WithoutLeadingTrivia()) .WithLeadingTrivia(invocation.GetLeadingTrivia()); } static SimpleNameSyntax GetAsyncAssertionMethodName( MemberAccessExpressionSyntax memberAccess, string replacement) { if (memberAccess.Name is not GenericNameSyntax genericNameSyntax) return IdentifierName(replacement); return GenericName(IdentifierName(replacement).Identifier, genericNameSyntax.TypeArgumentList); } static IFunctionFixer? GetFunctionFixer( SyntaxNode? node, SemanticModel semanticModel, DocumentEditor editor) { return node switch { AnonymousFunctionExpressionSyntax anonymousFunction => new AnonymousFunctionFixer(anonymousFunction, semanticModel, editor), LocalFunctionStatementSyntax localFunction => new LocalFunctionFixer(localFunction, semanticModel, editor), MethodDeclarationSyntax method => new MethodFixer(method, editor), _ => null }; } static SyntaxNode? GetParentFunction(InvocationExpressionSyntax invocation) { return invocation.Parent?.FirstAncestorOrSelf(IsFunction); } static bool IsFunction(SyntaxNode node) { return node switch { AnonymousFunctionExpressionSyntax => true, LocalFunctionStatementSyntax => true, MethodDeclarationSyntax => true, _ => false }; } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var method = invocation.FirstAncestorOrSelf(); if (method is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.MethodName, out var methodName)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseAsyncAssertion(context.Document, invocation, replacement, ct), Key_UseAlternateAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static bool ShouldFixParentFunction( [NotNullWhen(true)] IFunctionFixer? parentFunctionFixer, IFunctionFixer? childFunctionFixer, CancellationToken cancellationToken) { // 3. Parent function is null, and child function is outer method. // Outer method was just fixed, so should stop fixing now. if (parentFunctionFixer is null) return false; // 1. Parent function is innermost function, and child function is null. // Should fix innermost function unconditionally. if (childFunctionFixer is null) return true; // 2. Parent function and child function are not null. // Should fix parent function if and only if child function is invoked inside parent function, and otherwise stop fixing. return childFunctionFixer.ShouldFixParentFunction(parentFunctionFixer.Function, cancellationToken); } static async Task UseAsyncAssertion( Document document, InvocationExpressionSyntax invocation, string replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess && semanticModel is not null) { var asyncAssertionInvocation = GetAsyncAssertionInvocation(invocation, memberAccess, replacement); editor.ReplaceNode(invocation, asyncAssertionInvocation); var parentFunctionFixer = GetFunctionFixer(GetParentFunction(invocation), semanticModel, editor); IFunctionFixer? childFunctionFixer = null; while (ShouldFixParentFunction(parentFunctionFixer, childFunctionFixer, cancellationToken)) { await parentFunctionFixer.Fix(cancellationToken).ConfigureAwait(false); childFunctionFixer = parentFunctionFixer; parentFunctionFixer = GetFunctionFixer(childFunctionFixer.GetParentFunction(), semanticModel, editor); } } return editor.GetChangedDocument(); } interface IFunctionFixer { SyntaxNode Function { get; } Task Fix(CancellationToken cancellationToken); SyntaxNode? GetParentFunction(); bool ShouldFixParentFunction(SyntaxNode parentFunction, CancellationToken cancellationToken); } sealed class AnonymousFunctionFixer( AnonymousFunctionExpressionSyntax anonymousFunction, SemanticModel semanticModel, DocumentEditor editor) : IFunctionFixer { public SyntaxNode Function => anonymousFunction; public async Task Fix(CancellationToken cancellationToken) { var modifiers = AsyncHelper.GetModifiersWithAsyncKeywordAdded(anonymousFunction.Modifiers); editor.ReplaceNode(anonymousFunction, (node, generator) => { if (node is AnonymousFunctionExpressionSyntax anonymousFunction) return anonymousFunction.WithModifiers(modifiers); return node; }); var declaration = await GetLocalDeclaration(cancellationToken).ConfigureAwait(false); if (declaration is null) return; var delegateType = await AsyncHelper.GetAsyncSystemDelegateType(declaration, anonymousFunction, editor, cancellationToken).ConfigureAwait(false); if (delegateType is null) return; editor.ReplaceNode(declaration, (node, generator) => { if (node is VariableDeclarationSyntax declaration) return declaration .WithType(delegateType) .WithLeadingTrivia(declaration.GetLeadingTrivia()); return node; }); } static IEnumerable GetAncestors(IOperation? operation) { while ((operation = operation?.Parent) is not null) yield return operation; } static IConditionalAccessOperation? GetConditionalAccessOperation(IConditionalAccessInstanceOperation instance) => GetAncestors(instance) .FirstOrDefault(operation => operation.Syntax.SpanStart >= instance.Syntax.SpanStart && operation is IConditionalAccessOperation conditionalAccess && conditionalAccess.Operation.Syntax.Equals(instance.Syntax) ) as IConditionalAccessOperation; static ILocalSymbol? GetInvocationLocalSymbol(IInvocationOperation invocation) { if (invocation.Instance is ILocalReferenceOperation localReference) return localReference.Local; if (invocation.Instance is IConditionalAccessInstanceOperation instance && GetConditionalAccessOperation(instance) is IConditionalAccessOperation conditionalAccess && conditionalAccess.Operation is ILocalReferenceOperation conditionalAccessLocalReference) { return conditionalAccessLocalReference.Local; } return null; } async Task GetLocalDeclaration(CancellationToken cancellationToken) { if (anonymousFunction.Parent is EqualsValueClauseSyntax clause && clause.Parent is VariableDeclaratorSyntax declarator && declarator.Parent is VariableDeclarationSyntax declaration) { return declaration; } var operation = semanticModel.GetOperation(anonymousFunction, cancellationToken) as IAnonymousFunctionOperation; return await GetLocalDeclaration(operation, cancellationToken).ConfigureAwait(false); } static async Task GetLocalDeclaration( IAnonymousFunctionOperation? operation, CancellationToken cancellationToken) { if (operation?.Parent is not IDelegateCreationOperation delegateCreation) return null; if (delegateCreation.Parent is IVariableInitializerOperation initializer && initializer.Parent is IVariableDeclaratorOperation declarator && declarator.Parent is IVariableDeclarationOperation declaration) { return declaration.Syntax as VariableDeclarationSyntax; } if (delegateCreation.Parent is IAssignmentOperation assignment && assignment.Target is ILocalReferenceOperation localReference) { var declaratorReference = localReference.Local.DeclaringSyntaxReferences.SingleOrDefault(); if (declaratorReference is null) return null; var node = await declaratorReference.GetSyntaxAsync(cancellationToken).ConfigureAwait(false); if (node is VariableDeclaratorSyntax declaratorSyntax) return declaratorSyntax.Parent as VariableDeclarationSyntax; } return null; } ILocalSymbol? GetLocalDeclarationSymbol(CancellationToken cancellationToken) { if (semanticModel.GetOperation(anonymousFunction, cancellationToken) is IAnonymousFunctionOperation operation && operation.Parent is IDelegateCreationOperation delegateCreation) { if (delegateCreation.Parent is IVariableInitializerOperation initializer && initializer.Parent is IVariableDeclaratorOperation declarator) { return declarator.Symbol; } if (delegateCreation.Parent is IAssignmentOperation assignment && assignment.Target is ILocalReferenceOperation localReference) { return localReference.Local; } } return null; } public SyntaxNode? GetParentFunction() => anonymousFunction.Parent?.FirstAncestorOrSelf(IsFunction); public bool ShouldFixParentFunction( SyntaxNode parentFunction, CancellationToken cancellationToken) { var symbol = GetLocalDeclarationSymbol(cancellationToken); if (symbol is null) return false; var invocations = parentFunction .DescendantNodes() .Where(node => node is InvocationExpressionSyntax) .Select(node => semanticModel.GetOperation((InvocationExpressionSyntax)node, cancellationToken) as IInvocationOperation) .Where(invocation => invocation is not null && invocation.TargetMethod.MethodKind == MethodKind.DelegateInvoke && GetInvocationLocalSymbol(invocation) is ILocalSymbol invocationLocalSymbol && SymbolEqualityComparer.Default.Equals(invocationLocalSymbol, symbol)); return invocations.Any(); } } sealed class LocalFunctionFixer( LocalFunctionStatementSyntax localFunction, SemanticModel semanticModel, DocumentEditor editor) : IFunctionFixer { public SyntaxNode Function => localFunction; public async Task Fix(CancellationToken cancellationToken) { var returnType = await AsyncHelper.GetAsyncReturnType(localFunction.ReturnType, editor, cancellationToken).ConfigureAwait(false); if (returnType is null) return; var modifiers = AsyncHelper.GetModifiersWithAsyncKeywordAdded(localFunction.Modifiers); editor.ReplaceNode(localFunction, (node, generator) => { if (node is LocalFunctionStatementSyntax localFunction) return localFunction .WithModifiers(modifiers) .WithReturnType(returnType); return node; }); } public SyntaxNode? GetParentFunction() => localFunction.Parent?.FirstAncestorOrSelf(IsFunction); public bool ShouldFixParentFunction( SyntaxNode parentFunction, CancellationToken cancellationToken) { if (semanticModel.GetOperation(localFunction, cancellationToken) is not ILocalFunctionOperation operation) return false; var symbol = operation.Symbol; if (symbol is null) return false; return parentFunction .DescendantNodes() .Where(node => node is InvocationExpressionSyntax) .Select(node => semanticModel.GetOperation((InvocationExpressionSyntax)node, cancellationToken) as IInvocationOperation) .Any(invocation => SymbolEqualityComparer.Default.Equals(invocation?.TargetMethod, symbol)); } } sealed class MethodFixer( MethodDeclarationSyntax method, DocumentEditor editor) : IFunctionFixer { public SyntaxNode Function => method; public async Task Fix(CancellationToken cancellationToken) { var returnType = await AsyncHelper.GetAsyncReturnType(method.ReturnType, editor, cancellationToken).ConfigureAwait(false); if (returnType is null) return; var modifiers = AsyncHelper.GetModifiersWithAsyncKeywordAdded(method.Modifiers); editor.ReplaceNode(method, (node, generator) => { if (node is MethodDeclarationSyntax method) return method .WithModifiers(modifiers) .WithReturnType(returnType); return node; }); } public SyntaxNode? GetParentFunction() => null; public bool ShouldFixParentFunction( SyntaxNode parentFunction, CancellationToken cancellationToken) => false; } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AssignableFromAssertionIsConfusinglyNamedFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssignableFromAssertionIsConfusinglyNamedFixer : XunitCodeFixProvider { public const string Key_UseIsType = "xUnit2032_UseIsType"; public AssignableFromAssertionIsConfusinglyNamedFixer() : base(Descriptors.X2032_AssignableFromAssertionIsConfusinglyNamed.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var simpleNameSyntax = invocation.GetSimpleName(); if (simpleNameSyntax is null) return; var methodName = simpleNameSyntax.Identifier.Text; if (!AssignableFromAssertionIsConfusinglyNamed.ReplacementMethods.TryGetValue(methodName, out var replacementName)) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseIsType(context.Document, invocation, simpleNameSyntax, replacementName, ct), Key_UseIsType, "Use Assert.{0}", replacementName ), context.Diagnostics ); } static async Task UseIsType( Document document, InvocationExpressionSyntax invocation, SimpleNameSyntax simpleName, string replacementName, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.ReplaceNode( invocation, invocation .ReplaceNode( simpleName, simpleName.WithIdentifier(Identifier(replacementName)) ) .WithArgumentList( invocation .ArgumentList .AddArguments( ParseArgumentList("false") .Arguments[0] .WithNameColon(NameColon("exactMatch")) ) ) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/AsyncAssertsShouldBeAwaitedFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AsyncAssertsShouldBeAwaitedFixer : XunitCodeFixProvider { public const string Key_AddAwait = "xUnit2021_AddAwait"; public AsyncAssertsShouldBeAwaitedFixer() : base(Descriptors.X2021_AsyncAssertionsShouldBeAwaited.Id) { } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var method = invocation.FirstAncestorOrSelf(); if (method is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; context.RegisterCodeFix( CodeAction.Create( "Add await", ct => UseAsyncAwait(context.Document, invocation, method, ct), Key_AddAwait ), context.Diagnostics ); } static async Task UseAsyncAwait( Document document, InvocationExpressionSyntax invocation, MethodDeclarationSyntax method, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var modifiers = AsyncHelper.GetModifiersWithAsyncKeywordAdded(method.Modifiers); var returnType = await AsyncHelper.GetAsyncReturnType(method.ReturnType, editor, cancellationToken).ConfigureAwait(false); var asyncThrowsInvocation = AwaitExpression(invocation.WithoutLeadingTrivia()).WithLeadingTrivia(invocation.GetLeadingTrivia()); if (returnType is not null) editor.ReplaceNode( method, method .ReplaceNode(invocation, asyncThrowsInvocation) .WithModifiers(modifiers) .WithReturnType(returnType) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/BooleanAssertsShouldNotBeNegatedFixer.cs ================================================ using System.Collections.Generic; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class BooleanAssertsShouldNotBeNegatedFixer : XunitCodeFixProvider { public const string Key_UseSuggestedAssert = "xUnit2022_UseSuggestedAssert"; public BooleanAssertsShouldNotBeNegatedFixer() : base(Descriptors.X2022_BooleanAssertionsShouldNotBeNegated.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (replacement is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseSuggestedAssert(context.Document, invocation, replacement, ct), Key_UseSuggestedAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseSuggestedAssert( Document document, InvocationExpressionSyntax invocation, string replacement, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) { if (invocation.ArgumentList.Arguments[0].Expression is PrefixUnaryExpressionSyntax prefixUnaryExpression) { var originalArguments = invocation.ArgumentList.Arguments; var newFirstArgument = Argument(prefixUnaryExpression.Operand); var newArguments = new List { newFirstArgument }; if (originalArguments.Count > 1) newArguments.AddRange(originalArguments.Skip(1)); editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(newArguments))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixer : XunitCodeFixProvider { public const string Key_UseSuggestedAssert = "xUnit2025_SimplifyBooleanAssert"; public BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixer() : base(Descriptors.X2025_BooleanAssertionCanBeSimplified.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.LiteralValue, out var isLeftLiteral)) return; if (replacement is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseSuggestedAssert(context.Document, invocation, replacement, isLeftLiteral == Constants.Asserts.True, ct), Key_UseSuggestedAssert, "Simplify the condition and use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseSuggestedAssert( Document document, InvocationExpressionSyntax invocation, string replacement, bool isLeftLiteral, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (invocation.ArgumentList.Arguments[0].Expression is BinaryExpressionSyntax binaryExpressionSyntax) { var newArgument = isLeftLiteral ? binaryExpressionSyntax.Right : binaryExpressionSyntax.Left; editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(invocation.ArgumentList.Arguments.Count > 1 ? [Argument(newArgument), invocation.ArgumentList.Arguments[1]] : new[] { Argument(newArgument) }))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer : XunitCodeFixProvider { public const string Key_UseSuggestedAssert = "xUnit2024_UseSuggestedAssert"; public BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer() : base(Descriptors.X2024_BooleanAssertionsShouldNotBeUsedForSimpleEqualityCheck.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.Replacement, out var replacement)) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.LiteralValue, out var isLeftLiteral)) return; if (replacement is null) return; var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel is null) return; context.RegisterCodeFix( XunitCodeAction.Create( ct => UseSuggestedAssert(context.Document, invocation, replacement, isLeftLiteral == Constants.Asserts.True, ct), Key_UseSuggestedAssert, "Use Assert.{0}", replacement ), context.Diagnostics ); } static async Task UseSuggestedAssert( Document document, InvocationExpressionSyntax invocation, string replacement, bool isLeftLiteral, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (invocation.ArgumentList.Arguments[0].Expression is BinaryExpressionSyntax binaryExpressionSyntax) { ArgumentSyntax[] separatedList = replacement is Constants.Asserts.Null or Constants.Asserts.NotNull ? isLeftLiteral ? [Argument(binaryExpressionSyntax.Right)] : [Argument(binaryExpressionSyntax.Left)] : isLeftLiteral ? [Argument(binaryExpressionSyntax.Left), Argument(binaryExpressionSyntax.Right)] : [Argument(binaryExpressionSyntax.Right), Argument(binaryExpressionSyntax.Left)]; editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList(separatedList))) .WithExpression(memberAccess.WithName(IdentifierName(replacement))) ); } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/UseAssertFailInsteadOfBooleanAssertFixer.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class UseAssertFailInsteadOfBooleanAssertFixer : XunitCodeFixProvider { public const string Key_UseAssertFail = "xUnit2020_UseAssertFail"; public UseAssertFailInsteadOfBooleanAssertFixer() : base(Descriptors.X2020_UseAssertFailInsteadOfBooleanAssert.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); if (invocation is null) return; context.RegisterCodeFix( CodeAction.Create( "Use Assert.Fail", ct => UseAssertFail(context.Document, invocation, ct), Key_UseAssertFail ), context.Diagnostics ); } static async Task UseAssertFail( Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (invocation.Expression is MemberAccessExpressionSyntax memberAccess) if (invocation.ArgumentList.Arguments.Count == 2) editor.ReplaceNode( invocation, invocation .WithArgumentList(ArgumentList(SeparatedList([invocation.ArgumentList.Arguments[1]]))) .WithExpression(memberAccess.WithName(IdentifierName(Constants.Asserts.Fail))) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X2000/UseGenericOverloadFix.cs ================================================ using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class UseGenericOverloadFix : XunitCodeFixProvider { public const string Key_UseAlternateAssert = "xUnit2007_xUnit2015_UseAlternateAssert"; public UseGenericOverloadFix() : base( Descriptors.X2007_AssertIsTypeShouldUseGenericOverload.Id, Descriptors.X2015_AssertThrowsShouldUseGenericOverload.Id ) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var syntaxNode = root.FindNode(context.Span); var invocation = syntaxNode.FirstAncestorOrSelf(); if (invocation is null) return; if (invocation.ArgumentList.Arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpression) return; if (invocation.Expression is not MemberAccessExpressionSyntax memberAccess) return; var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); var typeInfo = semanticModel.GetTypeInfo(typeOfExpression.Type); if (typeInfo.Type is null) return; var typeName = SymbolDisplay.ToDisplayString(typeInfo.Type, SymbolDisplayFormat.MinimallyQualifiedFormat); var methodName = memberAccess.Name.Identifier.ValueText; context.RegisterCodeFix( XunitCodeAction.Create( ct => RemoveTypeofInvocationAndAddGenericTypeAsync(context.Document, invocation, memberAccess, typeOfExpression, ct), Key_UseAlternateAssert, "Use Assert.{0}<{1}>", methodName, typeName ), context.Diagnostics ); } static async Task RemoveTypeofInvocationAndAddGenericTypeAsync( Document document, InvocationExpressionSyntax invocation, MemberAccessExpressionSyntax memberAccess, TypeOfExpressionSyntax typeOfExpression, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.ReplaceNode( invocation, invocation.WithExpression( memberAccess.WithName( GenericName( memberAccess.Name.Identifier, TypeArgumentList(SingletonSeparatedList(typeOfExpression.Type)) ) ) ) .WithArgumentList( invocation .ArgumentList .WithArguments(invocation.ArgumentList.Arguments.RemoveAt(0)) ) ); return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/X3000/CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer : XunitCodeFixProvider { public const string Key_SetBaseType = "xUnit3000_SetBaseType"; public CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer() : base(Descriptors.X3000_CrossAppDomainClassesMustBeLongLivedMarshalByRefObject.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var classDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (classDeclaration is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.NewBaseType, out var newBaseType) || newBaseType is null) return; context.RegisterCodeFix( CodeAction.Create( "Set Base Type", ct => context.Document.SetBaseClass(classDeclaration, newBaseType, ct), Key_SetBaseType ), context.Diagnostics ); } } ================================================ FILE: src/xunit.analyzers.fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixer.cs ================================================ using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class SerializableClassMustHaveParameterlessConstructorFixer : XunitCodeFixProvider { public const string Key_GenerateOrUpdateConstructor = "xUnit3001_GenerateOrUpdateConstructor"; static readonly LiteralExpressionSyntax obsoleteText = LiteralExpression(SyntaxKind.StringLiteralExpression, Literal("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")); public SerializableClassMustHaveParameterlessConstructorFixer() : base(Descriptors.X3001_SerializableClassMustHaveParameterlessConstructor.Id) { } public override FixAllProvider? GetFixAllProvider() => BatchFixer; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root is null) return; var classDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); if (classDeclaration is null) return; var diagnostic = context.Diagnostics.FirstOrDefault(); if (diagnostic is null) return; if (!diagnostic.Properties.TryGetValue(Constants.Properties.IsCtorObsolete, out var isCtorObsolete)) return; var parameterlessCtor = classDeclaration.Members.OfType().FirstOrDefault(c => c.ParameterList.Parameters.Count == 0); context.RegisterCodeFix( CodeAction.Create( parameterlessCtor is null ? "Create public constructor" : "Make parameterless constructor public", ct => CreateOrUpdateConstructor(context.Document, classDeclaration, isCtorObsolete == bool.TrueString, ct), Key_GenerateOrUpdateConstructor ), context.Diagnostics ); } static async Task CreateOrUpdateConstructor( Document document, ClassDeclarationSyntax declaration, bool isCtorObsolete, CancellationToken cancellationToken) { var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); var generator = editor.Generator; var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var parameterlessCtor = declaration.Members.OfType().FirstOrDefault(c => c.ParameterList.Parameters.Count == 0); if (parameterlessCtor is null) { var newCtor = generator.ConstructorDeclaration(); newCtor = generator.WithAccessibility(newCtor, Accessibility.Public); if (isCtorObsolete) { var obsoleteAttribute = generator.Attribute(Constants.Types.System.ObsoleteAttribute, obsoleteText); newCtor = generator.AddAttributes(newCtor, obsoleteAttribute); } editor.InsertMembers(declaration, 0, [newCtor]); } else { var updatedCtor = generator.WithAccessibility(parameterlessCtor, Accessibility.Public); if (isCtorObsolete) { var hasObsolete = parameterlessCtor .AttributeLists .SelectMany(al => al.Attributes) .Any(@as => semanticModel.GetTypeInfo(@as, cancellationToken).Type?.ToDisplayString() == Constants.Types.System.ObsoleteAttribute); if (!hasObsolete) { var obsoleteAttribute = generator.Attribute(Constants.Types.System.ObsoleteAttribute, obsoleteText); updatedCtor = generator.AddAttributes(updatedCtor, obsoleteAttribute); } } editor.ReplaceNode(parameterlessCtor, updatedCtor); } return editor.GetChangedDocument(); } } ================================================ FILE: src/xunit.analyzers.fixes/tools/install.ps1 ================================================ param($installPath, $toolsPath, $package, $project) if($project.Object.SupportsPackageDependencyResolution) { if($project.Object.SupportsPackageDependencyResolution()) { # Do not install analyzers via install.ps1, instead let the project system handle it. return } } $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve foreach($analyzersPath in $analyzersPaths) { if (Test-Path $analyzersPath) { # Install the language agnostic analyzers. foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) { if($project.Object.AnalyzerReferences) { $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) } } } } # $project.Type gives the language name like (C# or VB.NET) $languageFolder = "" if($project.Type -eq "C#") { $languageFolder = "cs" } if($project.Type -eq "VB.NET") { $languageFolder = "vb" } if($languageFolder -eq "") { return } foreach($analyzersPath in $analyzersPaths) { # Install language specific analyzers. $languageAnalyzersPath = join-path $analyzersPath $languageFolder if (Test-Path $languageAnalyzersPath) { foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) { if($project.Object.AnalyzerReferences) { $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) } } } } # SIG # Begin signature block # MIInugYJKoZIhvcNAQcCoIInqzCCJ6cCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA/i+qRUHsWzI0s # FVk99zLgt/HOEQ33uvkFsWtHTHZgf6CCDYEwggX/MIID56ADAgECAhMzAAACUosz # qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I # sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O # L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA # v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o # RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8 # q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3 # uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp # kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7 # l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u # TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1 # o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti # yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z # 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf # 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK # WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW # esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F # 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZjzCCGYsCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgRjg7DcI6 # uhYfXWwAQ6hK0mPW7iyr2tzHR0DHSDJkscIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQB3ERGpqvGnJrsyU0d9lERK2TJW4/OONhZAFjxrEvEk # PzdH0Fk0otvagAvjHzJ3q0G8C7gwRbXIyGgiYYIMefheNvgd/UKnubUGEzeG9h0/ # biX5Ro1mxuHBYvc3vqvWD292jXMg00iRmexDsTny8YgSAAWsTdkE8/W2ooEfbG1T # QkCg6ds9btpA1D1znVYpEbviCJoAfHLbNBr5nzAadgWjQM8nnb3UTvmLDIs5b1LO # 3lm9w485IBFRnfrj6QinVsCbSD7PU/N1hPY7rKfM9ScZC6QT6kjyuVVa1Ft+VYLH # qlV9hE6B4CGeB8qkko4x+MKovgbdpCgYz3eePWCakZywoYIXGTCCFxUGCisGAQQB # gjcDAwExghcFMIIXAQYJKoZIhvcNAQcCoIIW8jCCFu4CAQMxDzANBglghkgBZQME # AgEFADCCAVkGCyqGSIb3DQEJEAEEoIIBSASCAUQwggFAAgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIC58WTh4Q8r6c2kVXmD8xoHEhya2jc6YZ43KUAIy # flB4AgZh/WKJ50gYEzIwMjIwMjExMTkwMzQwLjE1M1owBIACAfSggdikgdUwgdIx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1p # Y3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhh # bGVzIFRTUyBFU046M0JENC00QjgwLTY5QzMxJTAjBgNVBAMTHE1pY3Jvc29mdCBU # aW1lLVN0YW1wIFNlcnZpY2WgghFoMIIHFDCCBPygAwIBAgITMwAAAYm0v4YwhBxL # jwABAAABiTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg # MjAxMDAeFw0yMTEwMjgxOTI3NDFaFw0yMzAxMjYxOTI3NDFaMIHSMQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQg # SXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # RVNOOjNCRDQtNEI4MC02OUMzMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvQZXxZFm # a6plmuOyvNpV8xONOwcYolZG/BjyZWGSk5JOGaLyrKId5VxVHWHlsmJE4Svnzsdp # sKmVx8otONveIUFvSceEZp8VXmu5m1fu8L7c+3lwXcibjccqtEvtQslokQVx0r+L # 54abrNDarwFG73IaRidIS1i9c+unJ8oYyhDRLrCysFAVxyQhPNZkWK7Z8/VGukaK # LAWHXCh/+R53h42gFL+9/mAALxzCXXuofi8f/XKCm7xNwVc1hONCCz6oq94AufzV # NkkIW4brUQgYpCcJm9U0XNmQvtropYDn9UtY8YQ0NKenXPtdgLHdQ8Nnv3igErKL # rWI0a5n5jjdKfwk+8mvakqdZmlOseeOS1XspQNJAK1uZllAITcnQZOcO5ofjOQ33 # ujWckAXdz+/x3o7l4AU/TSOMzGZMwhUdtVwC3dSbItpSVFgnjM2COEJ9zgCadvOi # rGDLN471jZI2jClkjsJTdgPk343TQA4JFvds/unZq0uLr+niZ3X44OBx2x+gVlln # 2c4UbZXNueA4yS1TJGbbJFIILAmTUA9Auj5eISGTbNiyWx79HnCOTar39QEKozm4 # LnTmDXy0/KI/H/nYZGKuTHfckP28wQS06rD+fDS5xLwcRMCW92DkHXmtbhGyRilB # OL5LxZelQfxt54wl4WUC0AdAEolPekODwO8CAwEAAaOCATYwggEyMB0GA1UdDgQW # BBSXbx+zR1p4IIAeguA6rHKkrfl7UDAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJl # pxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAx # MCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3Rh # bXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG # CCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4ICAQCOtLdpWUI4KwfLLrfaKrLB92Dq # bAspGWM41TaO4Jl+sHxPo522uu3GKQCjmkRWreHtlfyy9kOk7LWax3k3ke8Gtfet # fbh7qH0LeV2XOWg39BOnHf6mTcZq7FYSZZch1JDQjc98+Odlow+oWih0Dbt4CV/e # 19ZcE+1n1zzWkskUEd0f5jPIUis33p+vkY8szduAtCcIcPFUhI8Hb5alPUAPMjGz # wKb7NIKbnf8j8cP18As5IveckF0oh1cw63RY/vPK62LDYdpi7WnG2ObvngfWVKtw # iwTI4jHj2cO9q37HDe/PPl216gSpUZh0ap24mKmMDfcKp1N4mEdsxz4oseOrPYeF # sHHWJFJ6Aivvqn70KTeJpp5r+DxSqbeSy0mxIUOq/lAaUxgNSQVUX26t8r+fciko # fKv23WHrtRV3t7rVTsB9YzrRaiikmz68K5HWdt9MqULxPQPo+ppZ0LRqkOae466+ # UKRY0JxWtdrMc5vHlHZfnqjawj/RsM2S6Q6fa9T9CnY1Nz7DYBG3yZJyCPFsrgU0 # 5s9ljqfsSptpFdUh9R4ce+L71SWDLM2x/1MFLLHAMbXsEp8KloEGtaDULnxtfS2t # YhfuKGqRXoEfDPAMnIdTvQPh3GHQ4SjkkBARHL0MY75alhGTKHWjC2aLVOo8obKI # Bk8hfnFDUf/EyVw4uTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUw # DQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv # cml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z # b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw # ggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg # 4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aO # RmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41 # JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5 # LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL # 64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9 # QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj # 0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqE # UUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0 # kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435 # UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB # 3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTE # mr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwG # A1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNV # HSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNV # HQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo # 0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29m # dC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5j # cmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jv # c29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDAN # BgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4 # sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th54 # 2DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRX # ud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBew # VIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0 # DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+Cljd # QDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFr # DZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFh # bHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7n # tdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+ # oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6Fw # ZvKhggLXMIICQAIBATCCAQChgdikgdUwgdIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046M0JENC00Qjgw # LTY5QzMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoB # ATAHBgUrDgMCGgMVACGlCa3ketyeuey7bJNpWkMuiCcQoIGDMIGApH4wfDELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z # b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDlsRtBMCIY # DzIwMjIwMjEyMDEyODMzWhgPMjAyMjAyMTMwMTI4MzNaMHcwPQYKKwYBBAGEWQoE # ATEvMC0wCgIFAOWxG0ECAQAwCgIBAAICDbMCAf8wBwIBAAICEW8wCgIFAOWybMEC # AQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEK # MAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQCImCpEJ2AlAWBBkDABmkqIh1kM # LPDyea3b7evhOk+YSwXCzxnBIXuppujFT3tnk7w0p0a5YS9uwqbDM/M6rAUMBAR0 # boHamumEITNF5nVh0rlYyRZQ3WraVD2YPhouUINQavmS8ueYoh6r3HeM9QPBAnNB # vv7GDrZ637+2Dfe60jGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAABibS/hjCEHEuPAAEAAAGJMA0GCWCGSAFlAwQCAQUAoIIB # SjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIL86 # iebNndOm+CAgIp67s6y+HI1wHdhaMPILGf48RtXXMIH6BgsqhkiG9w0BCRACLzGB # 6jCB5zCB5DCBvQQgZndHMdxQV1VsbpWHOTHqWEycvcRJm7cY69l/UmT8j0UwgZgw # gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAYm0v4YwhBxL # jwABAAABiTAiBCDET+l3keOFFxaIqOZWSSuWNO774Ng/t5pe3p4QXoKcvjANBgkq # hkiG9w0BAQsFAASCAgADYrNFej7RbihwGcC0jF+cTik+HJog++dPEDXeIyBB+2pw # 23hC5KaX9H05ZknluIq2oxf2MLpKL+gA+76T3k5PnzPNJFDogUn5eFIIsMRpNF0h # MtPWoPJWYFK2odvKz1HwsuqHRg6hO//NwORcv4xPeAWEFO5+DOXzZKKp/BVDGe/D # c++y9/l41qpz/F2c3a1lugdqnZz7ZeoaQ8/JMlwrmMbciqcAytCn9A59EWJ1xYd/ # DaDhQ5Rd8hkcckuxJksjWf6URmc91cb4Jdatkyupq3dDGwCkjGNd2xetrOpqMLOZ # quoDONSgc9rGrhkf3xgKKVRhLg9bxd3f2oQ0IsOBg2AC5td1eqp6TILc0gei2E3I # uEAW1d+KXDnajvQmvQkaFHr5wEocTTLgrDglOPPhEaEumSTJS7jKFzUKHiBU005p # CgQ1So2WJ2RqFx0ppez1N1AFczOVLFllK3WGPLkDsN1GgT0nFfoqvs1WKkzyb2d2 # /v6PVER9xGky7LCu62dhsJCAFUbxF2dJxaC5ofrl98VaO/z72J9on9BTz+eCtcJ9 # rDIpqktGeL02f6+4zctFCyi2wgm6eh8kKvRlAPmN4/MNt9pWHtEV//xFGzGeDajr # diRhDoMZwsuon4QwS8b2YcKMoZ6gZ2lZah3960fTTmvBTBNqeBtR94KWCy0C0A== # SIG # End signature block ================================================ FILE: src/xunit.analyzers.fixes/tools/uninstall.ps1 ================================================ param($installPath, $toolsPath, $package, $project) if($project.Object.SupportsPackageDependencyResolution) { if($project.Object.SupportsPackageDependencyResolution()) { # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it. return } } $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve foreach($analyzersPath in $analyzersPaths) { # Uninstall the language agnostic analyzers. if (Test-Path $analyzersPath) { foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) { if($project.Object.AnalyzerReferences) { $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) } } } } # $project.Type gives the language name like (C# or VB.NET) $languageFolder = "" if($project.Type -eq "C#") { $languageFolder = "cs" } if($project.Type -eq "VB.NET") { $languageFolder = "vb" } if($languageFolder -eq "") { return } foreach($analyzersPath in $analyzersPaths) { # Uninstall language specific analyzers. $languageAnalyzersPath = join-path $analyzersPath $languageFolder if (Test-Path $languageAnalyzersPath) { foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) { if($project.Object.AnalyzerReferences) { try { $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) } catch { } } } } } # SIG # Begin signature block # MIInugYJKoZIhvcNAQcCoIInqzCCJ6cCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDC68wb97fg0QGL # yXrxJhYfmibzcOh8caqC0uZprfczDaCCDYEwggX/MIID56ADAgECAhMzAAACUosz # qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I # sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O # L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA # v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o # RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8 # q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3 # uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp # kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7 # l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u # TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1 # o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti # yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z # 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf # 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK # WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW # esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F # 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZjzCCGYsCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgF1ypFyzl # AvvWGVCeXczrfpXmJNm9vpyjcwd4y4ivfqowQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQAvi2rSDkhC82RJ4uqq/0WbHkOkzq1hrF6HxneBTNj8 # KX+niFtee3CYVfWaSAQ6xvOiLupRX3fsSfhabRQ+Jl8k28voGrTK1OC906OO3tUN # jdmv1PooWdxJNt2EbzQrap5Ui9KTUv4mJ4c836HAVMBPCJiq5NwmzAHfbgBxCaYq # +hupIf+gk8vuNB1bltILgNmU/smJt9OuGqSo5TrFajzb+35SqjnCz9JtAtbPNZvA # X9N37UPhITOecceAQmrHiEPbA7eu6VDp6VPjPfCEO7a+frWa83chEd4qzyou9xu5 # 3gnj7Ro8nFDnGyUe0+0oCaYGXO9fbIMN1HG2IZg5suj5oYIXGTCCFxUGCisGAQQB # gjcDAwExghcFMIIXAQYJKoZIhvcNAQcCoIIW8jCCFu4CAQMxDzANBglghkgBZQME # AgEFADCCAVkGCyqGSIb3DQEJEAEEoIIBSASCAUQwggFAAgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIH+XBTHuyyHZnIXrFWIe64WLvHx5GUFMCM6A56T1 # KwBtAgZh/WKJ52UYEzIwMjIwMjExMTkwMzQwLjU0OFowBIACAfSggdikgdUwgdIx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1p # Y3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhh # bGVzIFRTUyBFU046M0JENC00QjgwLTY5QzMxJTAjBgNVBAMTHE1pY3Jvc29mdCBU # aW1lLVN0YW1wIFNlcnZpY2WgghFoMIIHFDCCBPygAwIBAgITMwAAAYm0v4YwhBxL # jwABAAABiTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg # MjAxMDAeFw0yMTEwMjgxOTI3NDFaFw0yMzAxMjYxOTI3NDFaMIHSMQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQg # SXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # RVNOOjNCRDQtNEI4MC02OUMzMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvQZXxZFm # a6plmuOyvNpV8xONOwcYolZG/BjyZWGSk5JOGaLyrKId5VxVHWHlsmJE4Svnzsdp # sKmVx8otONveIUFvSceEZp8VXmu5m1fu8L7c+3lwXcibjccqtEvtQslokQVx0r+L # 54abrNDarwFG73IaRidIS1i9c+unJ8oYyhDRLrCysFAVxyQhPNZkWK7Z8/VGukaK # LAWHXCh/+R53h42gFL+9/mAALxzCXXuofi8f/XKCm7xNwVc1hONCCz6oq94AufzV # NkkIW4brUQgYpCcJm9U0XNmQvtropYDn9UtY8YQ0NKenXPtdgLHdQ8Nnv3igErKL # rWI0a5n5jjdKfwk+8mvakqdZmlOseeOS1XspQNJAK1uZllAITcnQZOcO5ofjOQ33 # ujWckAXdz+/x3o7l4AU/TSOMzGZMwhUdtVwC3dSbItpSVFgnjM2COEJ9zgCadvOi # rGDLN471jZI2jClkjsJTdgPk343TQA4JFvds/unZq0uLr+niZ3X44OBx2x+gVlln # 2c4UbZXNueA4yS1TJGbbJFIILAmTUA9Auj5eISGTbNiyWx79HnCOTar39QEKozm4 # LnTmDXy0/KI/H/nYZGKuTHfckP28wQS06rD+fDS5xLwcRMCW92DkHXmtbhGyRilB # OL5LxZelQfxt54wl4WUC0AdAEolPekODwO8CAwEAAaOCATYwggEyMB0GA1UdDgQW # BBSXbx+zR1p4IIAeguA6rHKkrfl7UDAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJl # pxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAx # MCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3Rh # bXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG # CCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4ICAQCOtLdpWUI4KwfLLrfaKrLB92Dq # bAspGWM41TaO4Jl+sHxPo522uu3GKQCjmkRWreHtlfyy9kOk7LWax3k3ke8Gtfet # fbh7qH0LeV2XOWg39BOnHf6mTcZq7FYSZZch1JDQjc98+Odlow+oWih0Dbt4CV/e # 19ZcE+1n1zzWkskUEd0f5jPIUis33p+vkY8szduAtCcIcPFUhI8Hb5alPUAPMjGz # wKb7NIKbnf8j8cP18As5IveckF0oh1cw63RY/vPK62LDYdpi7WnG2ObvngfWVKtw # iwTI4jHj2cO9q37HDe/PPl216gSpUZh0ap24mKmMDfcKp1N4mEdsxz4oseOrPYeF # sHHWJFJ6Aivvqn70KTeJpp5r+DxSqbeSy0mxIUOq/lAaUxgNSQVUX26t8r+fciko # fKv23WHrtRV3t7rVTsB9YzrRaiikmz68K5HWdt9MqULxPQPo+ppZ0LRqkOae466+ # UKRY0JxWtdrMc5vHlHZfnqjawj/RsM2S6Q6fa9T9CnY1Nz7DYBG3yZJyCPFsrgU0 # 5s9ljqfsSptpFdUh9R4ce+L71SWDLM2x/1MFLLHAMbXsEp8KloEGtaDULnxtfS2t # YhfuKGqRXoEfDPAMnIdTvQPh3GHQ4SjkkBARHL0MY75alhGTKHWjC2aLVOo8obKI # Bk8hfnFDUf/EyVw4uTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUw # DQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv # cml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z # b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw # ggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg # 4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aO # RmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41 # JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5 # LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL # 64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9 # QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj # 0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqE # UUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0 # kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435 # UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB # 3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTE # mr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwG # A1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNV # HSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNV # HQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo # 0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29m # dC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5j # cmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jv # c29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDAN # BgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4 # sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th54 # 2DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRX # ud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBew # VIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0 # DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+Cljd # QDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFr # DZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFh # bHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7n # tdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+ # oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6Fw # ZvKhggLXMIICQAIBATCCAQChgdikgdUwgdIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046M0JENC00Qjgw # LTY5QzMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoB # ATAHBgUrDgMCGgMVACGlCa3ketyeuey7bJNpWkMuiCcQoIGDMIGApH4wfDELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z # b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDlsRtBMCIY # DzIwMjIwMjEyMDEyODMzWhgPMjAyMjAyMTMwMTI4MzNaMHcwPQYKKwYBBAGEWQoE # ATEvMC0wCgIFAOWxG0ECAQAwCgIBAAICDbMCAf8wBwIBAAICEW8wCgIFAOWybMEC # AQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEK # MAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQCImCpEJ2AlAWBBkDABmkqIh1kM # LPDyea3b7evhOk+YSwXCzxnBIXuppujFT3tnk7w0p0a5YS9uwqbDM/M6rAUMBAR0 # boHamumEITNF5nVh0rlYyRZQ3WraVD2YPhouUINQavmS8ueYoh6r3HeM9QPBAnNB # vv7GDrZ637+2Dfe60jGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAABibS/hjCEHEuPAAEAAAGJMA0GCWCGSAFlAwQCAQUAoIIB # SjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIKY2 # Onyhltfi0+oc/UMKaXc0H6Ckw2gGK1/qmjRZNiXnMIH6BgsqhkiG9w0BCRACLzGB # 6jCB5zCB5DCBvQQgZndHMdxQV1VsbpWHOTHqWEycvcRJm7cY69l/UmT8j0UwgZgw # gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAYm0v4YwhBxL # jwABAAABiTAiBCDET+l3keOFFxaIqOZWSSuWNO774Ng/t5pe3p4QXoKcvjANBgkq # hkiG9w0BAQsFAASCAgB7AQ0Dv3muHoNAt+cccMfYk23lHgh8LGBitCSFwu0q7ufv # sXkoaIpwW0U0GikWhQoCH0U38SuzVbafg49FiE6ftkjOtiE03PwPYi1S6NSoDdaV # kUuvjz3OcuN1IHg3CyLn2dO8xbUlWCUfgoWhI1nax9ch7wT4Sw8RdmGKdYTZoZmq # vPXFRtDyZdmJDMDbTql/Brye8oEsDMoYKMmEYhY1t9TlusnWfUbxuBnyMqg/FkBy # QF78WFfT8mygMqUGmINxPGT6daxqmq3nfAC2vOtLT4DplNYMEymfDceJzBhb8VCT # UHc2CWK0qKT+eqwn30NBkwh//8aNHlXaA9Yq/9k2y+axIGdxFfG+X0stipRRpEXb # xCFm7FPD5/S4ddBH829yEZLZ4XTwSZ6YS/d3mFzu5rgZl3UhjOJPXx40GQtUiDP4 # XQZ/wW3154X/KtTypv62/Hl+CiMUrsO7MXtgwClfbJ3osg+zlpJgdraetVgmAUc1 # mjz2GCYX7rIliGkAJREKn4rV2MZzuGLEpTjz9dB+1Xp9Ndi9q3jQgs6k3IDIUube # YjPFFuPmFWRyi6oPTXmc4ExtTIewPvrOhwQ5q4ysxylkXoTS+UQt94BY2SvR+TMu # 6doU+0Y73xsO8Zz+lREh3fjBsDbPAgOV5989X6bmkJEEIwIK8LYgqvyED8XXTg== # SIG # End signature block ================================================ FILE: src/xunit.analyzers.fixes/xunit.analyzers.fixes.csproj ================================================ Xunit.Analyzers.Fixes netstandard2.0 ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/ClassDataAttributeMustPointAtValidClassTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class ClassDataAttributeMustPointAtValidClassTests { const string SupportedV2 = "IEnumerable"; const string SupportedV3 = "IEnumerable, IAsyncEnumerable, IEnumerable, or IAsyncEnumerable"; public class SuccessCases { [Fact] public async ValueTask v2_only() { var source = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; class DataClassObjectArray: IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class TestClass { [Theory] [ClassData(typeof(DataClassObjectArray))] public void TestMethod(int n) { } } """; await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp7_1, source); } [Fact] public async ValueTask v3_only() { var source = /* lang=c#-test */ """ #nullable enable using System.Collections; using System.Collections.Generic; using System.Threading; using Xunit; class DataClass_TheoryDataRow_1: IEnumerable> { public IEnumerator> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_TheoryDataRow_1_Async : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } class DataClass_TheoryDataRow_2: IEnumerable> { public IEnumerator> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_TheoryDataRow_2_Async : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } class DataClass_TheoryDataRow_2A: IEnumerable> { public IEnumerator> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_TheoryDataRow_2A_Async : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } class DataClass_TheoryDataRow_3: IEnumerable> { public IEnumerator> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_TheoryDataRow_3_Async : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } class DataClass_Tuple_Named : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } class DataClass_Tuple_Misnamed : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } class DataClass_Tuple_Unnamed : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class TestClass { [Theory] [ClassData(typeof(DataClass_TheoryDataRow_1))] [ClassData(typeof(DataClass_TheoryDataRow_1_Async))] public void TestMethod1(int n) { } [Theory] [ClassData(typeof(DataClass_TheoryDataRow_1))] [ClassData(typeof(DataClass_TheoryDataRow_1_Async))] public void TestMethod1Generic1(T t) { } [Theory] [ClassData(typeof(DataClass_TheoryDataRow_1))] [ClassData(typeof(DataClass_TheoryDataRow_1_Async))] public void TestMethod1Generic2(T? t) { } [Theory] [ClassData(typeof(DataClass_TheoryDataRow_1))] [ClassData(typeof(DataClass_TheoryDataRow_1_Async))] [ClassData(typeof(DataClass_TheoryDataRow_2))] [ClassData(typeof(DataClass_TheoryDataRow_2_Async))] public void TestMethod2(int n, string s = "") { } [Theory] [ClassData(typeof(DataClass_TheoryDataRow_1))] [ClassData(typeof(DataClass_TheoryDataRow_1_Async))] [ClassData(typeof(DataClass_TheoryDataRow_2))] [ClassData(typeof(DataClass_TheoryDataRow_2_Async))] [ClassData(typeof(DataClass_TheoryDataRow_2A))] [ClassData(typeof(DataClass_TheoryDataRow_2A_Async))] [ClassData(typeof(DataClass_TheoryDataRow_3))] [ClassData(typeof(DataClass_TheoryDataRow_3_Async))] public void TestMethod3(int n, params string[] a) { } [Theory] [ClassData(typeof(DataClass_Tuple_Named))] [ClassData(typeof(DataClass_Tuple_Misnamed))] [ClassData(typeof(DataClass_Tuple_Unnamed))] public void TestMethod4((int x, int y) point) { } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source); } } public class X1007_ClassDataAttributeMustPointAtValidClass { [Fact] public async ValueTask v2_only() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading; using Xunit; public class DataClass_Async : IAsyncEnumerable { public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class TestClass { [Theory] [{|#0:ClassData(typeof(DataClass_Async))|}] public void TestMethod(int n) { } } """; var expected = Verify.Diagnostic("xUnit1007").WithLocation(0).WithArguments("DataClass_Async", SupportedV2); await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp7_1, source, expected); } [Fact] public async ValueTask v2_and_v3() { var source = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; class DataClass_Enumerable_Object: IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } abstract class DataClass_Abstract: IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_NoParameterlessCtor: IEnumerable { public DataClass_NoParameterlessCtor(string parameter) { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_InternalCtor: IEnumerable { internal DataClass_InternalCtor() { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_PrivateCtor: IEnumerable { internal DataClass_PrivateCtor() { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class TestClass { [Theory] [{|#0:ClassData(typeof(DataClass_Enumerable_Object))|}] [{|#1:ClassData(typeof(DataClass_Abstract))|}] [{|#2:ClassData(typeof(DataClass_NoParameterlessCtor))|}] [{|#3:ClassData(typeof(DataClass_InternalCtor))|}] [{|#4:ClassData(typeof(DataClass_PrivateCtor))|}] public void TestMethod(int n) { } } """; var expectedV2 = new[] { Verify.Diagnostic("xUnit1007").WithLocation(0).WithArguments("DataClass_Enumerable_Object", SupportedV2), Verify.Diagnostic("xUnit1007").WithLocation(1).WithArguments("DataClass_Abstract", SupportedV2), Verify.Diagnostic("xUnit1007").WithLocation(2).WithArguments("DataClass_NoParameterlessCtor", SupportedV2), Verify.Diagnostic("xUnit1007").WithLocation(3).WithArguments("DataClass_InternalCtor", SupportedV2), Verify.Diagnostic("xUnit1007").WithLocation(4).WithArguments("DataClass_PrivateCtor", SupportedV2), }; var expectedV3 = new[] { Verify.Diagnostic("xUnit1007").WithLocation(0).WithArguments("DataClass_Enumerable_Object", SupportedV3), Verify.Diagnostic("xUnit1007").WithLocation(1).WithArguments("DataClass_Abstract", SupportedV3), Verify.Diagnostic("xUnit1007").WithLocation(2).WithArguments("DataClass_NoParameterlessCtor", SupportedV3), Verify.Diagnostic("xUnit1007").WithLocation(3).WithArguments("DataClass_InternalCtor", SupportedV3), Verify.Diagnostic("xUnit1007").WithLocation(4).WithArguments("DataClass_PrivateCtor", SupportedV3), }; await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp7_1, source, expectedV2); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, source, expectedV3); } #if ROSLYN_LATEST && !NETFRAMEWORK [Fact] public async ValueTask v3_only() { var source = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; class DataClass_Enumerable_Object: IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } abstract class DataClass_Abstract: IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_NoParameterlessCtor: IEnumerable { public DataClass_NoParameterlessCtor(string parameter) { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_InternalCtor: IEnumerable { internal DataClass_InternalCtor() { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class DataClass_PrivateCtor: IEnumerable { internal DataClass_PrivateCtor() { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class TestClass { [Theory] [{|#0:ClassData|}] [{|#1:ClassData|}] [{|#2:ClassData|}] [{|#3:ClassData|}] [{|#4:ClassData|}] public void TestMethod(int n) { } } """; var expectedV3 = new[] { Verify.Diagnostic("xUnit1007").WithLocation(0).WithArguments("DataClass_Enumerable_Object", SupportedV3), Verify.Diagnostic("xUnit1007").WithLocation(1).WithArguments("DataClass_Abstract", SupportedV3), Verify.Diagnostic("xUnit1007").WithLocation(2).WithArguments("DataClass_NoParameterlessCtor", SupportedV3), Verify.Diagnostic("xUnit1007").WithLocation(3).WithArguments("DataClass_InternalCtor", SupportedV3), Verify.Diagnostic("xUnit1007").WithLocation(4).WithArguments("DataClass_PrivateCtor", SupportedV3), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp11, source, expectedV3); } #endif // ROSLYN_LATEST && !NETFRAMEWORK } public class X1037_TheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters { [Fact] public async Task v3_only() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading; using Xunit; public class DataClass : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class TestClass { [Theory] [{|#0:ClassData(typeof(DataClass))|}] public void TestMethod(int n, string f) { } } """; var expected = Verify.Diagnostic("xUnit1037").WithLocation(0).WithArguments("Xunit.TheoryDataRow"); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, source, expected); } } public class X1038_TheoryDataTypeArgumentsMustMatchTestMethodParameters_ExtraTypeParameters { [Fact] public async Task v3_only() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading; using Xunit; public class DataClass1 : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class DataClass2 : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class TestClass { [Theory] [{|#0:ClassData(typeof(DataClass1))|}] public void TestMethod1(int n) { } [Theory] [ClassData(typeof(DataClass1))] [{|#1:ClassData(typeof(DataClass2))|}] public void TestMethod2(int n, params double[] d) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1038").WithLocation(0).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1038").WithLocation(1).WithArguments("Xunit.TheoryDataRow"), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, source, expected); } } public class X1039_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleTypes { [Fact] public async Task v3_only() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading; using Xunit; public class DataClass1 : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class DataClass2 : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class TestClass { [Theory] [ClassData(typeof(DataClass1))] public void TestMethod1(int n, {|#0:double|} d) { } [Theory] [ClassData(typeof(DataClass2))] public void TestMethod2(int n, params {|#1:string[]|} s) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("string", "DataClass1", "d"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "DataClass2", "s"), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, source, expected); } } public class X1040_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleNullability { [Fact] public async Task v3_only() { var source = /* lang=c#-test */ """ #nullable enable using System.Collections.Generic; using System.Threading; using Xunit; public class DataClass : IAsyncEnumerable> { public IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class TestClass { [Theory] [ClassData(typeof(DataClass))] public void TestMethod({|#0:string|} s) { } } """; var expected = Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "DataClass", "s"); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source, expected); } } public class X1050_ClassDataTheoryDataRowIsRecommendedForStronglyTypedAnalysis { [Fact] public async Task v3_only() { var source = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using System.Threading; using Xunit; public class DataClass_ObjectArray : IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class DataClass_ObjectArray_Async : IAsyncEnumerable { public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class DataClass_ITheoryDataRow : IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class DataClass_ITheoryDataRow_Async : IAsyncEnumerable { public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class DataClass_TheoryDataRow : IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class DataClass_TheoryDataRow_Async : IAsyncEnumerable { public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } public class TestClass { [Theory] [{|xUnit1050:ClassData(typeof(DataClass_ObjectArray))|}] [{|xUnit1050:ClassData(typeof(DataClass_ObjectArray_Async))|}] [{|xUnit1050:ClassData(typeof(DataClass_ITheoryDataRow))|}] [{|xUnit1050:ClassData(typeof(DataClass_ITheoryDataRow_Async))|}] [{|xUnit1050:ClassData(typeof(DataClass_TheoryDataRow))|}] [{|xUnit1050:ClassData(typeof(DataClass_TheoryDataRow_Async))|}] public void TestMethod(int n) { } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, source); } } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/CollectionDefinitionClassesMustBePublicTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class CollectionDefinitionClassesMustBePublicTests { [Fact] public async Task ForPublicClass_DoesNotTrigger() { var source = /* lang=c#-test */ """ [Xunit.CollectionDefinition("MyCollection")] public class CollectionDefinitionClass { } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("")] [InlineData("internal ")] public async Task ForFriendOrInternalClass_Triggers(string classAccessModifier) { var source = string.Format(/* lang=c#-test */ """ [Xunit.CollectionDefinition("MyCollection")] {0}class [|CollectionDefinitionClass|] {{ }} """, classAccessModifier); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("")] [InlineData("public ")] public async Task ForPartialClassInSameFile_WhenClassIsPublic_DoesNotTrigger(string otherPartAccessModifier) { var source = string.Format(/* lang=c#-test */ """ [Xunit.CollectionDefinition("MyCollection")] public partial class CollectionDefinitionClass {{ }} {0}partial class CollectionDefinitionClass {{ }} """, otherPartAccessModifier); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("", "")] [InlineData("", "internal ")] [InlineData("internal ", "internal ")] public async Task ForPartialClassInSameFile_WhenClassIsNonPublic_Triggers( string part1AccessModifier, string part2AccessModifier) { var source = string.Format(/* lang=c#-test */ """ [Xunit.CollectionDefinition("MyCollection")] {0}partial class {{|#0:CollectionDefinitionClass|}} {{ }} {1}partial class {{|#1:CollectionDefinitionClass|}} {{ }} """, part1AccessModifier, part2AccessModifier); var expected = Verify.Diagnostic().WithLocation(0).WithLocation(1); await Verify.VerifyAnalyzer(source, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/ConstructorsOnFactAttributeSubclassShouldBePublicTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class ConstructorsOnFactAttributeSubclassShouldBePublicTests { [Fact] public async Task DefaultConstructor_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using Xunit; [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class CustomFactAttribute : FactAttribute { } public class Tests { [CustomFact] public void TestCustomFact() { } [Fact] public void TestFact() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ParameterlessPublicConstructor_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using Xunit; [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class CustomFactAttribute : FactAttribute { public CustomFactAttribute() { this.Skip = "xxx"; } } public class Tests { [CustomFact] public void TestCustomFact() { } [Fact] public void TestFact() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task PublicConstructorWithParameters_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using Xunit; [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class CustomFactAttribute : FactAttribute { public CustomFactAttribute(string skip) { this.Skip = skip; } } public class Tests { [CustomFact("blah")] public void TestCustomFact() { } [Fact] public void TestFact() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task PublicConstructorWithOtherConstructors_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using Xunit; [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class CustomFactAttribute : FactAttribute { public CustomFactAttribute() { this.Skip = "xxx"; } internal CustomFactAttribute(string skip) { this.Skip = skip; } } public class Tests { [CustomFact] public void TestCustomFact() { } [Fact] public void TestFact() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task InternalConstructor_Triggers() { var source = /* lang=c#-test */ """ using System; using Xunit; [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class CustomFactAttribute : FactAttribute { internal CustomFactAttribute(string skip, params int[] values) { } } public class Tests { [{|#0:CustomFact("Skip", 42)|}] public void TestCustomFact() { } [Fact] public void TestFact() { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("CustomFactAttribute.CustomFactAttribute(string, params int[])"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ProtectedInternalConstructor_Triggers() { var source = /* lang=c#-test */ """ using System; using Xunit; [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] internal sealed class CustomFactAttribute : FactAttribute { protected internal CustomFactAttribute() { this.Skip = "xxx"; } } public class Tests { [{|#0:CustomFact|}] public void TestCustomFact() { } [Fact] public void TestFact() { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("CustomFactAttribute.CustomFactAttribute()"); await Verify.VerifyAnalyzer(source, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/DataAttributeShouldBeUsedOnATheoryTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class DataAttributeShouldBeUsedOnATheoryTests { [Fact] public async Task FactMethodWithNoDataAttributes_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("InlineData")] [InlineData("MemberData(\"\")")] [InlineData("ClassData(typeof(string))")] public async Task FactMethodWithDataAttributes_DoesNotTrigger(string dataAttribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Fact] [Xunit.{0}] public void TestMethod() {{ }} }} """, dataAttribute); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("InlineData")] [InlineData("MemberData(\"\")")] [InlineData("ClassData(typeof(string))")] public async Task TheoryMethodWithDataAttributes_DoesNotTrigger(string dataAttribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.{0}] public void TestMethod() {{ }} }} """, dataAttribute); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("InlineData")] [InlineData("MemberData(\"\")")] [InlineData("ClassData(typeof(string))")] public async Task MethodsWithDataAttributesButNotFactOrTheory_Triggers(string dataAttribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.{0}] public void [|TestMethod|]() {{ }} }} """, dataAttribute); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseAsyncVoidForTestMethodsTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class DoNotUseAsyncVoidForTestMethodsTests { [Fact] public async Task NonTestMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; public class MyClass { public async void MyMethod() { await Task.Yield(); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task NonAsyncTestMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task AsyncTaskMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { await Task.Yield(); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task AsyncValueTaskMethod_V3_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async ValueTask TestMethod() { await Task.Yield(); } } """; await Verify.VerifyAnalyzerV3(source); } [Fact] public async Task AsyncVoidMethod_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async void {|#0:TestMethod|}() { await Task.Yield(); } } """; var expectedV2 = Verify.Diagnostic("xUnit1048").WithLocation(0); var expectedV3 = Verify.Diagnostic("xUnit1049").WithLocation(0); await Verify.VerifyAnalyzerV2(source, expectedV2); await Verify.VerifyAnalyzerV3(source, expectedV3); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseBlockingTaskOperationsTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class DoNotUseBlockingTaskOperationsTests { [Fact] public async Task SuccessCase() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { await Task.Delay(1); } } """; await Verify.VerifyAnalyzer(source); } public class IValueTaskSource_NonGeneric { [Fact] public async Task GetResult_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks.Sources; using Xunit; public class TestClass { [Fact] public void TestMethod() { default(IValueTaskSource).[|GetResult(0)|]; Action _ = vts => vts.GetResult(0); void LocalFunction() { default(IValueTaskSource).GetResult(0); } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } } public class IValueTaskSource_Generic { [Fact] public async Task GetResult_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks.Sources; using Xunit; public class TestClass { [Fact] public void TestMethod() { default(IValueTaskSource).[|GetResult(0)|]; Func, int> _ = vts => vts.GetResult(0); void LocalFunction() { default(IValueTaskSource).GetResult(0); } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } } public class Task_NonGeneric { public class Wait { [Fact] public async Task Wait_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { Task.Delay(1).[|Wait()|]; Action _ = t => t.Wait(); void LocalFunction() { Task.Delay(1).Wait(); } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] public async Task Wait_BeforeWhenAll_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task = Task.Delay(1); task.[|Wait()|]; await Task.WhenAll(task); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task Wait_ForUnawaitedTask_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.Delay(1); var task2 = Task.Delay(2); await Task.WhenAll(new[] { task1 }); task1.Wait(); task2.[|Wait()|]; } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task Wait_InLambda_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { Task.CompletedTask.ContinueWith(x => x.Wait()); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task Wait_AfterWhenAll_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.Delay(1); var task2 = Task.Delay(2); await Task.WhenAll(task1, task2); task1.Wait(); task2.Wait(); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task Wait_AfterWhenAny_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.Delay(1); var task2 = Task.Delay(2); var finishedTask = await Task.WhenAny(task1, task2); finishedTask.Wait(); } } """; await Verify.VerifyAnalyzer(source); } } public class WaitAny_WaitAll { [Theory] [InlineData("WaitAny")] [InlineData("WaitAll")] public async Task WaitMethod_Triggers(string waitMethod) { var source = string.Format(/* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ Task.[|{0}(Task.Delay(1))|]; Action _ = t => Task.{0}(t); void LocalFunction() {{ Task.{0}(Task.Delay(1)); }} }} }} """, waitMethod); await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Theory] [InlineData("WaitAny")] [InlineData("WaitAll")] public async Task WaitMethod_BeforeWhenAll_Triggers(string waitMethod) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var task = Task.Delay(1); Task.[|{0}(task)|]; await Task.WhenAll(task); }} }} """, waitMethod); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("WaitAny")] [InlineData("WaitAll")] public async Task WaitMethod_ForUnawaitedTask_Triggers(string waitMethod) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var task1 = Task.Delay(1); var task2 = Task.Delay(2); await Task.WhenAll(new[] {{ task1 }}); Task.{0}(task1); Task.[|{0}(task2)|]; Task.[|{0}(task1, task2)|]; }} }} """, waitMethod); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("WaitAny")] [InlineData("WaitAll")] public async Task WaitMethod_InLambda_DoesNotTrigger(string waitMethod) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ Task.CompletedTask.ContinueWith(x => Task.{0}(x)); }} }} """, waitMethod); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("WaitAny")] [InlineData("WaitAll")] public async Task WaitMethod_AfterWhenAll_DoesNotTrigger(string waitMethod) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var task1 = Task.Delay(1); var task2 = Task.Delay(2); await Task.WhenAll(task1, task2); Task.{0}(task1); Task.{0}(task2); Task.{0}(task1, task2); }} }} """, waitMethod); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("WaitAny")] [InlineData("WaitAll")] public async Task WaitMethod_AfterWhenAny_DoesNotTrigger(string waitMethod) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var task1 = Task.Delay(1); var task2 = Task.Delay(2); var finishedTask = await Task.WhenAny(task1, task2); Task.{0}(finishedTask); }} }} """, waitMethod); await Verify.VerifyAnalyzer(source); } } public class GetAwaiterGetResult { [Fact] public async Task GetResult_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { Task.CompletedTask.GetAwaiter().[|GetResult()|]; Action _ = t => t.GetAwaiter().GetResult(); void LocalFunction() { Task.CompletedTask.GetAwaiter().GetResult(); } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] public async Task GetResult_BeforeWhenAll_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task = Task.Delay(1); task.GetAwaiter().[|GetResult()|]; await Task.WhenAll(task); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GetResult_OnUnawaitedTask_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.Delay(1); var task2 = Task.Delay(2); await Task.WhenAll(new[] { task1 }); task1.GetAwaiter().GetResult(); task2.GetAwaiter().[|GetResult()|]; } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GetResult_InLambda_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { Task.CompletedTask.ContinueWith(x => x.GetAwaiter().GetResult()); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GetResult_AfterWhenAll_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.Delay(1); var task2 = Task.Delay(2); await Task.WhenAll(task1, task2); task1.GetAwaiter().GetResult(); task2.GetAwaiter().GetResult(); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GetResult_AfterWhenAny_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.Delay(1); var task2 = Task.Delay(2); var finishedTask = await Task.WhenAny(task1, task2); finishedTask.GetAwaiter().GetResult(); } } """; await Verify.VerifyAnalyzer(source); } } } public class Task_Generic { public class Result { [Fact] public async Task Result_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { var _ = Task.FromResult(42).[|Result|]; Func, int> _2 = t => t.Result; void LocalFunction() { var _3 = Task.FromResult(42).Result; } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] public async Task Result_BeforeWhenAll_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task = Task.FromResult(42); Assert.Equal(42, task.[|Result|]); await Task.WhenAll(task); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task Result_ForUnawaitedTask_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.FromResult(42); var task2 = Task.FromResult(2112); await Task.WhenAll(new[] { task1 }); Assert.Equal(42, task1.Result); Assert.Equal(2112, task2.[|Result|]); Assert.Equal(2154, task1.Result + task2.[|Result|]); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task Result_InLambda_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { var _ = Task.FromResult(42).ContinueWith(x => x.Result); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task Result_AfterWhenAll_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.FromResult(42); var task2 = Task.FromResult(2112); await Task.WhenAll(task1, task2); Assert.Equal(42, task1.Result); Assert.Equal(2112, task2.Result); Assert.Equal(2154, task1.Result + task2.Result); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task Result_AfterWhenAny_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.FromResult(42); var task2 = Task.FromResult(2112); var finishedTask = await Task.WhenAny(task1, task2); Assert.Equal(2600, finishedTask.Result); } } """; await Verify.VerifyAnalyzer(source); } } public class GetAwaiterGetResult { [Fact] public async Task GetResult_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { var _ = Task.FromResult(42).GetAwaiter().[|GetResult()|]; Func, int> _2 = t => t.GetAwaiter().GetResult(); void LocalFunction() { var _3 = Task.FromResult(42).GetAwaiter().GetResult(); } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] public async Task GetResult_BeforeWhenAll_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task = Task.FromResult(42); Assert.Equal(42, task.GetAwaiter().[|GetResult()|]); await Task.WhenAll(task); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GetResult_OnUnawaitedTask_Triggers() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.FromResult(42); var task2 = Task.FromResult(2112); await Task.WhenAll(new[] { task1 }); Assert.Equal(42, task1.GetAwaiter().GetResult()); Assert.Equal(2112, task2.GetAwaiter().[|GetResult()|]); Assert.Equal(2154, task1.GetAwaiter().GetResult() + task2.GetAwaiter().[|GetResult()|]); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GetResult_InLambda_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { var _ = Task.FromResult(42).ContinueWith(x => x.GetAwaiter().GetResult()); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GetResult_AfterWhenAll_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.FromResult(42); var task2 = Task.FromResult(2112); await Task.WhenAll(task1, task2); Assert.Equal(42, task1.GetAwaiter().GetResult()); Assert.Equal(2112, task2.GetAwaiter().GetResult()); Assert.Equal(2154, task1.GetAwaiter().GetResult() + task2.GetAwaiter().GetResult()); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GetResult_AfterWhenAny_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var task1 = Task.FromResult(42); var task2 = Task.FromResult(2112); var finishedTask = await Task.WhenAny(task1, task2); Assert.Equal(2600, finishedTask.GetAwaiter().GetResult()); } } """; await Verify.VerifyAnalyzer(source); } } } public class ValueTask_NonGeneric { [Fact] public async Task GetResult_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { default(ValueTask).GetAwaiter().[|GetResult()|]; Action _ = vt => vt.GetAwaiter().GetResult(); void LocalFunction() { default(ValueTask).GetAwaiter().GetResult(); } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } } public class ValueTask_Generic { [Fact] public async Task Result_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { var _ = new ValueTask(42).[|Result|]; Func, int> _2 = vt => vt.Result; void LocalFunction() { var _3 = new ValueTask(42).Result; } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] public async Task GetResult_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { var _ = new ValueTask(42).GetAwaiter().[|GetResult()|]; Func, int> _2 = vt => vt.GetAwaiter().GetResult(); void LocalFunction() { var _3 = new ValueTask(42).GetAwaiter().GetResult(); } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseConfigureAwaitTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class DoNotUseConfigureAwaitTests { [Fact] public async Task NoCall_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { await Task.Delay(1); } } """; await Verify.VerifyAnalyzer(source); } public class ConfigureAwait_Boolean { [Fact] public async Task NonTestMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class NonTestClass { public async Task NonTestMethod() { await Task.Delay(1).ConfigureAwait(false); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task True_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { await Task.Delay(1).ConfigureAwait(true); } } """; await Verify.VerifyAnalyzer(source); } public static TheoryData InvalidValues = [ "false", // Literal false "1 == 2", // Logical false (we don't compute) "1 == 1", // Logical true (we don't compute) "booleanVar", // Reference value (we don't do lookup) ]; [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_InsideLambda_DoesNotTrigger(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var t = Task.Run(async () => {{ await Task.Delay(1).ConfigureAwait({0}); }}); await t; }} }} """, argumentValue); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_InsideLocalFunction_DoesNotTrigger(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; async Task AssertEventStateAsync() {{ await Task.Delay(1).ConfigureAwait({0}); }} }} }} """, argumentValue); await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_TaskWithAwait_Triggers(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; await Task.Delay(1).{{|#0:ConfigureAwait({0})|}}; }} }} """, argumentValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007."); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_TaskWithoutAwait_Triggers(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var booleanVar = true; Task.Delay(1).{{|#0:ConfigureAwait({0})|}}.GetAwaiter().GetResult(); }} }} """, argumentValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007."); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_TaskOfT_Triggers(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var task = Task.FromResult(42); await task.{{|#0:ConfigureAwait({0})|}}; }} }} """, argumentValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007."); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_ValueTask_Triggers(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var valueTask = default(ValueTask); await valueTask.{{|#0:ConfigureAwait({0})|}}; }} }} """, argumentValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007."); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_ValueTaskOfT_Triggers(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var valueTask = default(ValueTask); await valueTask.{{|#0:ConfigureAwait({0})|}}; }} }} """, argumentValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007."); await Verify.VerifyAnalyzer(source, expected); } } #if NETCOREAPP public class ConfigureAwait_ConfigureAwaitOptions { [Fact] public async Task NonTestMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class NonTestClass { public async Task NonTestMethod() { await Task.Delay(1).ConfigureAwait(ConfigureAwaitOptions.None); } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("ConfigureAwaitOptions.ContinueOnCapturedContext")] [InlineData("ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ContinueOnCapturedContext")] [InlineData("ConfigureAwaitOptions.ForceYielding | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ContinueOnCapturedContext")] public async Task ValidValue_DoesNotTrigger(string enumValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ await Task.Delay(1).ConfigureAwait({0}); }} }} """, enumValue); await Verify.VerifyAnalyzer(source); } public static TheoryData InvalidValues = [ // Literal values "ConfigureAwaitOptions.None", "ConfigureAwaitOptions.SuppressThrowing", "ConfigureAwaitOptions.ForceYielding | ConfigureAwaitOptions.SuppressThrowing", // Reference values (we don't do lookup) "enumVar", ]; [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_InsideLambda_DoesNotTrigger(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; var t = Task.Run(async () => {{ await Task.Delay(1).ConfigureAwait({0}); }}); await t; }} }} """, argumentValue); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_InsideLocalFunction_DoesNotTrigger(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; async Task AssertEventStateAsync() {{ await Task.Delay(1).ConfigureAwait({0}); }} }} }} """, argumentValue); await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_TaskWithAwait_Triggers(string enumValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; await Task.Delay(1).{{|#0:ConfigureAwait({0})|}}; }} }} """, enumValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(enumValue, "Ensure ConfigureAwaitOptions.ContinueOnCapturedContext in the flags."); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_TaskWithoutAwait_Triggers(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; Task.Delay(1).{{|#0:ConfigureAwait({0})|}}.GetAwaiter().GetResult(); }} }} """, argumentValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(argumentValue, "Ensure ConfigureAwaitOptions.ContinueOnCapturedContext in the flags."); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(InvalidValues))] public async Task InvalidValue_TaskOfT_Triggers(string argumentValue) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; var task = Task.FromResult(42); await task.{{|#0:ConfigureAwait({0})|}}; }} }} """, argumentValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(argumentValue, "Ensure ConfigureAwaitOptions.ContinueOnCapturedContext in the flags."); await Verify.VerifyAnalyzer(source, expected); } } #endif } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/EnsureFixturesHaveASourceTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class EnsureFixturesHaveASourceTests { public class NonTestClass { [Fact] public async Task DoesNotTrigger() { var source = /* lang=c#-test */ """ public class NonTestClass { public NonTestClass(object _) { } } """; await Verify.VerifyAnalyzer(source); } } public class SupportedNonFixtureData { [Theory] [InlineData("")] [InlineData("[Collection(\"TestCollection\")]")] public async Task SupportedTypes_V2_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ using Xunit; using Xunit.Abstractions; {0} public class TestClass {{ public TestClass(ITestOutputHelper _) {{ }} [Fact] public void TestMethod() {{ }} }} """, attribute); await Verify.VerifyAnalyzerV2(source); } [Theory] [InlineData("")] [InlineData("[Collection(\"TestCollection\")]")] public async Task SupportedTypes_V3_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ using Xunit; using Xunit.v3; {0} public class TestClass {{ public TestClass(ITestOutputHelper _1, ITestContextAccessor _2) {{ }} [Fact] public void TestMethod() {{ }} }} """, attribute); await Verify.VerifyAnalyzerV3(source); } [Fact] public async Task OptionalParameter_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public TestClass(bool value = true) { } [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ParamsParameter_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public TestClass(params object[] _) { } [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } } public class ClassFixtures { [Theory] // Everything on the base type [InlineData( "[Collection(\"TestCollection\")]", ": IClassFixture", "", "")] // Everything on the derived type [InlineData( "", "", "[Collection(\"TestCollection\")]", ", IClassFixture")] // Fixture on the base type, collection on the derived type [InlineData( "", ": IClassFixture", "[Collection(\"TestCollection\")]", "")] // Collection on the base type, fixture on the derived type [InlineData( "[Collection(\"TestCollection\")]", "", "", ", IClassFixture")] public async Task BaseClassParameter_DerivedClassFixture_DoesNotTrigger( string baseAttribute, string baseInterface, string derivedAttribute, string derivedInterface) { var source = string.Format(/* lang=c#-test */ """ using Xunit; {0} public abstract class BaseClass {1} {{ }} {2} public class TestClass : BaseClass {3} {{ public TestClass(object _) {{ }} [Fact] public void TestMethod() {{ }} }} """, baseAttribute, baseInterface, derivedAttribute, derivedInterface); await Verify.VerifyAnalyzer(source); } [Fact] public async Task ClassFixtureOnCollectionDefinition_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; [CollectionDefinition(nameof(TestCollection))] public class TestCollection : IClassFixture { } [Collection(nameof(TestCollection))] public class TestClass { public TestClass(object _) { } [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MissingClassFixtureDefinition_Triggers() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public TestClass(object [|_|]) { } [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } } public class CollectionFixtures { [Theory] [InlineData("")] [InlineData("[CollectionDefinition(nameof(TestCollection))]")] public async Task NoFixture_DoesNotTrigger(string definitionAttribute) { var source = string.Format(/* lang=c#-test */ """ using Xunit; {0} public class TestCollection {{ }} [Collection(nameof(TestCollection))] public class TestClass {{ [Fact] public void TestMethod() {{ }} }} """, definitionAttribute); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("[CollectionDefinition(nameof(TestCollection))]", "[Collection(nameof(TestCollection))]", true)] [InlineData("", "[Collection(typeof(TestCollection))]", false)] #if NETCOREAPP && ROSLYN_LATEST // C# 11 is required for generic attributes [InlineData("", "[Collection]", false, LanguageVersion.CSharp11)] #endif public async Task WithInheritedFixture_DoesNotTrigger( string collectionDefinition, string collectionReference, bool supportedByV2, LanguageVersion? languageVersion = null) { var source = string.Format(/* lang=c#-test */ """ using Xunit; public class Fixture {{ }} {0} public class TestCollection : ICollectionFixture {{ }} public abstract class TestContext {{ protected TestContext(Fixture fixture) {{ }} }} {1} public class TestClass : TestContext {{ public TestClass(Fixture fixture) : base(fixture) {{ }} [Fact] public void TestMethod() {{ }} }} """, collectionDefinition, collectionReference); if (supportedByV2) await Verify.VerifyAnalyzerV2(languageVersion ?? LanguageVersion.CSharp6, source); await Verify.VerifyAnalyzerV3(languageVersion ?? LanguageVersion.CSharp6, source); } [Fact] public async Task WithGenericFixture_TriggersWithV2_DoesNotTriggerWithV3() { var source = /* lang=c#-test */ """ using Xunit; public class Fixture { } [CollectionDefinition("test")] public class TestCollection : ICollectionFixture> { } [Collection("test")] public class TestClass { public TestClass(Fixture {|#0:fixture|}) { } [Fact] public void TestMethod() { } } """; var expectedV2 = Verify.Diagnostic().WithLocation(0).WithArguments("fixture"); await Verify.VerifyAnalyzerV2(source, expectedV2); await Verify.VerifyAnalyzerV3(source); } [Fact] public async Task WithInheritedGenericFixture_TriggersWithV2_DoesNotTriggerWithV3() { var source = /* lang=c#-test */ """ using Xunit; public class Fixture { } [CollectionDefinition("test")] public class TestCollection : ICollectionFixture> { } [Collection("test")] public abstract class TestContext { protected TestContext(Fixture fixture) { } } public class TestClass : TestContext { public TestClass(Fixture {|#0:fixture|}) : base(fixture) { } [Fact] public void TestMethod() { } } """; var expectedV2 = Verify.Diagnostic().WithLocation(0).WithArguments("fixture"); await Verify.VerifyAnalyzerV2(source, expectedV2); await Verify.VerifyAnalyzerV3(source); } [Theory] [InlineData("[Collection(nameof(TestCollection))]", "")] [InlineData("", "[Collection(nameof(TestCollection))]")] public async Task WithFixture_SupportsDerivation( string baseAttribute, string derivedAttribute) { var source = string.Format(/* lang=c#-test */ """ using Xunit; [CollectionDefinition(nameof(TestCollection))] public class TestCollection : ICollectionFixture {{ }} {0} public abstract class BaseClass {{ }} {1} public class TestClass : BaseClass {{ public TestClass(object _) {{ }} }} """, baseAttribute, derivedAttribute); await Verify.VerifyAnalyzer(source); } [Fact] public async Task WithFixture_WithDefinition_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; [CollectionDefinition(nameof(TestCollection))] public class TestCollection : ICollectionFixture { } [Collection(nameof(TestCollection))] public class TestClass { public TestClass(object _) { } [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("")] [InlineData("[CollectionDefinition(nameof(TestCollection))]")] public async Task WithFixture_WithoutCollectionFixtureInterface_Triggers(string definitionAttribute) { var source = string.Format(/* lang=c#-test */ """ using Xunit; {0} public class TestCollection {{ }} [Collection(nameof(TestCollection))] public class TestClass {{ public TestClass(object [|_|]) {{ }} [Fact] public void TestMethod() {{ }} }} """, definitionAttribute); await Verify.VerifyAnalyzer(source); } } public class AssemblyFixtures { [Fact] public async Task WithAssemblyFixture_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; [assembly: AssemblyFixture(typeof(object))] public class TestClass { public TestClass(object _) { } [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzerV3(source); } } public class MixedFixtures { [Theory] [InlineData("")] [InlineData("[CollectionDefinition(nameof(TestCollection))]")] public async Task WithClassFixture_WithCollection_DoesNotTrigger(string definitionAttribute) { var source = string.Format(/* lang=c#-test */ """ using Xunit; {0} public class TestCollection {{ }} [Collection(nameof(TestCollection))] public class TestClass : IClassFixture {{ public TestClass(object _) {{ }} [Fact] public void TestMethod() {{ }} }} """, definitionAttribute); await Verify.VerifyAnalyzer(source); } [Fact] public async Task WithMixedClassAndCollectionFixture_AndSupportedNonFixture_DoesNotTrigger() { var sourceTemplate = /* lang=c#-test */ """ using Xunit; public class ClassFixture {{ }} public class CollectionFixture {{ }} [CollectionDefinition(nameof(TestCollection))] public class TestCollection : ICollectionFixture {{ }} [Collection(nameof(TestCollection))] public class TestClass : IClassFixture {{ public TestClass(ClassFixture _1, CollectionFixture _2, {0} _3) {{ }} [Fact] public void TestMethod() {{ }} }} """; await Verify.VerifyAnalyzerV2(string.Format(sourceTemplate, "Xunit.Abstractions.ITestOutputHelper")); await Verify.VerifyAnalyzerV3(string.Format(sourceTemplate, "Xunit.ITestContextAccessor")); } [Fact] public async Task MissingClassFixture_Triggers() { var source = /* lang=c#-test */ """ using Xunit; public class ClassFixture { } public class CollectionFixture { } [CollectionDefinition(nameof(TestCollection))] public class TestCollection : ICollectionFixture { } [Collection(nameof(TestCollection))] public class TestClass { public TestClass(ClassFixture [|_1|], CollectionFixture _2) { } [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MissingCollectionFixture_Triggers() { var source = /* lang=c#-test */ """ using Xunit; public class ClassFixture { } public class CollectionFixture { } [CollectionDefinition(nameof(TestCollection))] public class TestCollection { } [Collection(nameof(TestCollection))] public class TestClass : IClassFixture { public TestClass(ClassFixture _1, CollectionFixture [|_2|]) { } [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/FactMethodMustNotHaveParametersTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class FactMethodMustNotHaveParametersTests { [Fact] public async Task FactWithNoParameters_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task TheoryWithParameters_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] public void TestMethod(string p) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task FactWithParameters_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void [|TestMethod|](string p) { } } """; await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/FactMethodShouldNotHaveTestDataTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class FactMethodShouldNotHaveTestDataTests { [Fact] public async Task FactWithNoDataAttributes_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("InlineData")] [InlineData("MemberData(\"\")")] [InlineData("ClassData(typeof(string))")] public async Task TheoryWithDataAttributes_DoesNotTrigger(string dataAttribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.{0}] public void TestMethod() {{ }} }} """, dataAttribute); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("InlineData")] [InlineData("MemberData(\"\")")] [InlineData("ClassData(typeof(string))")] public async Task FactDerivedMethodWithDataAttributes_DoesNotTrigger(string dataAttribute) { var source1 = /* lang=c#-test */ "public class DerivedFactAttribute: Xunit.FactAttribute {}"; var source2 = string.Format(/* lang=c#-test */ """ public class TestClass {{ [DerivedFactAttribute] [Xunit.{0}] public void TestMethod() {{ }} }} """, dataAttribute); await Verify.VerifyAnalyzer([source1, source2]); } [Theory] [InlineData("InlineData")] [InlineData("MemberData(\"\")")] [InlineData("ClassData(typeof(string))")] public async Task FactWithDataAttributes_Triggers(string dataAttribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Fact] [Xunit.{0}] public void [|TestMethod|]() {{ }} }} """, dataAttribute); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/InlineDataMustMatchTheoryParametersTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v2_Pre240 = CSharpVerifier; public class InlineDataMustMatchTheoryParametersTests { public class NonErrors { [Fact] public async Task MethodUsingParamsArgument() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc", "xyz")] public void TestMethod(params string[] args) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodUsingNullParamsArgument_NonNullable() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(null)] public void TestMethod(params string[] args) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodUsingNullParamsArgument_Nullable() { var source = /* lang=c#-test */ """ #nullable enable public class TestClass { [Xunit.Theory] [Xunit.InlineData(null)] public void TestMethod(params string[]? args) { } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp9, source); } [Fact] public async Task MethodUsingNormalAndParamsArgument() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc", "xyz")] public void TestMethod(string first, params string[] args) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodUsingNormalAndNullParamsArgument_NonNullable() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc", null)] public void TestMethod(string first, params string[] args) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodUsingNormalAndNullParamsArgument_Nullable() { var source = /* lang=c#-test */ """ #nullable enable public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc", null)] public void TestMethod(string first, params string[]? args) { } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp9, source); } [Fact] public async Task MethodUsingNormalAndUnusedParamsArgument() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc")] public void TestMethod(string first, params string[] args) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodUsingEmptyArrayForParams() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new int[] { })] public void VariableArgumentsTest(params int[] sq) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodUsingMixedArgumentsAndEmptyArrayForParams() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(21.12, new int[] { })] public void VariableArgumentsTest(double d, params int[] sq) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodUsingNonEmptyArrayForParams() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new int[] { 1, 2, 3 })] public void VariableArgumentsTest(params int[] sq) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodUsingMixedArgumentsAndNonEmptyArrayForParams() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(21.12, new int[] { 1, 2, 3 })] public void VariableArgumentsTest(double d, params int[] sq) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingParameters() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc", 1, null)] public void TestMethod(string a, int b, object c) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingParametersWithDefaultValues() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc")] public void TestMethod(string a, string b = "default", string c = null) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingParametersWithDefaultValuesAndParamsArgument() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc")] public void TestMethod(string a, string b = "default", string c = null, params string[] d) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingParameterWithOptionalAttribute() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData("abc")] public void TestMethod(string a, [System.Runtime.InteropServices.Optional] string b) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingMultipleParametersWithOptionalAttributes() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData] [Xunit.InlineData("abc")] [Xunit.InlineData("abc", "def")] public void TestMethod( [System.Runtime.InteropServices.Optional] string a, [System.Runtime.InteropServices.Optional] string b) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingExplicitArray() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new object[] { "abc", 1, null })] public void TestMethod(string a, int b, object c) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingExplicitNamedArray() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(data: new object[] { "abc", 1, null })] public void TestMethod(string a, int b, object c) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingImplicitArray() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new[] { (object)"abc", 1, null })] public void TestMethod(string a, int b, object c) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task UsingImplicitNamedArray() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(data: new[] { (object)"abc", 1, null })] public void TestMethod(string a, int b, object c) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task EmptyArray() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new byte[0])] public void TestMethod(byte[] input) { } } """; await Verify.VerifyAnalyzer(source); } // https://github.com/xunit/xunit/issues/3000 [Fact] public async Task DecimalValue() { var source = /* lang=c#-test */ """ using Xunit; public sealed class ReproClass { [Theory] [InlineData({|CS0182:0.1m|})] public void ReproMethod(decimal m) { } } """; await Verify.VerifyAnalyzer(source); } } public class X1009_TooFewValues { [Fact] public async Task IgnoresFact() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] [Xunit.InlineData] public void TestMethod(string a) { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("Xunit.InlineData()")] [InlineData("Xunit.InlineData")] public async Task NoArguments(string attribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [{{|xUnit1009:{0}|}}] public void TestMethod(int a) {{ }} }} """, attribute); await Verify.VerifyAnalyzer(source); } [Fact] public async Task TooFewArguments() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [{|xUnit1009:Xunit.InlineData(1)|}] public void TestMethod(int a, int b, string c) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task TooFewArguments_WithParams() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [{|xUnit1009:Xunit.InlineData(1)|}] public void TestMethod(int a, int b, params string[] value) { } } """; await Verify.VerifyAnalyzer(source); } } public class X1010_IncompatibleValueType { [Fact] public async Task MethodUsingIncompatibleExplicitArrayForParams() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(21.12, {|xUnit1010:new object[] { }|})] public void VariableArgumentsTest(double d, params int[] sq) { } } """; await Verify.VerifyAnalyzer(source); } public class NumericParameter : X1010_IncompatibleValueType { public static readonly IEnumerable> NumericTypes = [ "int", "uint", "long", "ulong", "short", "ushort", "byte", "sbyte", "float", "double", "decimal", ]; public static MatrixTheoryData NumericValuesAndNumericTypes = new(NumericValues.Select(d => d.Data), NumericTypes.Select(d => d.Data)); public static MatrixTheoryData BoolValuesAndNumericTypes = new(BoolValues.Select(d => d.Data), NumericTypes.Select(d => d.Data)); [Theory] [MemberData(nameof(NumericValuesAndNumericTypes))] public async Task CompatibleNumericValue_NonNullableType( string value, string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod({1} a) {{ }} }} """, value, type); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NumericValuesAndNumericTypes))] public async Task CompatibleNumericValue_NullableType( string value, string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod({1}? a) {{ }} }} """, value, type); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(BoolValuesAndNumericTypes))] public async Task BooleanValue_NumericType( string value, string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod({1} a) {{ }} }} """, value, type); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", type); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(NumericTypes))] public async Task CharValue_NumericType(string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData('a')] public void TestMethod({0} a) {{ }} }} """, type); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NumericTypes))] public async Task EnumValue_NumericType(string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:System.StringComparison.InvariantCulture|}})] public void TestMethod({0} a) {{ }} }} """, type); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", type); await Verify.VerifyAnalyzer(source, expected); } } public class BooleanParameter : X1010_IncompatibleValueType { [Theory] [MemberData(nameof(BoolValues))] public async Task FromBooleanValue_ToNonNullable(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(bool a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(BoolValues))] public async Task FromBooleanValue_ToNullable(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(bool? a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NumericValues))] [InlineData("System.StringComparison.Ordinal")] [InlineData("'a'")] [InlineData("\"abc\"")] [InlineData("typeof(string)")] public async Task FromIncompatibleValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(bool a) {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "bool"); await Verify.VerifyAnalyzer(source, expected); } } public class CharParameter : X1010_IncompatibleValueType { [Theory] [InlineData("'a'")] [MemberData(nameof(IntegerValues))] public async Task FromCharOrIntegerValue_ToNonNullable(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(char a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("'a'")] [MemberData(nameof(IntegerValues))] public async Task FromCharOrIntegerValue_ToNullable(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(char? a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(FloatingPointValues))] [MemberData(nameof(BoolValues))] [InlineData("\"abc\"")] [InlineData("System.StringComparison.Ordinal")] [InlineData("typeof(string)")] public async Task FromIncompatibleValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(char a) {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "char"); await Verify.VerifyAnalyzer(source, expected); } } public class EnumParameter : X1010_IncompatibleValueType { [Fact] public async Task FromEnumValue_ToNonNullable() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(System.StringComparison.Ordinal)] public void TestMethod(System.StringComparison a) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task FromEnumValue_ToNullable() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(System.StringComparison.Ordinal)] public void TestMethod(System.StringComparison? a) { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NumericValues))] [MemberData(nameof(BoolValues))] [InlineData("'a'")] [InlineData("\"abc\"")] [InlineData("typeof(string)")] public async Task FromIncompatibleValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(System.StringComparison a) {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "System.StringComparison"); await Verify.VerifyAnalyzer(source, expected); } } public class TypeParameter : X1010_IncompatibleValueType { [Theory] [InlineData("typeof(string)")] [InlineData("null")] public async Task FromTypeValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(System.Type a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("typeof(string)")] [InlineData("null")] public async Task FromTypeValue_ToParams(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(params System.Type[] a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NumericValues))] [MemberData(nameof(BoolValues))] [InlineData("'a'")] [InlineData("\"abc\"")] [InlineData("System.StringComparison.Ordinal")] public async Task FromIncompatibleValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(System.Type a) {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "System.Type"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(NumericValues))] [MemberData(nameof(BoolValues))] [InlineData("'a'")] [InlineData("\"abc\"")] [InlineData("System.StringComparison.Ordinal")] public async Task FromIncompatibleValue_ToParams(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(params System.Type[] a) {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "System.Type"); await Verify.VerifyAnalyzer(source, expected); } } public class StringParameter : X1010_IncompatibleValueType { [Theory] [InlineData("\"abc\"")] [InlineData("null")] public async Task FromStringValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(string a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NumericValues))] [MemberData(nameof(BoolValues))] [InlineData("System.StringComparison.Ordinal")] [InlineData("'a'")] [InlineData("typeof(string)")] public async Task FromIncompatibleValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(string a) {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "string"); await Verify.VerifyAnalyzer(source, expected); } } public class InterfaceParameter : X1010_IncompatibleValueType { [Theory] [MemberData(nameof(NumericValues))] [InlineData("System.StringComparison.Ordinal")] [InlineData("null")] public async Task FromTypeImplementingInterface(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(System.IFormattable a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] #if NETFRAMEWORK [InlineData("'a'")] #endif [InlineData("\"abc\"")] [InlineData("typeof(string)")] [MemberData(nameof(BoolValues))] public async Task FromIncompatibleValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(System.IFormattable a) {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "System.IFormattable"); await Verify.VerifyAnalyzer(source, expected); } } public class ObjectParameter : X1010_IncompatibleValueType { [Theory] [MemberData(nameof(BoolValues))] [MemberData(nameof(NumericValues))] [InlineData("System.StringComparison.Ordinal")] [InlineData("'a'")] [InlineData("\"abc\"")] [InlineData("null")] [InlineData("typeof(string)")] public async Task FromAnyValue(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(object a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(BoolValues))] [MemberData(nameof(NumericValues))] [InlineData("System.StringComparison.Ordinal")] [InlineData("'a'")] [InlineData("\"abc\"")] [InlineData("null")] [InlineData("typeof(string)")] public async Task FromAnyValue_ToParams(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(params object[] a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } } public class GenericParameter : X1010_IncompatibleValueType { [Theory] [MemberData(nameof(BoolValues))] [MemberData(nameof(NumericValues))] [InlineData("System.StringComparison.Ordinal")] [InlineData("'a'")] [InlineData("\"abc\"")] [InlineData("null")] [InlineData("typeof(string)")] public async Task FromAnyValue_NoConstraint(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(T a) {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(BoolValues))] [MemberData(nameof(NumericValues))] [InlineData("System.StringComparison.Ordinal")] [InlineData("'a'")] public async Task FromValueTypeValue_WithStructConstraint(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(T a) where T: struct {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("\"abc\"")] [InlineData("typeof(string)")] public async Task FromReferenceTypeValue_WithStructConstraint(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(T a) where T: struct {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "T"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData("\"abc\"")] [InlineData("typeof(string)")] [InlineData("null")] public async Task FromReferenceTypeValue_WithClassConstraint(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(T a) where T: class {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(BoolValues))] [MemberData(nameof(NumericValues))] [InlineData("System.StringComparison.Ordinal")] [InlineData("'a'")] public async Task FromValueTypeValue_WithClassConstraint(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(T a) where T: class {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "T"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData("null")] [InlineData("System.StringComparison.Ordinal")] [MemberData(nameof(NumericValues))] public async Task FromCompatibleValue_WithTypeConstraint(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(T a) where T: System.IConvertible, System.IFormattable {{ }} }} """, value); await Verify.VerifyAnalyzer(source); } #if ROSLYN_LATEST && NET8_0_OR_GREATER [Fact] public async Task TypeConstraint_WithCRTP() // CRTP: Curiously Recurring Template Pattern or T: Interface { var source = /* lang=c#-test */ """ using Xunit; using System.Numerics; public class TestClass { [Theory] [InlineData(0U)] [InlineData(2U)] [InlineData(5294967295U)] // ulong value [InlineData({|xUnit1010:-1U|})] [InlineData({|xUnit1010:2|})] [InlineData({|xUnit1010:0|})] [InlineData({|xUnit1010:"A"|})] public void UnsignedNumberIsAtLeastZero(T number) where T : IUnsignedNumber => Assert.False(T.IsNegative(number)); } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp11, source); } #endif [Theory] #if NETFRAMEWORK [InlineData("'a'")] #endif [InlineData("\"abc\"")] [InlineData("typeof(string)")] [MemberData(nameof(BoolValues))] public async Task FromIncompatibleValue_WithTypeConstraint(string value) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(T a) where T: System.IConvertible, System.IFormattable {{ }} }} """, value); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "T"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task FromIncompatibleArray() { var source =/* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new int[] { {|#0:1|}, 2, 3 })] public void TestMethod(T a) where T: System.IConvertible, System.IFormattable { } } """; var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("a", "T"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task FromCompatibleArray() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new int[] { 1, 2, 3 })] public void TestMethod(T[] a) { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(SignedIntAndUnsignedInt))] public async Task FromNegativeInteger_ToUnsignedInteger( string signedType, string unsignedType) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:({0})-1|}})] public void TestMethod({1} value) {{ }} }} """, signedType, unsignedType); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("value", unsignedType); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(UnsignedIntegralTypes))] public async Task FromLongMinValue_ToUnsignedInteger(string unsignedType) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:long.MinValue|}})] public void TestMethod({0} value) {{ }} }} """, unsignedType); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("value", unsignedType); await Verify.VerifyAnalyzer(source, expected); } } public class DateTimeLikeParameter : X1010_IncompatibleValueType { public static readonly IEnumerable> ValidDateTimeStrings = [ "\"\"", "\"2018-01-02\"", "\"2018-01-02 12:34\"", "\"obviously-rubbish-datetime-value\"", "MyConstString" ]; public static MatrixTheoryData ValueTypedArgumentsCombinedWithDateTimeLikeTypes = new(ValueTypedValues.Select(d => d.Data), ["System.DateTime", "System.DateTimeOffset"]); [Theory] [MemberData(nameof(ValueTypedArgumentsCombinedWithDateTimeLikeTypes))] [InlineData("MyConstInt", "System.DateTime")] [InlineData("MyConstInt", "System.DateTimeOffset")] public async Task NonStringValue( string data, string parameterType) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ const int MyConstInt = 1; [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod({1} parameter) {{ }} }} """, data, parameterType); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("parameter", parameterType); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(ValidDateTimeStrings))] public async Task StringValue_ToDateTime(string data) { var source = CreateSourceWithStringConst(data, "System.DateTime"); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(ValidDateTimeStrings))] public async Task StringValue_ToDateTimeOffset(string data) { var source = CreateSourceWithStringConst(data, "System.DateTimeOffset"); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(ValidDateTimeStrings))] public async Task StringValue_ToDateTimeOffset_Pre240(string data) { var source = CreateSourceWithStringConst("{|#0:" + data + "|}", "System.DateTimeOffset"); var expected = Verify_v2_Pre240.Diagnostic("xUnit1010").WithLocation(0).WithArguments("parameter", "System.DateTimeOffset"); await Verify_v2_Pre240.VerifyAnalyzer(source, expected); } static string CreateSourceWithStringConst( string data, string parameterType) => string.Format(/* lang=c#-test */ """ public class TestClass {{ const string MyConstString = "some string"; [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod({1} parameter) {{ }} }} """, data, parameterType); } public class GuidParameter : X1010_IncompatibleValueType { public static IEnumerable> ValidGuidStrings = [ "\"\"", "\"{5B21E154-15EB-4B1E-BC30-127E8A41ECA1}\"", "\"4EBCD32C-A2B8-4600-9E72-3873347E285C\"", "\"39A3B4C85FEF43A988EB4BB4AC4D4103\"", "\"obviously-rubbish-guid-value\"" ]; [Theory] [MemberData(nameof(ValueTypedValues))] [InlineData("MyConstInt")] public async Task NonStringValue(string data) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ private const int MyConstInt = 1; [Xunit.Theory] [Xunit.InlineData({{|#0:{0}|}})] public void TestMethod(System.Guid parameter) {{ }} }} """, data); var expected = Verify.Diagnostic("xUnit1010").WithLocation(0).WithArguments("parameter", "System.Guid"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(ValidGuidStrings))] public async Task StringValue(string inlineData) { var source = CreateSource(inlineData); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(ValidGuidStrings))] public async Task StringValue_Pre240(string data) { var source = CreateSource("{|#0:" + data + "|}"); var expected = Verify_v2_Pre240.Diagnostic("xUnit1010").WithLocation(0).WithArguments("parameter", "System.Guid"); await Verify_v2_Pre240.VerifyAnalyzer(source, expected); } static string CreateSource(string data) => string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] public void TestMethod(System.Guid parameter) {{ }} }} """, data); } public class UserDefinedConversionOperators : X1010_IncompatibleValueType { [Fact] public async Task SupportsImplicitConversion() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData("abc")] public void ParameterDeclaredImplicitConversion(Implicit i) => Assert.Equal("abc", i.Value); public class Implicit { public string Value { get; set; } public static implicit operator Implicit(string value) => new Implicit() { Value = value }; public static implicit operator string(Implicit i) => i.Value; } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task SupportsExplicitConversion() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData("abc")] public void ParameterDeclaredExplicitConversion(Explicit i) => Assert.Equal("abc", i.Value); public class Explicit { public string Value { get; set; } public static explicit operator Explicit(string value) => new Explicit() { Value = value }; public static explicit operator string(Explicit i) => i.Value; } } """; await Verify.VerifyAnalyzer(source); } } // Note: decimal literal 42M is not valid as an attribute argument public static IEnumerable> BoolValues = [ "true", "false" ]; public static IEnumerable> FloatingPointValues = [ "42f", "42d" ]; public static IEnumerable> IntegerValues = [ "42", "42L", "42u", "42ul", "(short)42", "(byte)42", "(ushort)42", "(sbyte)42" ]; public static IEnumerable> NumericValues = IntegerValues.Concat(FloatingPointValues); public static IEnumerable> ValueTypedValues = IntegerValues.Concat(FloatingPointValues).Concat(BoolValues).Append(new("typeof(int)")); public static IEnumerable> SignedIntegralTypes = ["int", "long", "short", "sbyte"]; public static IEnumerable> UnsignedIntegralTypes = ["uint", "ulong", "ushort", "byte"]; public static readonly MatrixTheoryData SignedIntAndUnsignedInt = new( SignedIntegralTypes.Select(r => r.Data), UnsignedIntegralTypes.Select(r => r.Data) ); } public class X1011_ExtraValue { [Fact] public async Task IgnoresFact() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] [Xunit.InlineData(1, 2, "abc")] public void TestMethod(int a) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ExtraArguments() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(1, {|#0:2|}, {|#1:"abc"|})] public void TestMethod(int a) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1011").WithLocation(0).WithArguments("2"), Verify.Diagnostic("xUnit1011").WithLocation(1).WithArguments("\"abc\""), }; await Verify.VerifyAnalyzer(source, expected); } } public class X1012_NullShouldNotBeUsedForIncompatibleParameter { [Fact] public async Task IgnoresFact() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] [Xunit.InlineData(null)] public void TestMethod(int a) { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("int")] [InlineData("params int[]")] public async Task SingleNullValue(string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({{|#0:null|}})] public void TestMethod({0} a) {{ }} }} """, type); var expected = Verify.Diagnostic("xUnit1012").WithLocation(0).WithArguments("a", "int"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(ValueTypes))] public async Task NonNullableValueTypes(string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1, {{|#0:null|}}, {{|#1:null|}})] public void TestMethod(int a, {0} b, params {0}[] c) {{ }} }} """, type); var expected = new[] { Verify.Diagnostic("xUnit1012").WithLocation(0).WithArguments("b", type), Verify.Diagnostic("xUnit1012").WithLocation(1).WithArguments("c", type), }; await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(ValueTypes))] public async Task NullableValueTypes(string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1, null)] public void TestMethod(int a, {0}? b) {{ }} }} """, type); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("object")] [InlineData("string")] [InlineData("System.Exception")] public async Task ReferenceTypes(string type) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1, null)] public void TestMethod(int a, {0} b) {{ }} }} """, type); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("object")] [InlineData("string")] [InlineData("System.Exception")] public async Task NonNullableReferenceTypes(string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1, {{|#0:null|}})] public void TestMethod(int a, {0} b) {{ }} }} """, type); var expected = Verify.Diagnostic("xUnit1012").WithLocation(0).WithArguments("b", type); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source, expected); } [Theory] [InlineData("object")] [InlineData("string")] [InlineData("System.Exception")] public async Task NullableReferenceTypes(string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1, null)] public void TestMethod(int a, {0}? b) {{ }} }} """, type); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source); } [Theory] [InlineData("1", "object")] [InlineData("\"bob\"", "string")] public async Task NullableParamsReferenceTypes( string param, string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1, {0}, null, null)] public void TestMethod(int a, params {1}?[] b) {{ }} }} """, param, type); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source); } [Theory] [InlineData("1", "object")] [InlineData("\"bob\"", "string")] public async Task NonNullableParamsReferenceTypes( string param, string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1, {0}, {{|#0:null|}}, {{|#1:null|}})] public void TestMethod(int a, params {1}[] b) {{ }} }} """, param, type); var expected = new[] { Verify.Diagnostic("xUnit1012").WithLocation(0).WithArguments("b", type), Verify.Diagnostic("xUnit1012").WithLocation(1).WithArguments("b", type), }; await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source, expected); } public static IEnumerable> ValueTypes = [ "bool", "int", "byte", "short", "long", "decimal", "double", "float", "char", "ulong", "uint", "ushort", "sbyte", "System.StringComparison", "System.Guid", ]; } internal class Analyzer_v2_Pre240 : InlineDataMustMatchTheoryParameters { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2(compilation, new Version(2, 3, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/InlineDataShouldBeUniqueWithinTheoryTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public abstract class InlineDataShouldBeUniqueWithinTheoryTests { public class ForNonRelatedToInlineDataMethod : InlineDataShouldBeUniqueWithinTheoryTests { [Fact] public async Task WithNoDataAttributes_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("MemberData(\"\")")] [InlineData("ClassData(typeof(string))")] public async Task WithDataAttributesOtherThanInline_DoesNotTrigger( string dataAttribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.{0}] public void TestMethod() {{ }} }} """, dataAttribute); await Verify.VerifyAnalyzer(source); } } public class ForUniqueInlineDataMethod : InlineDataShouldBeUniqueWithinTheoryTests { [Fact] public async Task NonTheory_SingleInlineData_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] [Xunit.InlineData] public void TestMethod(int x) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task NonTheory_DoubledInlineData_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] [Xunit.InlineData] [Xunit.InlineData] public void TestMethod(int x) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task SingleInlineData_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(10)] public void TestMethod(int x) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MultipleInlineData_DifferentValues_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(10)] [Xunit.InlineData(20)] public void TestMethod(int x) { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData(/* lang=c#-test */ "new object[] { 1, 3 }")] [InlineData(/* lang=c#-test */ "data: new object[] { 1, 3 }")] [InlineData(/* lang=c#-test */ "new object[] { }")] [InlineData(/* lang=c#-test */ "data: new object[] { 1 }")] public async Task UniqueValues_WithParamsInitializerValues_DoesNotTrigger(string data) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1, 2)] [Xunit.InlineData({0})] public void TestMethod(params int[] args) {{ }} }} """, data); await Verify.VerifyAnalyzer(source); } [Fact] public async Task UniqueValues_WithOverridingDefaultValues_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(1)] [Xunit.InlineData(1, "non-default-val")] public void TestMethod(int x, string a = "default-val") { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task NullAndEmpty_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(null)] [Xunit.InlineData] public void TestMethod(string s) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task NullAndArray_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass{ [Xunit.Theory] [Xunit.InlineData(new[] { 0 })] [Xunit.InlineData(null)] public void TestMethod(int[] arr) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ArrayOrderVariance_DoesNotTrigger() { // Specially crafted InlineData values that will cause the InlineDataUniquenessComparer // to return same hashcodes, because GetFlattenedArgumentPrimitives ignores empty arrays. // This will trigger the actual bug, where the first parameter object array being equal // would cause the other parameters to not be evaluated for equality at all. var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new int[] { 1 }, new int[0], new int[] { 1 })] [Xunit.InlineData(new int[] { 1 }, new int[] { 1 }, new int[0])] public static void Test(int[] x, int[] y, int[] z) { } } """; await Verify.VerifyAnalyzer(source); } } public class ForDuplicatedInlineDataMethod : InlineDataShouldBeUniqueWithinTheoryTests { [Fact] public async Task DoubleEmptyInlineData_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData] [{|#0:Xunit.InlineData|}] public void TestMethod(int x) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task DoubleNullInlineData_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(null)] [{|#0:Xunit.InlineData(null)|}] public void TestMethod(string x) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task DoubleValues_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(10)] [{|#0:Xunit.InlineData(10)|}] public void TestMethod(int x) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ValueFromConstant_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { private const int X = 10; [Xunit.Theory] [Xunit.InlineData(10)] [{|#0:Xunit.InlineData(X)|}] public void TestMethod(int x) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(/* lang=c#-test */ "new object[] { 10, 20 }")] [InlineData(/* lang=c#-test */ "data: new object[] { 10, 20 }")] public async Task TwoParams_RawValuesVsArgumentArray_Triggers(string data) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(10, 20)] [{{|#0:Xunit.InlineData({0})|}}] public void TestMethod(int x, int y) {{ }} }} """, data); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(/* lang=c#-test */ "new object[] { 10, 20 }")] [InlineData(/* lang=c#-test */ "data: new object[] { 10, 20 }")] public async Task ParamsArray_RawValuesVsArgumentArray_Triggers(string data) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(10, 20)] [{{|#0:Xunit.InlineData({0})|}}] public void TestMethod(params int[] args) {{ }} }} """, data); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task DoubledArgumentArrays_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new object[] { 10, 20 })] [{|#0:Xunit.InlineData(new object[] { 10, 20 })|}] public void TestMethod(int x, int y) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task DoubledComplexValuesForObject_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(new object[] {10, new object[] { new object[] {20}, 30}})] [{|#0:Xunit.InlineData(new object[] {10, new object[] { new object[] {20}, 30}})|}] public void TestMethod(object x, object y) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task DoubledComplexValues_RawValuesVsArgumentArray_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(10, new object[] { new object[] {20}, 30}, 40)] [{|#0:Xunit.InlineData(new object[] {10, new object[] { new object[] {20}, 30}})|}] public void TestMethod(object x, object y, int z = 40) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } // The value 1 doesn't seem to trigger bugs related to comparing boxed values, but 2 does public static TheoryData DefaultValueData = [1, 2]; [Theory] [MemberData(nameof(DefaultValueData))] public async Task DefaultValueVsExplicitValue_Triggers(int defaultValue) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData] [{{|#0:Xunit.InlineData({0})|}}] public void TestMethod(int y = {0}) {{ }} }} """, defaultValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(DefaultValueData))] public async Task ExplicitValueVsDefaultValue_Triggers(int defaultValue) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] [{{|#0:Xunit.InlineData|}}] public void TestMethod(int y = {0}) {{ }} }} """, defaultValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(DefaultValueData))] public async Task DefaultValueVsDefaultValue_Triggers(int defaultValue) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData] [{{|#0:Xunit.InlineData|}}] public void TestMethod(int y = {0}) {{ }} }} """, defaultValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData("null", "null")] [InlineData("null", "")] [InlineData("", "null")] [InlineData("", "")] public async Task DefaultValueVsNull_Triggers( string firstArg, string secondArg) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData({0})] [{{|#0:Xunit.InlineData({1})|}}] public void TestMethod(string x = null) {{ }} }} """, firstArg, secondArg); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task Null_RawValuesVsExplicitArray_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(1, null)] [{|#0:Xunit.InlineData(new object[] { 1, null })|}] public void TestMethod(object x, object y) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData("", "")] [InlineData("", ", default")] [InlineData(", default", "")] [InlineData(", default", ", default")] public async Task DefaultOfStruct_Triggers( string firstDefaultOverride, string secondDefaultOverride) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1{0})] [{{|#0:Xunit.InlineData(1{1})|}}] public void TestMethod(int x, System.DateTime date = default) {{ }} }} """, firstDefaultOverride, secondDefaultOverride); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(LanguageVersion.CSharp7_1, source, expected); } [Theory] [InlineData("", "")] [InlineData("", ", null")] [InlineData(", null", "")] [InlineData(", null", ", null")] public async Task DefaultOfString_Triggers( string firstDefaultOverride, string secondDefaultOverride) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.Theory] [Xunit.InlineData(1{0})] [{{|#0:Xunit.InlineData(1{1})|}}] public void TestMethod(int x, string y = null) {{ }} }} """, firstDefaultOverride, secondDefaultOverride); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task Tripled_TriggersTwice() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(10)] [{|#0:Xunit.InlineData(10)|}] [{|#1:Xunit.InlineData(10)|}] public void TestMethod(int x) { } } """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"), Verify.Diagnostic().WithLocation(1).WithArguments("TestMethod", "TestClass"), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task DoubledTwice_TriggersTwice() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] [Xunit.InlineData(10)] [Xunit.InlineData(20)] [{|#0:Xunit.InlineData(10)|}] [{|#1:Xunit.InlineData(20)|}] public void TestMethod(int x) { } } """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass"), Verify.Diagnostic().WithLocation(1).WithArguments("TestMethod", "TestClass"), }; await Verify.VerifyAnalyzer(source, expected); } } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/LocalFunctionsCannotBeTestFunctionsTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class LocalFunctionsCannotBeTestFunctionsTests { [Fact] public async Task NoTestAttribute_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { public void Method() { void LocalFunction() { } } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp9, source); } [Theory] [InlineData("Fact")] [InlineData("Theory")] [InlineData("InlineData(42)")] [InlineData("MemberData(nameof(MyData))")] [InlineData("ClassData(typeof(TestClass))")] public async Task TestAttribute_Triggers(string attribute) { var source = string.Format(/* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass {{ public void Method() {{ [{{|#0:{0}|}}] void LocalFunction() {{ }} }} public static IEnumerable MyData; }} """, attribute); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"[{attribute}]"); await Verify.VerifyAnalyzer(LanguageVersion.CSharp9, source, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/MemberDataShouldReferenceValidMemberTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class MemberDataShouldReferenceValidMemberTests { public class X1014_MemberDataShouldUseNameOfOperator { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #pragma warning disable xUnit1053 using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData Data { get; set; } [MemberData(nameof(Data))] [MemberData(nameof(OtherClass.OtherData), MemberType = typeof(OtherClass))] public void TestMethod1(int _) { } [MemberData({|#0:"Data"|})] [MemberData({|#1:"OtherData"|}, MemberType = typeof(OtherClass))] public void TestMethod2(int _) { } } public class OtherClass { public static TheoryData OtherData { get; set; } } """; var expected = new[] { Verify.Diagnostic("xUnit1014").WithLocation(0).WithArguments("Data", "TestClass"), Verify.Diagnostic("xUnit1014").WithLocation(1).WithArguments("OtherData", "OtherClass"), }; await Verify.VerifyAnalyzer(source, expected); } } public class X1015_MemberDataMustReferenceExistingMember { [Fact] public async ValueTask V2_and_V3() { var source1 = /* lang=c#-test */ """ using Xunit; public class TestClass { [{|#0:MemberData("BogusName")|}] public void TestMethod1() { } [{|#1:MemberData("BogusName", MemberType = typeof(TestClass))|}] public void TestMethod2() { } [{|#2:MemberData("BogusName", MemberType = typeof(OtherClass))|}] public void TestMethod3() { } [{|#3:MemberData(nameof(TestClass.TestMethod4), MemberType = typeof(OtherClass))|}] public void TestMethod4() { } } """; var source2 = /* lang=c#-test */ "public class OtherClass { }"; var expected = new[] { Verify.Diagnostic("xUnit1015").WithLocation(0).WithArguments("BogusName", "TestClass"), Verify.Diagnostic("xUnit1015").WithLocation(1).WithArguments("BogusName", "TestClass"), Verify.Diagnostic("xUnit1015").WithLocation(2).WithArguments("BogusName", "OtherClass"), Verify.Diagnostic("xUnit1015").WithLocation(3).WithArguments("TestMethod4", "OtherClass"), }; await Verify.VerifyAnalyzer([source1, source2], expected); } [Fact] public async ValueTask DoesNotTrigger_WhenMemberExistsOnDerivedType_AndBaseTypeIsAbstract() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public abstract class BaseClassWithTestWithoutData { [Theory] [MemberData(nameof(SubClassWithTestData.TestData))] public void Test(int x) { } } public class SubClassWithTestData : BaseClassWithTestWithoutData { public static IEnumerable TestData() { yield return new object?[] { 42 }; } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source); } } public class X1016_MemberDataMustReferencePublicMember { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData PublicData = null; [MemberData(nameof(PublicData))] public void TestMethod1a(int _) { } const string PrivateDataNameConst = "PrivateData"; const string PrivateDataNameofConst = nameof(PrivateData); private static TheoryData PrivateData = null; [{|xUnit1016:MemberData(nameof(PrivateData))|}] public void TestMethod2a(int _) { } [{|xUnit1016:MemberData(PrivateDataNameConst)|}] public void TestMethod2b(int _) { } [{|xUnit1016:MemberData(PrivateDataNameofConst)|}] public void TestMethod2c(int _) { } internal static TheoryData InternalData = null; [{|xUnit1016:MemberData(nameof(InternalData))|}] public void TestMethod3(int _) { } protected static TheoryData ProtectedData = null; [{|xUnit1016:MemberData(nameof(ProtectedData))|}] public void TestMethod4(int _) { } protected internal static TheoryData ProtectedInternalData = null; [{|xUnit1016:MemberData(nameof(ProtectedInternalData))|}] public void TestMethod5(int _) { } } """; await Verify.VerifyAnalyzer(source); } } public class X1017_MemberDataMustReferenceStaticMember { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData StaticData = null; public TheoryData NonStaticData = null; [MemberData(nameof(StaticData))] [{|xUnit1017:MemberData(nameof(NonStaticData))|}] public void TestMethod(int _) { } } """; await Verify.VerifyAnalyzer(source); } } public class X1018_MemberDataMustReferenceValidMemberKind { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #pragma warning disable xUnit1053 using System; using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData FieldData; public static TheoryData PropertyData { get; set; } public static TheoryData MethodData() { return null; } public static class ClassData { } public delegate IEnumerable DelegateData(); public static event EventHandler EventData; [MemberData(nameof(FieldData))] public void TestMethod1(int _) { } [MemberData(nameof(PropertyData))] public void TestMethod2(int _) { } [MemberData(nameof(MethodData))] public void TestMethod3(int _) { } [{|xUnit1018:MemberData(nameof(ClassData))|}] public void TestMethod4(int _) { } [{|xUnit1018:MemberData(nameof(DelegateData))|}] public void TestMethod5(int _) { } [{|xUnit1018:MemberData(nameof(EventData))|}] public void TestMethod6(int _) { } } """; await Verify.VerifyAnalyzer(source); } } public class X1019_MemberDataMustReferenceMemberOfValidType { const string V2AllowedTypes = "'System.Collections.Generic.IEnumerable'"; const string V3AllowedTypes = "'System.Collections.Generic.IEnumerable', 'System.Collections.Generic.IAsyncEnumerable', 'System.Collections.Generic.IEnumerable', 'System.Collections.Generic.IAsyncEnumerable', 'System.Collections.Generic.IEnumerable', or 'System.Collections.Generic.IAsyncEnumerable'"; [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #pragma warning disable xUnit1042 #pragma warning disable xUnit1053 using System; using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class NamedTypeForIEnumerableStringArray : IEnumerable { private readonly List _items = new List(); public void Add(string sdl) { _items.Add(new string[] { sdl }); } public IEnumerator GetEnumerator() { return _items.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public class NamedSubtypeForIEnumerableStringArray : NamedTypeForIEnumerableStringArray {} public class TestClass { public static IEnumerable ObjectSource; public static object NakedObjectSource; public static object[] NakedObjectArraySource; public static object[][] NakedObjectMatrixSource; public static IEnumerable ObjectArraySource; public static IEnumerable StringArraySource; public static NamedTypeForIEnumerableStringArray NamedTypeForIEnumerableStringArraySource; public static NamedSubtypeForIEnumerableStringArray NamedSubtypeForIEnumerableStringArraySource; public static Task> TaskObjectArraySource; public static ValueTask> ValueTaskObjectArraySource; public static IAsyncEnumerable AsyncObjectArraySource; public static Task> TaskAsyncObjectArraySource; public static ValueTask> ValueTaskAsyncObjectArraySource; public static TheoryData TheoryDataSource; public static Task> TaskTheoryDataSource; public static ValueTask> ValueTaskTheoryDataSource; public static IEnumerable<(string, int)> UntypedTupleSource; public static Task> TaskUntypedTupleSource; public static ValueTask> ValueTaskUntypedTupleSource; public static IAsyncEnumerable<(string, int)> AsyncUntypedTupleSource; public static Task> TaskAsyncUntypedTupleSource; public static ValueTask> ValueTaskAsyncUntypedTupleSource; public static IEnumerable> TypedTupleSource; public static Task>> TaskTypedTupleSource; public static ValueTask>> ValueTaskTypedTupleSource; public static IAsyncEnumerable> AsyncTypedTupleSource; public static Task>> TaskAsyncTypedTupleSource; public static ValueTask>> ValueTaskAsyncTypedTupleSource; [{|#0:MemberData(nameof(ObjectSource))|}] [{|#1:MemberData(nameof(NakedObjectSource))|}] [{|#2:MemberData(nameof(NakedObjectArraySource))|}] [MemberData(nameof(NakedObjectMatrixSource))] [MemberData(nameof(ObjectArraySource))] [MemberData(nameof(StringArraySource))] [MemberData(nameof(NamedTypeForIEnumerableStringArraySource))] [MemberData(nameof(NamedSubtypeForIEnumerableStringArraySource))] [{|#10:MemberData(nameof(TaskObjectArraySource))|}] [{|#11:MemberData(nameof(ValueTaskObjectArraySource))|}] [{|#20:MemberData(nameof(AsyncObjectArraySource))|}] [{|#21:MemberData(nameof(TaskAsyncObjectArraySource))|}] [{|#22:MemberData(nameof(ValueTaskAsyncObjectArraySource))|}] [MemberData(nameof(TheoryDataSource))] [{|#30:MemberData(nameof(TaskTheoryDataSource))|}] [{|#31:MemberData(nameof(ValueTaskTheoryDataSource))|}] [{|#40:MemberData(nameof(UntypedTupleSource))|}] [{|#41:MemberData(nameof(TaskUntypedTupleSource))|}] [{|#42:MemberData(nameof(ValueTaskUntypedTupleSource))|}] [{|#50:MemberData(nameof(AsyncUntypedTupleSource))|}] [{|#51:MemberData(nameof(TaskAsyncUntypedTupleSource))|}] [{|#52:MemberData(nameof(ValueTaskAsyncUntypedTupleSource))|}] [{|#60:MemberData(nameof(TypedTupleSource))|}] [{|#61:MemberData(nameof(TaskTypedTupleSource))|}] [{|#62:MemberData(nameof(ValueTaskTypedTupleSource))|}] [{|#70:MemberData(nameof(AsyncTypedTupleSource))|}] [{|#71:MemberData(nameof(TaskAsyncTypedTupleSource))|}] [{|#72:MemberData(nameof(ValueTaskAsyncTypedTupleSource))|}] public void TestMethod(string _1, int _2) { } } """; var expectedV2 = new[] { // Generally invalid types Verify.Diagnostic("xUnit1019").WithLocation(0).WithArguments(V2AllowedTypes, $"System.Collections.Generic.IEnumerable"), Verify.Diagnostic("xUnit1019").WithLocation(1).WithArguments(V2AllowedTypes, $"object"), Verify.Diagnostic("xUnit1019").WithLocation(2).WithArguments(V2AllowedTypes, $"object[]"), // v2 does not support tuples, wrapping in Task/ValueTask, and does not support IAsyncEnumerable Verify.Diagnostic("xUnit1019").WithLocation(10).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.Task>"), Verify.Diagnostic("xUnit1019").WithLocation(11).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.ValueTask>"), Verify.Diagnostic("xUnit1019").WithLocation(20).WithArguments(V2AllowedTypes, $"System.Collections.Generic.IAsyncEnumerable"), Verify.Diagnostic("xUnit1019").WithLocation(21).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.Task>"), Verify.Diagnostic("xUnit1019").WithLocation(22).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.ValueTask>"), Verify.Diagnostic("xUnit1019").WithLocation(30).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.Task>"), Verify.Diagnostic("xUnit1019").WithLocation(31).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.ValueTask>"), Verify.Diagnostic("xUnit1019").WithLocation(40).WithArguments(V2AllowedTypes, $"System.Collections.Generic.IEnumerable<(string, int)>"), Verify.Diagnostic("xUnit1019").WithLocation(41).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.Task>"), Verify.Diagnostic("xUnit1019").WithLocation(42).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.ValueTask>"), Verify.Diagnostic("xUnit1019").WithLocation(50).WithArguments(V2AllowedTypes, $"System.Collections.Generic.IAsyncEnumerable<(string, int)>"), Verify.Diagnostic("xUnit1019").WithLocation(51).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.Task>"), Verify.Diagnostic("xUnit1019").WithLocation(52).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.ValueTask>"), Verify.Diagnostic("xUnit1019").WithLocation(60).WithArguments(V2AllowedTypes, $"System.Collections.Generic.IEnumerable>"), Verify.Diagnostic("xUnit1019").WithLocation(61).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.Task>>"), Verify.Diagnostic("xUnit1019").WithLocation(62).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.ValueTask>>"), Verify.Diagnostic("xUnit1019").WithLocation(70).WithArguments(V2AllowedTypes, $"System.Collections.Generic.IAsyncEnumerable>"), Verify.Diagnostic("xUnit1019").WithLocation(71).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.Task>>"), Verify.Diagnostic("xUnit1019").WithLocation(72).WithArguments(V2AllowedTypes, $"System.Threading.Tasks.ValueTask>>"), }; var expectedV3 = new[] { // Generally invalid types Verify.Diagnostic("xUnit1019").WithLocation(0).WithArguments(V3AllowedTypes, $"System.Collections.Generic.IEnumerable"), Verify.Diagnostic("xUnit1019").WithLocation(1).WithArguments(V3AllowedTypes, $"object"), Verify.Diagnostic("xUnit1019").WithLocation(2).WithArguments(V3AllowedTypes, $"object[]"), }; await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp9, source, expectedV2); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source, expectedV3); } [Fact] public async ValueTask V3_only() { var source = /* lang=c#-test */ """ #pragma warning disable xUnit1042 #pragma warning disable xUnit1053 using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { public static List ITheoryDataRowSource; public static Task> TaskITheoryDataRowSource; public static ValueTask> ValueTaskITheoryDataRowSource; public static List> TheoryDataRowSource; public static Task>> TaskTheoryDataRowSource; public static ValueTask>> ValueTaskTheoryDataRowSource; public static IAsyncEnumerable AsyncITheoryDataRowSource; public static Task> TaskAsyncITheoryDataRowSource; public static ValueTask> ValueTaskAsyncITheoryDataRowSource; public static IAsyncEnumerable> AsyncTheoryDataRowSource; public static Task>> TaskAsyncTheoryDataRowSource; public static ValueTask>> ValueTaskAsyncTheoryDataRowSource; [MemberData(nameof(ITheoryDataRowSource))] [MemberData(nameof(TaskITheoryDataRowSource))] [MemberData(nameof(ValueTaskITheoryDataRowSource))] [MemberData(nameof(TheoryDataRowSource))] [MemberData(nameof(TaskTheoryDataRowSource))] [MemberData(nameof(ValueTaskTheoryDataRowSource))] [MemberData(nameof(AsyncITheoryDataRowSource))] [MemberData(nameof(TaskAsyncITheoryDataRowSource))] [MemberData(nameof(ValueTaskAsyncITheoryDataRowSource))] [MemberData(nameof(AsyncTheoryDataRowSource))] [MemberData(nameof(TaskAsyncTheoryDataRowSource))] [MemberData(nameof(ValueTaskAsyncTheoryDataRowSource))] public void TestMethod(int _1, string _2) { } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source); } } public class X1020_MemberDataPropertyMustHaveGetter { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #pragma warning disable xUnit1053 using Xunit; public class TestClass { public static TheoryData PublicWithGetter => new(); public static TheoryData PublicWithoutGetter { set { } } public static TheoryData ProtectedGetter { protected get { return null; } set { } } public static TheoryData InternalGetter { internal get { return null; } set { } } public static TheoryData PrivateGetter { private get { return null; } set { } } [MemberData(nameof(PublicWithGetter))] [{|xUnit1020:MemberData(nameof(PublicWithoutGetter))|}] [{|xUnit1020:MemberData(nameof(ProtectedGetter))|}] [{|xUnit1020:MemberData(nameof(InternalGetter))|}] [{|xUnit1020:MemberData(nameof(PrivateGetter))|}] public void TestMethod(int _) { } } """; await Verify.VerifyAnalyzer(LanguageVersion.CSharp9, source); } } public class X1021_MemberDataNonMethodShouldNotHaveParameters { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #pragma warning disable xUnit1053 using Xunit; public class TestClassBase { public static TheoryData BaseTestData(int n) => new TheoryData { n }; } public class TestClass : TestClassBase { private static void TestData() { } public static TheoryData SingleData(int n) => new TheoryData { n }; [MemberData(nameof(SingleData), 1)] [MemberData(nameof(SingleData), new object[] { 1 })] public void TestMethod1(int n) { } public static TheoryData ParamsData(params int[] n) => new TheoryData { n[0] }; [MemberData(nameof(ParamsData), 1, 2)] [MemberData(nameof(ParamsData), new object[] { 1, 2 })] public void TestMethod2(int n) { } [MemberData(nameof(BaseTestData), 1)] [MemberData(nameof(BaseTestData), new object[] { 1 })] public void TestMethod3(int n) { } public static TheoryData FieldData; [MemberData(nameof(FieldData), {|xUnit1021:'a', 123|})] public void TestMethod4a(int _) { } [MemberData(nameof(FieldData), {|xUnit1021:new object[] { 'a', 123 }|})] public void TestMethod4b(int _) { } public static TheoryData PropertyData { get; set; } [MemberData(nameof(PropertyData), {|xUnit1021:'a', 123|})] public void TestMethod5a(int _) { } [MemberData(nameof(PropertyData), {|xUnit1021:new object[] { 'a', 123 }|})] public void TestMethod5b(int _) { } } """; await Verify.VerifyAnalyzer(source); } } public class X1034_MemberDataArgumentsMustMatchMethodParameters_NullShouldNotBeUsedForIncompatibleParameter { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #nullable enable using Xunit; public class TestClass { public static TheoryData NullableReferenceData(string? s) => new TheoryData { s }; [MemberData(nameof(NullableReferenceData), default(string))] public void TestMethod1(string? _) { } public static TheoryData NonNullableReferenceData(string s) => new TheoryData { s }; [MemberData(nameof(NonNullableReferenceData), {|#0:default(string)|})] public void TestMethod(string _) { } #nullable disable public static TheoryData MaybeNullableReferenceData(string s) => new TheoryData { s }; #nullable enable [MemberData(nameof(MaybeNullableReferenceData), default(string))] public void TestMethod3(string? _) { } public static TheoryData NullableStructData(int? n) => new TheoryData { n }; [MemberData(nameof(NullableStructData), new object[] { null })] public void TestMethod4(int? _) { } public static TheoryData NonNullableStructData(int n) => new TheoryData { n }; [MemberData(nameof(NonNullableStructData), new object[] { {|#1:null|} })] public void TestMethod5(int _) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1034").WithLocation(0).WithArguments("s", "string"), Verify.Diagnostic("xUnit1034").WithLocation(1).WithArguments("n", "int"), }; await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source, expected); } } public class X1035_MemberDataArgumentsMustMatchMethodParameters_IncompatibleValueType { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public enum Foo { Bar } public class TestClass { public static TheoryData StringData(string s) => new TheoryData { s.Length }; [MemberData(nameof(StringData), {|#0:1|})] public void TestMethod1(int _) { } public static TheoryData ParamsIntData(params int[] n) => new TheoryData { n[0] }; [MemberData(nameof(ParamsIntData), {|#1:"bob"|})] public void TestMethod2(int _) { } // https://github.com/xunit/xunit/issues/2817 public static TheoryData EnumData(Foo foo) => new TheoryData { (int)foo }; [Theory] [MemberData(nameof(EnumData), Foo.Bar)] [MemberData(nameof(EnumData), (Foo)42)] public void TestMethod3(int _) { } // https://github.com/xunit/xunit/issues/2852 public static TheoryData IntegerSequenceData(IEnumerable seq) => new TheoryData { 42, 2112 }; [Theory] [MemberData(nameof(IntegerSequenceData), new int[] { 1, 2 })] [MemberData(nameof(IntegerSequenceData), {|#2:new char[] { 'a', 'b' }|})] public void TestMethod4(int _) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1035").WithLocation(0).WithArguments("s", "string"), Verify.Diagnostic("xUnit1035").WithLocation(1).WithArguments("n", "int"), Verify.Diagnostic("xUnit1035").WithLocation(2).WithArguments("seq", "System.Collections.Generic.IEnumerable"), }; await Verify.VerifyAnalyzer(source, expected); } } public class X1036_MemberDataArgumentsMustMatchMethodParameters_ExtraValue { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData(int n) => new TheoryData { n }; [MemberData(nameof(TestData), 1)] public void TestMethod1(int _) { } [MemberData(nameof(TestData), new object[] { 1 })] public void TestMethod2(int _) { } [MemberData(nameof(TestData), 1, {|#0:2|})] public void TestMethod3(int _) { } [MemberData(nameof(TestData), new object[] { 1, {|#1:2|} })] public void TestMethod4(int _) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1036").WithLocation(0).WithArguments("2"), Verify.Diagnostic("xUnit1036").WithLocation(1).WithArguments("2"), }; await Verify.VerifyAnalyzer(source, expected); } } public class X1037_TheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ using Xunit; public class DerivedTheoryData : TheoryData { } public class TestClass { public static TheoryData FieldData = new TheoryData(); public static TheoryData PropertyData => new TheoryData(); public static TheoryData MethodData() => new TheoryData(); public static TheoryData MethodDataWithArgs(int n) => new TheoryData(); [{|#0:MemberData(nameof(FieldData))|}] [{|#1:MemberData(nameof(PropertyData))|}] [{|#2:MemberData(nameof(MethodData))|}] [{|#3:MemberData(nameof(MethodDataWithArgs), 42)|}] public void TestMethod1(int n, string f) { } public static DerivedTheoryData DerivedFieldData = new DerivedTheoryData(); public static DerivedTheoryData DerivedPropertyData => new DerivedTheoryData(); public static DerivedTheoryData DerivedMethodData() => new DerivedTheoryData(); public static DerivedTheoryData DerivedMethodDataWithArgs(int n) => new DerivedTheoryData(); [{|#10:MemberData(nameof(DerivedFieldData))|}] [{|#11:MemberData(nameof(DerivedPropertyData))|}] [{|#12:MemberData(nameof(DerivedMethodData))|}] [{|#13:MemberData(nameof(DerivedMethodDataWithArgs), 42)|}] public void TestMethod3(int n, string f) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1037").WithLocation(0).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1037").WithLocation(1).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1037").WithLocation(2).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1037").WithLocation(3).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1037").WithLocation(10).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1037").WithLocation(11).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1037").WithLocation(12).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1037").WithLocation(13).WithArguments("Xunit.TheoryData"), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async ValueTask V3_only() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable> NullFieldData = null; public static IEnumerable> NullPropertyData => null; public static IEnumerable> NullMethodData() => null; public static IEnumerable> NullMethodDataWithArgs(int n) => null; [{|#0:MemberData(nameof(NullFieldData))|}] [{|#1:MemberData(nameof(NullPropertyData))|}] [{|#2:MemberData(nameof(NullMethodData))|}] [{|#3:MemberData(nameof(NullMethodDataWithArgs), 42)|}] public void TestMethod2(int n, string f) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1037").WithLocation(0).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1037").WithLocation(1).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1037").WithLocation(2).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1037").WithLocation(3).WithArguments("Xunit.TheoryDataRow"), }; await Verify.VerifyAnalyzerV3(source, expected); } } public class X1038_TheoryDataTypeArgumentsMustMatchTestMethodParameters_ExtraTypeParameters { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #nullable enable using Xunit; public class DerivedTheoryData : TheoryData { } public class DerivedTheoryData : TheoryData { } public class DerivedTheoryData : TheoryData { } public class DerivedTheoryData2 : TheoryData { } public class DerivedTheoryData2 : TheoryData { } public class DerivedTheoryData3 : TheoryData { } public class DerivedTheoryData3 : TheoryData { } public class TestClass { // ===== Direct TheoryData<> usage ===== public static TheoryData FieldTheoryData = new TheoryData(); public static TheoryData PropertyTheoryData => new TheoryData(); public static TheoryData MethodTheoryData() => new TheoryData(); public static TheoryData MethodWithArgsTheoryData(int _) => new TheoryData(); // Exact match [MemberData(nameof(FieldTheoryData))] [MemberData(nameof(PropertyTheoryData))] [MemberData(nameof(MethodTheoryData))] [MemberData(nameof(MethodWithArgsTheoryData), 42)] public void TestMethod1a(int _) { } // Optional paramter, no argument from data source [MemberData(nameof(FieldTheoryData))] [MemberData(nameof(PropertyTheoryData))] [MemberData(nameof(MethodTheoryData))] [MemberData(nameof(MethodWithArgsTheoryData), 42)] public void TestMethod1b(int _1, int _2 = 0) { } // Params array, no argument from data source [MemberData(nameof(FieldTheoryData))] [MemberData(nameof(PropertyTheoryData))] [MemberData(nameof(MethodTheoryData))] [MemberData(nameof(MethodWithArgsTheoryData), 42)] public void TestMethod1c(int _1, params int[] _2) { } // Generic match [MemberData(nameof(FieldTheoryData))] [MemberData(nameof(PropertyTheoryData))] [MemberData(nameof(MethodTheoryData))] [MemberData(nameof(MethodWithArgsTheoryData), 42)] public void TestMethod1d(T _) { } // Generic nullable match [MemberData(nameof(FieldTheoryData))] [MemberData(nameof(PropertyTheoryData))] [MemberData(nameof(MethodTheoryData))] [MemberData(nameof(MethodWithArgsTheoryData), 42)] public void TestMethod1e(T? _) { } public static TheoryData FieldTheoryData2 = new TheoryData(); public static TheoryData PropertyTheoryData2 => new TheoryData(); public static TheoryData MethodTheoryData2() => new TheoryData(); public static TheoryData MethodWithArgsTheoryData2(int _) => new TheoryData(); // Params array, single non-array argument from data source [MemberData(nameof(FieldTheoryData2))] [MemberData(nameof(PropertyTheoryData2))] [MemberData(nameof(MethodTheoryData2))] [MemberData(nameof(MethodWithArgsTheoryData2), 42)] public void TestMethod1f(int _1, params int[] _2) { } // Too many arguments [{|#0:MemberData(nameof(FieldTheoryData2))|}] [{|#1:MemberData(nameof(PropertyTheoryData2))|}] [{|#2:MemberData(nameof(MethodTheoryData2))|}] [{|#3:MemberData(nameof(MethodWithArgsTheoryData2), 42)|}] public void TestMethod1g(int _) { } public static TheoryData FieldTheoryData3 = new TheoryData(); public static TheoryData PropertyTheoryData3 => new TheoryData(); public static TheoryData MethodTheoryData3() => new TheoryData(); public static TheoryData MethodWithArgsTheoryData3(int _) => new TheoryData(); // Extra parameter type on data source [{|#4:MemberData(nameof(FieldTheoryData3))|}] [{|#5:MemberData(nameof(PropertyTheoryData3))|}] [{|#6:MemberData(nameof(MethodTheoryData3))|}] [{|#7:MemberData(nameof(MethodWithArgsTheoryData3), 42)|}] public void TestMethod1h(int _1, params string[] _2) { } // ===== Indirect TheoryData<> without generics ===== public static DerivedTheoryData FieldDerivedTheoryData = new DerivedTheoryData(); public static DerivedTheoryData PropertyDerivedTheoryData => new DerivedTheoryData(); public static DerivedTheoryData MethodDerivedTheoryData() => new DerivedTheoryData(); public static DerivedTheoryData MethodWithArgsDerivedTheoryData(int _) => new DerivedTheoryData(); // Exact match [MemberData(nameof(FieldDerivedTheoryData))] [MemberData(nameof(PropertyDerivedTheoryData))] [MemberData(nameof(MethodDerivedTheoryData))] [MemberData(nameof(MethodWithArgsDerivedTheoryData), 42)] public void TestMethod2a(int _) { } // Optional paramter, no argument from data source [MemberData(nameof(FieldDerivedTheoryData))] [MemberData(nameof(PropertyDerivedTheoryData))] [MemberData(nameof(MethodDerivedTheoryData))] [MemberData(nameof(MethodWithArgsDerivedTheoryData), 42)] public void TestMethod2b(int _1, int _2 = 0) { } // Params array, no argument from data source [MemberData(nameof(FieldDerivedTheoryData))] [MemberData(nameof(PropertyDerivedTheoryData))] [MemberData(nameof(MethodDerivedTheoryData))] [MemberData(nameof(MethodWithArgsDerivedTheoryData), 42)] public void TestMethod2c(int _1, params int[] _2) { } // Generic match [MemberData(nameof(FieldDerivedTheoryData))] [MemberData(nameof(PropertyDerivedTheoryData))] [MemberData(nameof(MethodDerivedTheoryData))] [MemberData(nameof(MethodWithArgsDerivedTheoryData), 42)] public void TestMethod2d(T _) { } // Generic nullable match [MemberData(nameof(FieldDerivedTheoryData))] [MemberData(nameof(PropertyDerivedTheoryData))] [MemberData(nameof(MethodDerivedTheoryData))] [MemberData(nameof(MethodWithArgsDerivedTheoryData), 42)] public void TestMethod2e(T? _) { } public static DerivedTheoryData2 FieldDerivedTheoryData2 = new DerivedTheoryData2(); public static DerivedTheoryData2 PropertyDerivedTheoryData2 => new DerivedTheoryData2(); public static DerivedTheoryData2 MethodDerivedTheoryData2() => new DerivedTheoryData2(); public static DerivedTheoryData2 MethodWithArgsDerivedTheoryData2(int _) => new DerivedTheoryData2(); // Params array, single non-array argument from data source [MemberData(nameof(FieldDerivedTheoryData2))] [MemberData(nameof(PropertyDerivedTheoryData2))] [MemberData(nameof(MethodDerivedTheoryData2))] [MemberData(nameof(MethodWithArgsDerivedTheoryData2), 42)] public void TestMethod2f(int _1, params int[] _2) { } // Too many arguments [{|#10:MemberData(nameof(FieldDerivedTheoryData2))|}] [{|#11:MemberData(nameof(PropertyDerivedTheoryData2))|}] [{|#12:MemberData(nameof(MethodDerivedTheoryData2))|}] [{|#13:MemberData(nameof(MethodWithArgsDerivedTheoryData2), 42)|}] public void TestMethod2g(int _) { } public static DerivedTheoryData3 FieldDerivedTheoryData3 = new DerivedTheoryData3(); public static DerivedTheoryData3 PropertyDerivedTheoryData3 => new DerivedTheoryData3(); public static DerivedTheoryData3 MethodDerivedTheoryData3() => new DerivedTheoryData3(); public static DerivedTheoryData3 MethodWithArgsDerivedTheoryData3(int _) => new DerivedTheoryData3(); // Extra parameter type on data source [{|#14:MemberData(nameof(FieldDerivedTheoryData3))|}] [{|#15:MemberData(nameof(PropertyDerivedTheoryData3))|}] [{|#16:MemberData(nameof(MethodDerivedTheoryData3))|}] [{|#17:MemberData(nameof(MethodWithArgsDerivedTheoryData3), 42)|}] public void TestMethod2h(int _1, params string[] _2) { } // ===== Indirect TheoryData<> with generics ===== public static DerivedTheoryData FieldDerivedGenericTheoryData = new DerivedTheoryData(); public static DerivedTheoryData PropertyDerivedGenericTheoryData => new DerivedTheoryData(); public static DerivedTheoryData MethodDerivedGenericTheoryData() => new DerivedTheoryData(); public static DerivedTheoryData MethodWithArgsDerivedGenericTheoryData(int _) => new DerivedTheoryData(); // Exact match [MemberData(nameof(FieldDerivedGenericTheoryData))] [MemberData(nameof(PropertyDerivedGenericTheoryData))] [MemberData(nameof(MethodDerivedGenericTheoryData))] [MemberData(nameof(MethodWithArgsDerivedGenericTheoryData), 42)] public void TestMethod3a(int _) { } // Optional paramter, no argument from data source [MemberData(nameof(FieldDerivedGenericTheoryData))] [MemberData(nameof(PropertyDerivedGenericTheoryData))] [MemberData(nameof(MethodDerivedGenericTheoryData))] [MemberData(nameof(MethodWithArgsDerivedGenericTheoryData), 42)] public void TestMethod3b(int _1, int _2 = 0) { } // Params array, no argument from data source [MemberData(nameof(FieldDerivedGenericTheoryData))] [MemberData(nameof(PropertyDerivedGenericTheoryData))] [MemberData(nameof(MethodDerivedGenericTheoryData))] [MemberData(nameof(MethodWithArgsDerivedGenericTheoryData), 42)] public void TestMethod3c(int _1, params int[] _2) { } // Generic match [MemberData(nameof(FieldDerivedGenericTheoryData))] [MemberData(nameof(PropertyDerivedGenericTheoryData))] [MemberData(nameof(MethodDerivedGenericTheoryData))] [MemberData(nameof(MethodWithArgsDerivedGenericTheoryData), 42)] public void TestMethod3d(T _) { } // Generic nullable match [MemberData(nameof(FieldDerivedGenericTheoryData))] [MemberData(nameof(PropertyDerivedGenericTheoryData))] [MemberData(nameof(MethodDerivedGenericTheoryData))] [MemberData(nameof(MethodWithArgsDerivedGenericTheoryData), 42)] public void TestMethod3e(T? _) { } public static DerivedTheoryData2 FieldDerivedGenericTheoryData2 = new DerivedTheoryData2(); public static DerivedTheoryData2 PropertyDerivedGenericTheoryData2 => new DerivedTheoryData2(); public static DerivedTheoryData2 MethodDerivedGenericTheoryData2() => new DerivedTheoryData2(); public static DerivedTheoryData2 MethodWithArgsDerivedGenericTheoryData2(int _) => new DerivedTheoryData2(); // Params array, single non-array argument from data source [MemberData(nameof(FieldDerivedGenericTheoryData2))] [MemberData(nameof(PropertyDerivedGenericTheoryData2))] [MemberData(nameof(MethodDerivedGenericTheoryData2))] [MemberData(nameof(MethodWithArgsDerivedGenericTheoryData2), 42)] public void TestMethod3f(int _1, params int[] _2) { } // Too many arguments [{|#20:MemberData(nameof(FieldDerivedGenericTheoryData2))|}] [{|#21:MemberData(nameof(PropertyDerivedGenericTheoryData2))|}] [{|#22:MemberData(nameof(MethodDerivedGenericTheoryData2))|}] [{|#23:MemberData(nameof(MethodWithArgsDerivedGenericTheoryData2), 42)|}] public void TestMethod3g(int _) { } public static DerivedTheoryData3 FieldDerivedGenericTheoryData3 = new DerivedTheoryData3(); public static DerivedTheoryData3 PropertyDerivedGenericTheoryData3 => new DerivedTheoryData3(); public static DerivedTheoryData3 MethodDerivedGenericTheoryData3() => new DerivedTheoryData3(); public static DerivedTheoryData3 MethodWithArgsDerivedGenericTheoryData3(int _) => new DerivedTheoryData3(); // Extra parameter type on data source [{|#24:MemberData(nameof(FieldDerivedGenericTheoryData3))|}] [{|#25:MemberData(nameof(PropertyDerivedGenericTheoryData3))|}] [{|#26:MemberData(nameof(MethodDerivedGenericTheoryData3))|}] [{|#27:MemberData(nameof(MethodWithArgsDerivedGenericTheoryData3), 42)|}] public void TestMethod3h(int _1, params string[] _2) { } // ===== Indirect TheoryData<> with generic type reduction ===== public static DerivedTheoryData FieldTheoryDataTypeReduced = new DerivedTheoryData(); public static DerivedTheoryData PropertyTheoryDataTypeReduced => new DerivedTheoryData(); public static DerivedTheoryData MethodTheoryDataTypeReduced() => new DerivedTheoryData(); public static DerivedTheoryData MethodWithArgsTheoryDataTypeReduced(int _) => new DerivedTheoryData(); // Exact match [MemberData(nameof(FieldTheoryDataTypeReduced))] [MemberData(nameof(PropertyTheoryDataTypeReduced))] [MemberData(nameof(MethodTheoryDataTypeReduced))] [MemberData(nameof(MethodWithArgsTheoryDataTypeReduced), 42)] public void TestMethod4a(int _) { } // Generic match [MemberData(nameof(FieldTheoryDataTypeReduced))] [MemberData(nameof(PropertyTheoryDataTypeReduced))] [MemberData(nameof(MethodTheoryDataTypeReduced))] [MemberData(nameof(MethodWithArgsTheoryDataTypeReduced), 42)] public void TestMethod4d(T _) { } // Generic nullable match [MemberData(nameof(FieldTheoryDataTypeReduced))] [MemberData(nameof(PropertyTheoryDataTypeReduced))] [MemberData(nameof(MethodTheoryDataTypeReduced))] [MemberData(nameof(MethodWithArgsTheoryDataTypeReduced), 42)] public void TestMethod4e(T? _) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1038").WithLocation(0).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(1).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(2).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(3).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(4).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(5).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(6).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(7).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(10).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(11).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(12).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(13).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(14).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(15).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(16).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(17).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(20).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(21).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(22).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(23).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(24).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(25).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(26).WithArguments("Xunit.TheoryData"), Verify.Diagnostic("xUnit1038").WithLocation(27).WithArguments("Xunit.TheoryData"), }; await Verify.VerifyAnalyzer(LanguageVersion.CSharp9, source, expected); } [Fact] public async ValueTask V3_only() { var source = /* lang=c#-test */ """ #nullable enable using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryDataRow[] FieldData = new TheoryDataRow[0]; public static TheoryDataRow[] PropertyData => new TheoryDataRow[0]; public static TheoryDataRow[] MethodData() => new TheoryDataRow[0]; public static TheoryDataRow[] MethodWithArgsData(int _) => new TheoryDataRow[0]; // Exact match [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1a(int _) { } // Optional paramter, no argument from data source [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1b(int _1, int _2 = 0) { } // Params array, no argument from data source [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1c(int _1, params int[] _2) { } // Generic match [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1d(T _) { } // Generic nullable match [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1e(T? _) { } public static TheoryDataRow[] FieldData2 = new TheoryDataRow[0]; public static TheoryDataRow[] PropertyData2 => new TheoryDataRow[0]; public static TheoryDataRow[] MethodData2() => new TheoryDataRow[0]; public static TheoryDataRow[] MethodWithArgsData2(int _) => new TheoryDataRow[0]; // Params array, single non-array argument from data source [MemberData(nameof(FieldData2))] [MemberData(nameof(PropertyData2))] [MemberData(nameof(MethodData2))] [MemberData(nameof(MethodWithArgsData2), 42)] public void TestMethod1f(int _1, params int[] _2) { } // Too many arguments [{|#0:MemberData(nameof(FieldData2))|}] [{|#1:MemberData(nameof(PropertyData2))|}] [{|#2:MemberData(nameof(MethodData2))|}] [{|#3:MemberData(nameof(MethodWithArgsData2), 42)|}] public void TestMethod1g(int _) { } public static TheoryDataRow[] FieldData3 = new TheoryDataRow[0]; public static TheoryDataRow[] PropertyData3 => new TheoryDataRow[0]; public static TheoryDataRow[] MethodData3() => new TheoryDataRow[0]; public static TheoryDataRow[] MethodWithArgsData3(int _) => new TheoryDataRow[0]; // Extra parameter type on data source [{|#4:MemberData(nameof(FieldData3))|}] [{|#5:MemberData(nameof(PropertyData3))|}] [{|#6:MemberData(nameof(MethodData3))|}] [{|#7:MemberData(nameof(MethodWithArgsData3), 42)|}] public void TestMethod1h(int _1, params string[] _2) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1038").WithLocation(0).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1038").WithLocation(1).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1038").WithLocation(2).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1038").WithLocation(3).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1038").WithLocation(4).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1038").WithLocation(5).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1038").WithLocation(6).WithArguments("Xunit.TheoryDataRow"), Verify.Diagnostic("xUnit1038").WithLocation(7).WithArguments("Xunit.TheoryDataRow"), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source, expected); } } public class X1039_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleTypes { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData FieldData = new TheoryData(); public static TheoryData PropertyData => new TheoryData(); public static TheoryData MethodData() => new TheoryData(); public static TheoryData MethodWithArgsData(int _) => new TheoryData(); // Exact match [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1(int _1, params string[] _2) { } public static TheoryData FieldDataCollapse = new TheoryData(); public static TheoryData PropertyDataCollapse => new TheoryData(); public static TheoryData MethodDataCollapse() => new TheoryData(); public static TheoryData MethodWithArgsDataCollapse(int _) => new TheoryData(); // Multiple values can be collapsed into the params array [MemberData(nameof(FieldDataCollapse))] [MemberData(nameof(PropertyDataCollapse))] [MemberData(nameof(MethodDataCollapse))] [MemberData(nameof(MethodWithArgsDataCollapse), 42)] public void TestMethod2(int _1, params string[] _2) { } public static TheoryData<(int, int)> FieldNamelessTupleData = new TheoryData<(int, int)>(); public static TheoryData<(int, int)> PropertyNamelessTupleData => new TheoryData<(int, int)>(); public static TheoryData<(int, int)> MethodNamelessTupleData() => new TheoryData<(int, int)>(); public static TheoryData<(int, int)> MethodWithArgsNamelessTupleData(int _) => new TheoryData<(int, int)>(); // Nameless anonymous tuples [MemberData(nameof(FieldNamelessTupleData))] [MemberData(nameof(PropertyNamelessTupleData))] [MemberData(nameof(MethodNamelessTupleData))] [MemberData(nameof(MethodWithArgsNamelessTupleData), 42)] public void TestMethod3((int a, int b) _) { } public static TheoryData<(int x, int y)> FieldNamedTupleData = new TheoryData<(int x, int y)>(); public static TheoryData<(int x, int y)> PropertyNamedTupleData => new TheoryData<(int x, int y)>(); public static TheoryData<(int x, int y)> MethodNamedTupleData() => new TheoryData<(int x, int y)>(); public static TheoryData<(int x, int y)> MethodWithArgsNamedTupleData(int _) => new TheoryData<(int x, int y)>(); // Named anonymous tuples (names don't need to match, just the shape) [MemberData(nameof(FieldNamedTupleData))] [MemberData(nameof(PropertyNamedTupleData))] [MemberData(nameof(MethodNamedTupleData))] [MemberData(nameof(MethodWithArgsNamedTupleData), 42)] public void TestMethod4((int a, int b) _) { } public static TheoryData FieldArrayData = new TheoryData(); public static TheoryData PropertyArrayData => new TheoryData(); public static TheoryData MethodArrayData() => new TheoryData(); public static TheoryData MethodWithArgsArrayData(int _) => new TheoryData(); // https://github.com/xunit/xunit/issues/3007 [MemberData(nameof(FieldArrayData))] [MemberData(nameof(PropertyArrayData))] [MemberData(nameof(MethodArrayData))] [MemberData(nameof(MethodWithArgsArrayData), 42)] public void TestMethod5a(T[] _1) {{ }} [MemberData(nameof(FieldArrayData))] [MemberData(nameof(PropertyArrayData))] [MemberData(nameof(MethodArrayData))] [MemberData(nameof(MethodWithArgsArrayData), 42)] public void TestMethod5b(IEnumerable _1) {{ }} public static TheoryData FieldWithExtraArgData = new TheoryData(); public static TheoryData PropertyWithExtraArgData => new TheoryData(); public static TheoryData MethodWithExtraArgData() => new TheoryData(); public static TheoryData MethodWithArgsWithExtraArgData(int _) => new TheoryData(); // Extra argument does not match params array type [MemberData(nameof(FieldWithExtraArgData))] [MemberData(nameof(PropertyWithExtraArgData))] [MemberData(nameof(MethodWithExtraArgData))] [MemberData(nameof(MethodWithArgsWithExtraArgData))] public void TestMethod6(int _1, params {|#0:string[]|} _2) { } public static TheoryData FieldIncompatibleData = new TheoryData(); public static TheoryData PropertyIncompatibleData => new TheoryData(); public static TheoryData MethodIncompatibleData() => new TheoryData(); public static TheoryData MethodWithArgsIncompatibleData(int _) => new TheoryData(); // Incompatible data type [MemberData(nameof(FieldIncompatibleData))] [MemberData(nameof(PropertyIncompatibleData))] [MemberData(nameof(MethodIncompatibleData))] [MemberData(nameof(MethodWithArgsIncompatibleData))] public void TestMethod7({|#1:string|} _) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("int", "TestClass.FieldWithExtraArgData", "_2"), Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("int", "TestClass.PropertyWithExtraArgData", "_2"), Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("int", "TestClass.MethodWithExtraArgData", "_2"), Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("int", "TestClass.MethodWithArgsWithExtraArgData", "_2"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "TestClass.FieldIncompatibleData", "_"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "TestClass.PropertyIncompatibleData", "_"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "TestClass.MethodIncompatibleData", "_"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "TestClass.MethodWithArgsIncompatibleData", "_"), }; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source, expected); } [Fact] public async ValueTask V3_only() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable> FieldData = new List>(); public static IEnumerable> PropertyData => new List>(); public static IEnumerable> MethodData() => new List>(); public static IEnumerable> MethodWithArgsData(int _) => new List>(); // Exact match [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1(int _1, params string[] _2) { } public static IEnumerable> FieldDataCollapse = new List>(); public static IEnumerable> PropertyDataCollapse => new List>(); public static IEnumerable> MethodDataCollapse() => new List>(); public static IEnumerable> MethodWithArgsDataCollapse(int _) => new List>(); // Multiple values can be collapsed into the params array [MemberData(nameof(FieldDataCollapse))] [MemberData(nameof(PropertyDataCollapse))] [MemberData(nameof(MethodDataCollapse))] [MemberData(nameof(MethodWithArgsDataCollapse), 42)] public void TestMethod2(int _1, params string[] _2) { } public static IEnumerable> FieldNamelessTupleData = new List>(); public static IEnumerable> PropertyNamelessTupleData => new List>(); public static IEnumerable> MethodNamelessTupleData() => new List>(); public static IEnumerable> MethodWithArgsNamelessTupleData(int _) => new List>(); // Nameless anonymous tuples [MemberData(nameof(FieldNamelessTupleData))] [MemberData(nameof(PropertyNamelessTupleData))] [MemberData(nameof(MethodNamelessTupleData))] [MemberData(nameof(MethodWithArgsNamelessTupleData), 42)] public void TestMethod3((int a, int b) _) { } public static IEnumerable> FieldNamedTupleData = new List>(); public static IEnumerable> PropertyNamedTupleData => new List>(); public static IEnumerable> MethodNamedTupleData() => new List>(); public static IEnumerable> MethodWithArgsNamedTupleData(int _) => new List>(); // Named anonymous tuples (names don't need to match, just the shape) [MemberData(nameof(FieldNamedTupleData))] [MemberData(nameof(PropertyNamedTupleData))] [MemberData(nameof(MethodNamedTupleData))] [MemberData(nameof(MethodWithArgsNamedTupleData), 42)] public void TestMethod4((int a, int b) _) { } public static IEnumerable> FieldArrayData = new List>(); public static IEnumerable> PropertyArrayData => new List>(); public static IEnumerable> MethodArrayData() => new List>(); public static IEnumerable> MethodWithArgsArrayData(int _) => new List>(); // https://github.com/xunit/xunit/issues/3007 [MemberData(nameof(FieldArrayData))] [MemberData(nameof(PropertyArrayData))] [MemberData(nameof(MethodArrayData))] [MemberData(nameof(MethodWithArgsArrayData), 42)] public void TestMethod5a(T[] _1) {{ }} [MemberData(nameof(FieldArrayData))] [MemberData(nameof(PropertyArrayData))] [MemberData(nameof(MethodArrayData))] [MemberData(nameof(MethodWithArgsArrayData), 42)] public void TestMethod5b(IEnumerable _1) {{ }} public static IEnumerable> FieldWithExtraArgData = new List>(); public static IEnumerable> PropertyWithExtraArgData => new List>(); public static IEnumerable> MethodWithExtraArgData() => new List>(); public static IEnumerable> MethodWithArgsWithExtraArgData(int _) => new List>(); // Extra argument does not match params array type [MemberData(nameof(FieldWithExtraArgData))] [MemberData(nameof(PropertyWithExtraArgData))] [MemberData(nameof(MethodWithExtraArgData))] [MemberData(nameof(MethodWithArgsWithExtraArgData))] public void TestMethod6(int _1, params {|#0:string[]|} _2) { } public static IEnumerable> FieldIncompatibleData = new List>(); public static IEnumerable> PropertyIncompatibleData => new List>(); public static IEnumerable> MethodIncompatibleData() => new List>(); public static IEnumerable> MethodWithArgsIncompatibleData(int _) => new List>(); // Incompatible data type [MemberData(nameof(FieldIncompatibleData))] [MemberData(nameof(PropertyIncompatibleData))] [MemberData(nameof(MethodIncompatibleData))] [MemberData(nameof(MethodWithArgsIncompatibleData))] public void TestMethod7({|#1:string|} _) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("int", "TestClass.FieldWithExtraArgData", "_2"), Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("int", "TestClass.PropertyWithExtraArgData", "_2"), Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("int", "TestClass.MethodWithExtraArgData", "_2"), Verify.Diagnostic("xUnit1039").WithLocation(0).WithArguments("int", "TestClass.MethodWithArgsWithExtraArgData", "_2"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "TestClass.FieldIncompatibleData", "_"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "TestClass.PropertyIncompatibleData", "_"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "TestClass.MethodIncompatibleData", "_"), Verify.Diagnostic("xUnit1039").WithLocation(1).WithArguments("int", "TestClass.MethodWithArgsIncompatibleData", "_"), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7, source, expected); } } public class X1040_TheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleNullability { [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #nullable enable using Xunit; public class TestClass { public static TheoryData FieldData = new TheoryData(); public static TheoryData PropertyData => new TheoryData(); public static TheoryData MethodData() => new TheoryData(); public static TheoryData MethodWithArgsData(int _) => new TheoryData(); [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod({|#0:string|} _) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "TestClass.FieldData", "_"), Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "TestClass.PropertyData", "_"), Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "TestClass.MethodData", "_"), Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "TestClass.MethodWithArgsData", "_"), }; await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source, expected); } [Fact] public async ValueTask V3_only() { var source = /* lang=c#-test */ """ #nullable enable using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable> FieldData = new List>(); public static IEnumerable> PropertyData => new List>(); public static IEnumerable> MethodData() => new List>(); public static IEnumerable> MethodWithArgsData(int _) => new List>(); [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod({|#0:string|} _) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "TestClass.FieldData", "_"), Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "TestClass.PropertyData", "_"), Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "TestClass.MethodData", "_"), Verify.Diagnostic("xUnit1040").WithLocation(0).WithArguments("string?", "TestClass.MethodWithArgsData", "_"), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } } public class X1042_MemberDataTheoryDataIsRecommendedForStronglyTypedAnalysis { const string V2AllowedTypes = "TheoryData<>"; const string V3AllowedTypes = "TheoryData<> or IEnumerable>"; [Fact] public async ValueTask V2_and_V3() { var source = /* lang=c#-test */ """ #pragma warning disable xUnit1053 using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData FieldData; public static TheoryData PropertyData { get; set; } public static TheoryData MethodData() => null; public static TheoryData MethodWithArgsData(int _) => null; [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1(int _) { } public static IEnumerable FieldUntypedData; public static IEnumerable PropertyUntypedData { get; set; } public static List MethodUntypedData() => null; public static object[][] MethodWithArgsUntypedData(int _) => null; [{|#0:MemberData(nameof(FieldUntypedData))|}] [{|#1:MemberData(nameof(PropertyUntypedData))|}] [{|#2:MemberData(nameof(MethodUntypedData))|}] [{|#3:MemberData(nameof(MethodWithArgsUntypedData), 42)|}] public void TestMethod2(int _) { } } """; var expectedV2 = new[] { Verify.Diagnostic("xUnit1042").WithLocation(0).WithArguments(V2AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(1).WithArguments(V2AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(2).WithArguments(V2AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(3).WithArguments(V2AllowedTypes), }; var expectedV3 = new[] { Verify.Diagnostic("xUnit1042").WithLocation(0).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(1).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(2).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(3).WithArguments(V3AllowedTypes), }; await Verify.VerifyAnalyzerV2(source, expectedV2); await Verify.VerifyAnalyzerV3(source, expectedV3); } [Fact] public async Task V3_only() { var source = /* lang=c#-test */ """ #pragma warning disable xUnit1053 using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { public static MatrixTheoryData FieldData; public static MatrixTheoryData PropertyData { get; set; } public static MatrixTheoryData MethodData() => null; public static MatrixTheoryData MethodWithArgsData(int _) => null; [MemberData(nameof(FieldData))] [MemberData(nameof(PropertyData))] [MemberData(nameof(MethodData))] [MemberData(nameof(MethodWithArgsData), 42)] public void TestMethod1(int _1, string _2) { } public static IEnumerable> FieldEnumerableData; public static IAsyncEnumerable> PropertyEnumerableData { get; set; } public static List> MethodEnumerableData() => null; public static TheoryDataRow[] MethodWithArgsEnumerableData(int _) => null; [MemberData(nameof(FieldEnumerableData))] [MemberData(nameof(PropertyEnumerableData))] [MemberData(nameof(MethodEnumerableData))] [MemberData(nameof(MethodWithArgsEnumerableData), 42)] public void TestMethod2(int _) { } public static Task> FieldUntypedTaskData; public static Task> PropertyUntypedTaskData { get; set; } public static Task> MethodUntypedTaskData() => null; public static Task MethodWithArgsUntypedTaskData(int _) => null; [{|#0:MemberData(nameof(FieldUntypedTaskData))|}] [{|#1:MemberData(nameof(PropertyUntypedTaskData))|}] [{|#2:MemberData(nameof(MethodUntypedTaskData))|}] [{|#3:MemberData(nameof(MethodWithArgsUntypedTaskData), 42)|}] public void TestMethod3(int _) { } public static ValueTask> FieldUntypedValueTaskData; public static ValueTask> PropertyUntypedValueTaskData { get; set; } public static ValueTask> MethodUntypedValueTaskData() => default; public static ValueTask MethodWithArgsUntypedValueTaskData(int _) => default; [{|#10:MemberData(nameof(FieldUntypedValueTaskData))|}] [{|#11:MemberData(nameof(PropertyUntypedValueTaskData))|}] [{|#12:MemberData(nameof(MethodUntypedValueTaskData))|}] [{|#13:MemberData(nameof(MethodWithArgsUntypedValueTaskData), 42)|}] public void TestMethod4(int _) { } public static Task> FieldUntypedTaskData2; public static Task> PropertyUntypedTaskData2 { get; set; } public static Task> MethodUntypedTaskData2() => null; public static Task MethodWithArgsUntypedTaskData2(int _) => null; [{|#20:MemberData(nameof(FieldUntypedTaskData2))|}] [{|#21:MemberData(nameof(PropertyUntypedTaskData2))|}] [{|#22:MemberData(nameof(MethodUntypedTaskData2))|}] [{|#23:MemberData(nameof(MethodWithArgsUntypedTaskData2), 42)|}] public void TestMethod5(int _) { } public static ValueTask> FieldUntypedValueTaskData2; public static ValueTask> PropertyUntypedValueTaskData2 { get; set; } public static ValueTask> MethodUntypedValueTaskData2() => default; public static ValueTask MethodWithArgsUntypedValueTaskData2(int _) => default; [{|#30:MemberData(nameof(FieldUntypedValueTaskData2))|}] [{|#31:MemberData(nameof(PropertyUntypedValueTaskData2))|}] [{|#32:MemberData(nameof(MethodUntypedValueTaskData2))|}] [{|#33:MemberData(nameof(MethodWithArgsUntypedValueTaskData2), 42)|}] public void TestMethod6(int _) { } public static TheoryData LongData; [MemberData(nameof(LongData))] public void TestMethod7(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o) { } } """; var expected = new[] { Verify.Diagnostic("xUnit1042").WithLocation(0).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(1).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(2).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(3).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(10).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(11).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(12).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(13).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(20).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(21).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(22).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(23).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(30).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(31).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(32).WithArguments(V3AllowedTypes), Verify.Diagnostic("xUnit1042").WithLocation(33).WithArguments(V3AllowedTypes), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, source, expected); } } public class X1053_MemberDataMemberMustBeStaticallyWrittenTo { [Fact] public async ValueTask Initializers_AndGetExpressions_MarkAsWrittenTo() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData Field = null; public static TheoryData Property { get; } = null; public static TheoryData PropertyWithGetBody { get { return null; } } public static TheoryData PropertyWithGetExpression => null; public static TheoryData FieldWrittenInStaticConstructor; public static TheoryData PropertyWrittenInStaticConstructor { get; set; } static TestClass() { TestClass.FieldWrittenInStaticConstructor = null; PropertyWrittenInStaticConstructor = null; } [Theory] [MemberData(nameof(Field))] [MemberData(nameof(Property))] [MemberData(nameof(PropertyWithGetBody))] [MemberData(nameof(PropertyWithGetExpression))] [MemberData(nameof(FieldWrittenInStaticConstructor))] [MemberData(nameof(PropertyWrittenInStaticConstructor))] public void TestCase(int _) {} } """; await Verify.VerifyAnalyzer(source, []); } [Fact] public async ValueTask SimpleCase_GeneratesResult() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData {|#0:Field|}; public static TheoryData {|#1:Property|} { get; set; } public TestClass() { TestClass.Field = null; Property = null; } [Theory] [MemberData(nameof(Field))] [MemberData(nameof(Property))] public void TestCase(int _) {} } """; var expected = new[] { Verify.Diagnostic("xUnit1053").WithLocation(0).WithArguments("Field"), Verify.Diagnostic("xUnit1053").WithLocation(1).WithArguments("Property"), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async ValueTask OutOfScopeCase_GeneratesResult() { var source = /* lang=c#-test */ """ using Xunit; public class TheoryInitializer { static TheoryInitializer() { TestClass.Field = null; TestClass.Property = null; } } public class TestClass { public static TheoryData {|#0:Field|}; public static TheoryData {|#1:Property|} { get; set; } static TestClass() { new TheoryInitializer(); } [Theory] [MemberData(nameof(Field))] [MemberData(nameof(Property))] public void TestCase(int _) {} } """; var expected = new[] { Verify.Diagnostic("xUnit1053").WithLocation(0).WithArguments("Field"), Verify.Diagnostic("xUnit1053").WithLocation(1).WithArguments("Property"), }; await Verify.VerifyAnalyzer(source, expected); } } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/PublicMethodShouldBeMarkedAsTestTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class PublicMethodShouldBeMarkedAsTestTests { [Fact] public async Task PublicMethodInNonTestClass_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData(/* lang=c#-test */ "Xunit.Fact")] [InlineData(/* lang=c#-test */ "Xunit.Theory")] public async Task TestMethods_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [{0}] public void TestMethod() {{ }} }} """, attribute); await Verify.VerifyAnalyzer(source); } [Fact] public async Task IDisposableDisposeMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass: System.IDisposable { [Xunit.Fact] public void TestMethod() { } public void Dispose() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task PublicAbstractMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ public abstract class TestClass { [Xunit.Fact] public void TestMethod() { } public abstract void AbstractMethod(); } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task DerivedMethodWithFactOnBaseAbstractMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ public abstract class BaseClass { [Xunit.Fact] public abstract void TestMethod(); } public class TestClass : BaseClass { public override void TestMethod() { } [Xunit.Fact] public void TestMethod2() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task PublicAbstractMethodMarkedWithFact_DoesNotTrigger() { var source = /* lang=c#-test */ """ public abstract class TestClass { [Xunit.Fact] public void TestMethod() { } [Xunit.Fact] public abstract void AbstractMethod(); } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task IDisposableDisposeMethodOverrideFromParentClass_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class BaseClass: System.IDisposable { public virtual void Dispose() { } } public class TestClass: BaseClass { [Xunit.Fact] public void TestMethod() { } public override void Dispose() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task IDisposableDisposeMethodOverrideFromParentClassWithRepeatedInterfaceDeclaration_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class BaseClass: System.IDisposable { public virtual void Dispose() { } } public class TestClass: BaseClass, System.IDisposable { [Xunit.Fact] public void TestMethod() { } public override void Dispose() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task IDisposableDisposeMethodOverrideFromGrandParentClass_DoesNotTrigger() { var source = /* lang=c#-test */ """ public abstract class BaseClass: System.IDisposable { public abstract void Dispose(); } public abstract class IntermediateClass: BaseClass { } public class TestClass: IntermediateClass { [Xunit.Fact] public void TestMethod() { } public override void Dispose() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task IAsyncLifetimeMethods_V2_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass: Xunit.IAsyncLifetime { [Xunit.Fact] public void TestMethod() { } public System.Threading.Tasks.Task DisposeAsync() { throw new System.NotImplementedException(); } public System.Threading.Tasks.Task InitializeAsync() { throw new System.NotImplementedException(); } } """; await Verify.VerifyAnalyzerV2(source); } [Fact] public async Task IAsyncLifetimeMethods_V3_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass: Xunit.IAsyncLifetime { [Xunit.Fact] public void TestMethod() { } public System.Threading.Tasks.ValueTask DisposeAsync() { throw new System.NotImplementedException(); } public System.Threading.Tasks.ValueTask InitializeAsync() { throw new System.NotImplementedException(); } } """; await Verify.VerifyAnalyzerV3(source); } [Fact] public async Task PublicMethodMarkedWithAttributeWhichIsMarkedWithIgnoreXunitAnalyzersRule1013_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class IgnoreXunitAnalyzersRule1013Attribute: System.Attribute { } [IgnoreXunitAnalyzersRule1013] public class CustomTestTypeAttribute: System.Attribute { } public class TestClass { [Xunit.Fact] public void TestMethod() { } [CustomTestType] public void CustomTestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task PublicMethodMarkedWithAttributeWhichInheritsFromAttributeMarkedWithIgnoreXunitAnalyzersRule1013_Triggers() { var source = /* lang=c#-test */ """ public class IgnoreXunitAnalyzersRule1013Attribute: System.Attribute { } [IgnoreXunitAnalyzersRule1013] public class BaseCustomTestTypeAttribute: System.Attribute { } public class DerivedCustomTestTypeAttribute: BaseCustomTestTypeAttribute { } public class TestClass { [Xunit.Fact] public void TestMethod() { } [DerivedCustomTestType] public void {|#0:CustomTestMethod|}() { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("CustomTestMethod", "TestClass", "Fact"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(/* lang=c#-test */ "Xunit.Fact")] [InlineData(/* lang=c#-test */ "Xunit.Theory")] public async Task PublicMethodWithoutParametersInTestClass_Triggers(string attribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [{0}] public void TestMethod() {{ }} public void {{|#0:Method|}}() {{ }} }} """, attribute); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Method", "TestClass", "Fact"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(/* lang=c#-test */ "Xunit.Fact")] [InlineData(/* lang=c#-test */ "Xunit.Theory")] public async Task PublicMethodWithParametersInTestClass_Triggers(string attribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [{0}] public void TestMethod() {{ }} public void {{|#0:Method|}}(int a) {{ }} }} """, attribute); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Method", "TestClass", "Theory"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(/* lang=c#-test */ "Fact")] [InlineData(/* lang=c#-test */ "Theory")] public async Task OverridenMethod_FromParentNonTestClass_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ using Xunit; public abstract class ParentClass {{ public abstract void ParentMethod(); }} public class TestClass : ParentClass {{ [{0}] public void TestMethod() {{ }} public override void ParentMethod() {{ }} public override void {{|CS0115:MissingMethod|}}() {{ }} }} """, attribute); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TestClassCannotBeNestedInGenericClassTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class TestClassCannotBeNestedInGenericClassTests { [Fact] public async Task WhenTestClassIsNestedInOpenGenericType_Triggers() { var source = /* lang=c#-test */ """ public abstract class OpenGenericType { public class [|NestedTestClass|] { [Xunit.Fact] public void TestMethod() { } } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task WhenDerivedTestClassIsNestedInOpenGenericType_Triggers() { var source = /* lang=c#-test */ """ public abstract class BaseTestClass { [Xunit.Fact] public void TestMethod() { } } public abstract class OpenGenericType { public class [|NestedTestClass|] : BaseTestClass { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task WhenTestClassIsNestedInClosedGenericType_DoesNotTrigger() { var source = /* lang=c#-test */ """ public abstract class OpenGenericType { } public abstract class ClosedGenericType : OpenGenericType { public class NestedTestClass { [Xunit.Fact] public void TestMethod() { } } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task WhenNestedClassIsNotTestClass_DoesNotTrigger() { var source = /* lang=c#-test */ """ public abstract class OpenGenericType { public class NestedClass { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task WhenTestClassIsNotNestedInOpenGenericType_DoesNotTrigger() { var source = /* lang=c#-test */ """ public abstract class NonGenericType { public class NestedTestClass { [Xunit.Fact] public void TestMethod() { } } } """; await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TestClassMustBePublicTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class TestClassMustBePublicTests { public static MatrixTheoryData CreateFactsInNonPublicClassCases = new( /* lang=c#-test */ ["Xunit.Fact", "Xunit.Theory"], /* lang=c#-test */ ["", "internal"] ); [Fact] public async Task ForPublicClass_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(CreateFactsInNonPublicClassCases))] public async Task ForFriendOrInternalClass_Triggers( string attribute, string modifier) { var source = string.Format(/* lang=c#-test */ """ {1} class [|TestClass|] {{ [{0}] public void TestMethod() {{ }} }} """, attribute, modifier); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData(/* lang=c#-test */ "")] [InlineData(/* lang=c#-test */ "public")] public async Task ForPartialClassInSameFile_WhenClassIsPublic_DoesNotTrigger(string modifier) { var source = string.Format(/* lang=c#-test */ """ public partial class TestClass {{ [Xunit.Fact] public void Test1() {{ }} }} {0} partial class TestClass {{ [Xunit.Fact] public void Test2() {{ }} }} """, modifier); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData(/* lang=c#-test */ "")] [InlineData(/* lang=c#-test */ "public")] public async Task ForPartialClassInOtherFiles_WhenClassIsPublic_DoesNotTrigger(string modifier) { var source1 = /* lang=c#-test */ """ public partial class TestClass { [Xunit.Fact] public void Test1() { } } """; var source2 = string.Format(/* lang=c#-test */ """ {0} partial class TestClass {{ [Xunit.Fact] public void Test2() {{ }} }} """, modifier); await Verify.VerifyAnalyzer([source1, source2]); } [Theory] [InlineData("", "")] [InlineData("", "internal")] [InlineData("internal", "internal")] public async Task ForPartialClassInSameFile_WhenClassIsNonPublic_Triggers( string modifier1, string modifier2) { var source = string.Format(/* lang=c#-test */ """ {0} partial class {{|#0:TestClass|}} {{ [Xunit.Fact] public void Test1() {{ }} }} {1} partial class {{|#1:TestClass|}} {{ [Xunit.Fact] public void Test2() {{ }} }} """, modifier1, modifier2); var expected = Verify.Diagnostic().WithLocation(0).WithLocation(1); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData("", "")] [InlineData("", "internal")] [InlineData("internal", "internal")] public async Task ForPartialClassInOtherFiles_WhenClassIsNonPublic_Triggers( string modifier1, string modifier2) { var source1 = string.Format(/* lang=c#-test */ """ {0} partial class {{|#0:TestClass|}} {{ [Xunit.Fact] public void Test1() {{ }} }} """, modifier1); var source2 = string.Format(/* lang=c#-test */ """ {0} partial class {{|#1:TestClass|}} {{ [Xunit.Fact] public void Test2() {{ }} }} """, modifier2); var expected = Verify.Diagnostic().WithLocation(0).WithLocation(1); await Verify.VerifyAnalyzer([source1, source2], expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TestClassShouldHaveTFixtureArgumentTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class TestClassShouldHaveTFixtureArgumentTests { public static MatrixTheoryData CreateFactsInNonPublicClassCases = new( /* lang=c#-test */ ["Xunit.Fact", "Xunit.Theory"], /* lang=c#-test */ ["Xunit.IClassFixture", "Xunit.ICollectionFixture"] ); [Theory] [MemberData(nameof(CreateFactsInNonPublicClassCases))] public async Task ForClassWithIClassFixtureWithoutConstructorArg_Triggers( string attribute, string @interface) { var source = string.Format(/* lang=c#-test */ """ public class FixtureData {{ }} public class {{|#0:TestClass|}}: {1} {{ [{0}] public void TestMethod() {{ }} }} """, attribute, @interface); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestClass", "FixtureData"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(CreateFactsInNonPublicClassCases))] public async Task ForClassWithIClassFixtureWithConstructorArg_DoesNotTrigger( string attribute, string @interface) { var source = string.Format(/* lang=c#-test */ """ public class FixtureData {{ }} public class TestClass: {1} {{ public TestClass(FixtureData fixtureData) {{ }} [{0}] public void TestMethod() {{ }} }} """, attribute, @interface); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(CreateFactsInNonPublicClassCases))] public async Task ForClassWithIClassFixtureWithConstructorMultipleArg_DoesNotTrigger( string attribute, string @interface) { var source = /* lang=c#-test */ """ public class FixtureData {{ }} public class TestClass: {1} {{ public TestClass(FixtureData fixtureData, {2}.ITestOutputHelper output) {{ }} [{0}] public void TestMethod() {{ }} }} """; await Verify.VerifyAnalyzerV2(string.Format(source, attribute, @interface, "Xunit.Abstractions")); await Verify.VerifyAnalyzerV3(string.Format(source, attribute, @interface, "Xunit")); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TestMethodCannotHaveOverloadsTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class TestMethodCannotHaveOverloadsTests { [Fact] public async Task ForInstanceMethodOverloads_InSameInstanceClass_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void {|#0:TestMethod|}() { } [Xunit.Theory] public void {|#1:TestMethod|}(int a) { } } """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "TestClass"), Verify.Diagnostic().WithLocation(1).WithArguments("TestMethod", "TestClass", "TestClass"), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ForStaticMethodOverloads_InSameStaticClass_Triggers() { var source = /* lang=c#-test */ """ public static class TestClass { [Xunit.Fact] public static void {|#0:TestMethod|}() { } [Xunit.Theory] public static void {|#1:TestMethod|}(int a) { } } """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "TestClass"), Verify.Diagnostic().WithLocation(1).WithArguments("TestMethod", "TestClass", "TestClass"), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ForInstanceMethodOverload_InDerivedClass_Triggers() { var source1 = /* lang=c#-test */ """ public class TestClass : BaseClass { [Xunit.Theory] public void {|#0:TestMethod|}(int a) { } private void {|#1:TestMethod|}(int a, byte c) { } } """; var source2 = /* lang=c#-test */ """ public class BaseClass { [Xunit.Fact] public void TestMethod() { } } """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "BaseClass"), Verify.Diagnostic().WithLocation(1).WithArguments("TestMethod", "TestClass", "BaseClass"), }; await Verify.VerifyAnalyzer([source1, source2], expected); } [Fact] public async Task ForStaticAndInstanceMethodOverload_Triggers() { var source1 = /* lang=c#-test */ """ public class TestClass : BaseClass { [Xunit.Theory] public void {|#0:TestMethod|}(int a) { } } """; var source2 = /* lang=c#-test */ """ public class BaseClass { [Xunit.Fact] public static void TestMethod() { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "BaseClass"); await Verify.VerifyAnalyzer([source1, source2], expected); } [Fact] public async Task ForMethodOverrides_DoesNotTrigger() { var source1 = /* lang=c#-test */ """ public class BaseClass { [Xunit.Fact] public virtual void TestMethod() { } } """; var source2 = /* lang=c#-test */ """ public class TestClass : BaseClass { [Xunit.Fact] public override void TestMethod() { } } """; await Verify.VerifyAnalyzer([source1, source2]); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TestMethodMustNotHaveMultipleFactAttributesTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class TestMethodMustNotHaveMultipleFactAttributesTests { [Theory] [InlineData("Fact")] [InlineData("Theory")] public async Task MethodWithSingleAttribute_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.{0}] public void TestMethod() {{ }} }} """, attribute); await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodWithFactAndTheory_Triggers() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] [Xunit.Theory] public void [|TestMethod|]() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task MethodWithFactAndCustomFactAttribute_Triggers() { var source1 = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] [CustomFact] public void [|TestMethod|]() { } } """; var source2 = /* lang=c#-test */ """ public class CustomFactAttribute : Xunit.FactAttribute { } """; await Verify.VerifyAnalyzer([source1, source2]); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TestMethodShouldNotBeSkippedTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class TestMethodShouldNotBeSkippedTests { [Theory] [InlineData("Fact")] [InlineData("Theory")] public async Task NotSkippedTest_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ [Xunit.{0}] public void TestMethod() {{ }} }} """, attribute); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("Fact")] [InlineData("Theory")] public async Task SkippedTest_Triggers(string attribute) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ [Xunit.{0}([|Skip="Lazy"|])] public void TestMethod() {{ }} }} """, attribute); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("Fact")] [InlineData("Theory")] public async Task SkippedTestWhenConditionIsTrue_V3_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ public static bool Condition {{ get; set; }} [Xunit.{0}(Skip="Lazy", SkipWhen=nameof(Condition))] public void TestMethod() {{ }} }} """, attribute); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7, source); } [Theory] [InlineData("Fact")] [InlineData("Theory")] public async Task SkippedTestUnlessConditionIsTrue_V3_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ public static bool Condition {{ get; set; }} [Xunit.{0}(Skip="Lazy", SkipUnless=nameof(Condition))] public void TestMethod() {{ }} }} """, attribute); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7, source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TestMethodSupportedReturnTypeTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class TestMethodSupportedReturnTypeTests { [Fact] public async Task NonTestMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class NonTestClass { public int Add(int x, int y) { return x + y; } } """; await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("object")] [InlineData("Task")] [InlineData("ValueTask")] public async Task InvalidReturnType_Triggers(string returnType) { var source = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public {0} {{|#0:TestMethod|}}() {{ return default({0}); }} }} """, returnType); var expectedV2 = Verify.Diagnostic().WithLocation(0).WithArguments("void, Task"); var expectedV3 = Verify.Diagnostic().WithLocation(0).WithArguments("void, Task, ValueTask"); await Verify.VerifyAnalyzerV2(source, expectedV2); await Verify.VerifyAnalyzerV3(source, expectedV3); } [Fact] public async Task ValueTask_TriggersInV2_DoesNotTriggerInV3() { var source = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public ValueTask {|#0:TestMethod|}() { return default(ValueTask); } } """; var expectedV2 = Verify.Diagnostic().WithLocation(0).WithArguments("void, Task"); await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp7, source, expectedV2); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7, source); } [Theory] [InlineData("MyTest")] [InlineData("MyTestAttribute")] public async Task CustomTestAttribute_DoesNotTrigger(string attribute) { var source = string.Format(/* lang=c#-test */ """ using Xunit; class MyTestAttribute : FactAttribute {{ }} public class TestClass {{ [{0}] public int TestMethod() {{ return 0; }} }} """, attribute); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TheoryDataRowArgumentsShouldBeSerializableTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v3_Pre301 = CSharpVerifier; public sealed class TheoryDataRowArgumentsShouldBeSerializableTests { [Fact] public async Task ParamArrayArguments_NotUsingTheoryDataRow_DoesNotTrigger() { var source = /* lang=c#-test */ """ #nullable enable public class Foo { public Foo(params object[] args) { } } public class TestClass { public void TestMethod() { var foo = new Foo(new object()); } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Theory] [InlineData("\"String value\"", "string")] [InlineData("'a'", "char")] [InlineData("(byte)42", "byte")] [InlineData("(sbyte)42", "sbyte")] [InlineData("(short)42", "short")] [InlineData("(ushort)42", "ushort")] [InlineData("42", "int")] [InlineData("42U", "uint")] [InlineData("42L", "long")] [InlineData("42UL", "ulong")] [InlineData("21.12F", "float")] [InlineData("21.12D", "double")] [InlineData("21.12M", "decimal")] [InlineData("true", "bool")] [InlineData("DateTime.Now", "DateTime")] [InlineData("DateTimeOffset.Now", "DateTimeOffset")] [InlineData("TimeSpan.Zero", "TimeSpan")] [InlineData("BigInteger.One", "BigInteger")] [InlineData("typeof(TheoryDataRow)", "Type")] [InlineData("ConsoleColor.Red", "ConsoleColor")] [InlineData("new Dictionary>()", "Dictionary>")] #if NET6_0_OR_GREATER [InlineData("DateOnly.MinValue", "DateOnly")] [InlineData("Index.Start", "Index")] [InlineData("Range.All", "Range")] [InlineData("TimeOnly.MinValue", "TimeOnly")] #endif [InlineData("Guid.Empty", "Guid")] [InlineData("new Uri(\"https://xunit.net/\")", "Uri")] [InlineData("new Version(\"1.2.3\")", "Version")] public async Task IntrinsicallySerializableValue_DoesNotTrigger( string value, string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable using System; using System.Collections.Generic; using System.Numerics; using Xunit; public class MyClass {{ public IEnumerable MyMethod() {{ var value = {0}; var defaultValue = default({1}); var nullValue = default({1}?); var arrayValue = new {1}[0]; yield return new TheoryDataRow(value, defaultValue, nullValue, arrayValue); yield return new TheoryDataRow<{1}, {1}?, {1}?, {1}[]>({0}, default({1}), default({1}?), new {1}[0]); }} }} """, value, type); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Theory] [InlineData("SerializableClass")] [InlineData("SerializableStruct")] public async Task IXunitSerializableValue_DoesNotTrigger(string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable using System.Collections.Generic; using Xunit; using Xunit.Sdk; public class MyClass {{ public IEnumerable MyMethod() {{ var value = new {0}(); var defaultValue = default({0}); var nullValue = default({0}?); var arrayValue = new {0}[0]; yield return new TheoryDataRow(value, defaultValue, nullValue, arrayValue); yield return new TheoryDataRow<{0}, {0}?, {0}?, {0}[]>(new {0}(), default({0}), default({0}?), new {0}[0]); }} }} public interface ISerializableInterface : IXunitSerializable {{ }} public class SerializableClass : ISerializableInterface {{ public void Deserialize(IXunitSerializationInfo info) {{ }} public void Serialize(IXunitSerializationInfo info) {{ }} }} public struct SerializableStruct : ISerializableInterface {{ public void Deserialize(IXunitSerializationInfo info) {{ }} public void Serialize(IXunitSerializationInfo info) {{ }} }} """, type); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Theory] [InlineData("CustomSerialized")] [InlineData("CustomSerializedDerived")] public async Task IXunitSerializerValue_DoesNotTrigger(string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable using System; using System.Collections.Generic; using Xunit; using Xunit.Sdk; [assembly: RegisterXunitSerializer(typeof(CustomSerializer), typeof(ICustomSerialized))] public class MyClass {{ public IEnumerable MyMethod() {{ var value = new {0}(); var defaultValue = default({0}); var nullValue = default({0}?); var arrayValue = new {0}[0]; yield return new TheoryDataRow(value, defaultValue, nullValue, arrayValue); yield return new TheoryDataRow<{0}, {0}?, {0}?, {0}[]>(new {0}(), default({0}), default({0}?), new {0}[0]); }} }} public interface ICustomSerialized {{ }} public class CustomSerialized : ICustomSerialized {{ }} public class CustomSerializedDerived : CustomSerialized {{ }} public class CustomSerializer : IXunitSerializer {{ public object Deserialize(Type type, string serializedValue) => throw new NotImplementedException(); public bool IsSerializable(Type type, object? value, out string? failureReason) {{ failureReason = null; return true; }} public string Serialize(object value) => throw new NotImplementedException(); }} """, type); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Theory] [InlineData("Delegate", "Delegate?", "Delegate?")] [InlineData("Func", "Func?", "Func?")] [InlineData("NonSerializableSealedClass", "NonSerializableSealedClass?", "NonSerializableSealedClass?")] [InlineData("NonSerializableStruct", "NonSerializableStruct", "NonSerializableStruct?")] public async Task KnownNonSerializableValue_Triggers1046( string type, string defaultValueType, string nullValueType) { var source = string.Format(/* lang=c#-test */ """ #nullable enable using System; using System.Collections.Generic; using Xunit; public class MyClass {{ public IEnumerable MyMethod() {{ var defaultValue = default({0}); var nullValue = default({0}?); var arrayValue = new {0}[0]; yield return new TheoryDataRow({{|#0:defaultValue|}}, {{|#1:nullValue|}}, {{|#2:arrayValue|}}); yield return new TheoryDataRow<{1}, {2}, {0}[]>({{|#3:default({0})|}}, {{|#4:default({0}?)|}}, {{|#5:new {0}[0]|}}); }} }} public sealed class NonSerializableSealedClass {{ }} public struct NonSerializableStruct {{ }} """, type, defaultValueType, nullValueType); var expected = new[] { Verify.Diagnostic("xUnit1046").WithLocation(0).WithArguments("defaultValue", defaultValueType), Verify.Diagnostic("xUnit1046").WithLocation(1).WithArguments("nullValue", nullValueType), Verify.Diagnostic("xUnit1046").WithLocation(2).WithArguments("arrayValue", $"{type}[]"), Verify.Diagnostic("xUnit1046").WithLocation(3).WithArguments($"default({type})", defaultValueType), Verify.Diagnostic("xUnit1046").WithLocation(4).WithArguments($"default({type}?)", nullValueType), Verify.Diagnostic("xUnit1046").WithLocation(5).WithArguments($"new {type}[0]", $"{type}[]"), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } [Theory] [InlineData("NonSerializableSealedClass")] [InlineData("NonSerializableStruct")] public async Task KnownNonSerializableValue_Constructable_Triggers1046(string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable using System.Collections.Generic; using Xunit; public class MyClass {{ public IEnumerable MyMethod() {{ var value = new {0}(); yield return new TheoryDataRow({{|#0:value|}}); yield return new TheoryDataRow({{|#1:new {0}()|}}); yield return new TheoryDataRow<{0}>({{|#2:value|}}); yield return new TheoryDataRow<{0}>({{|#3:new {0}()|}}); }} }} public sealed class NonSerializableSealedClass {{ }} public struct NonSerializableStruct {{ }} """, type); var expected = new[] { Verify.Diagnostic("xUnit1046").WithLocation(0).WithArguments("value", type), Verify.Diagnostic("xUnit1046").WithLocation(1).WithArguments($"new {type}()", type), Verify.Diagnostic("xUnit1046").WithLocation(2).WithArguments("value", type), Verify.Diagnostic("xUnit1046").WithLocation(3).WithArguments($"new {type}()", type), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } [Theory] [InlineData("object")] [InlineData("Array")] [InlineData("ValueType")] [InlineData("IEnumerable")] [InlineData("IEnumerable")] [InlineData("Dictionary")] [InlineData("IPossiblySerializableInterface")] [InlineData("PossiblySerializableUnsealedClass")] public async Task MaybeNonSerializableValue_Triggers1047(string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable using System; using System.Collections; using System.Collections.Generic; using Xunit; public class MyClass {{ public IEnumerable MyMethod() {{ var defaultValue = default({0}); var nullValue = default({0}?); var arrayValue = new {0}[0]; yield return new TheoryDataRow({{|#0:defaultValue|}}, {{|#1:nullValue|}}, {{|#2:arrayValue|}}); yield return new TheoryDataRow<{0}, {0}, {0}[]>({{|#3:default({0})|}}, {{|#4:default({0}?)|}}, {{|#5:new {0}[0]|}}); }} }} public interface IPossiblySerializableInterface {{ }} public class PossiblySerializableUnsealedClass {{ }} """, type); var expected = new[] { Verify.Diagnostic("xUnit1047").WithLocation(0).WithArguments("defaultValue", $"{type}?"), Verify.Diagnostic("xUnit1047").WithLocation(1).WithArguments("nullValue", $"{type}?"), Verify.Diagnostic("xUnit1047").WithLocation(2).WithArguments("arrayValue", $"{type}[]"), Verify.Diagnostic("xUnit1047").WithLocation(3).WithArguments($"default({type})", $"{type}?"), Verify.Diagnostic("xUnit1047").WithLocation(4).WithArguments($"default({type}?)", $"{type}?"), Verify.Diagnostic("xUnit1047").WithLocation(5).WithArguments($"new {type}[0]", $"{type}[]"), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } [Theory] [InlineData("object")] [InlineData("Dictionary")] [InlineData("PossiblySerializableUnsealedClass")] public async Task MaybeNonSerializableValue_Constructable_Triggers1047(string type) { var source = string.Format(/* lang=c#-test */ """ #nullable enable using System.Collections.Generic; using Xunit; public class MyClass {{ public IEnumerable MyMethod() {{ var value = new {0}(); yield return new TheoryDataRow({{|#0:value|}}); yield return new TheoryDataRow({{|#1:new {0}()|}}); yield return new TheoryDataRow<{0}>({{|#2:value|}}); yield return new TheoryDataRow<{0}>({{|#3:new {0}()|}}); }} }} public class PossiblySerializableUnsealedClass {{ }} """, type); var expected = new[] { Verify.Diagnostic("xUnit1047").WithLocation(0).WithArguments("value", type), Verify.Diagnostic("xUnit1047").WithLocation(1).WithArguments($"new {type}()", type), Verify.Diagnostic("xUnit1047").WithLocation(2).WithArguments("value", type), Verify.Diagnostic("xUnit1047").WithLocation(3).WithArguments($"new {type}()", type), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } [Fact] public async Task IFormattableAndIParseable_DoesNotTrigger() { var source = /* lang=c#-test */ """ #nullable enable using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Xunit; public class Formattable : IFormattable { public string ToString(string? format, IFormatProvider? formatProvider) => string.Empty; } public class Parsable : IParsable { public static Parsable Parse(string s, IFormatProvider? provider) => new(); public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Parsable result) { result = new(); return true; } } public class FormattableAndParsable : IFormattable, IParsable { public static FormattableAndParsable Parse(string s, IFormatProvider? provider) => new(); public string ToString(string? format, IFormatProvider? formatProvider) => string.Empty; public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out FormattableAndParsable result) { result = new(); return true; } } public class FormattableData { public IEnumerable MyMethod() { var defaultValue = default(Formattable); var nullValue = default(Formattable?); var arrayValue = new Formattable[0]; yield return new TheoryDataRow({|#0:defaultValue|}, {|#1:nullValue|}, {|#2:arrayValue|}); yield return new TheoryDataRow({|#3:default(Formattable)|}, {|#4:default(Formattable?)|}, {|#5:new Formattable[0]|}); } } public class ParsableData { public IEnumerable MyMethod() { var defaultValue = default(Parsable); var nullValue = default(Parsable?); var arrayValue = new Parsable[0]; yield return new TheoryDataRow({|#10:defaultValue|}, {|#11:nullValue|}, {|#12:arrayValue|}); yield return new TheoryDataRow({|#13:default(Parsable)|}, {|#14:default(Parsable?)|}, {|#15:new Parsable[0]|}); } } public class FormattableAndParsableData { public IEnumerable MyMethod() { var defaultValue = default(FormattableAndParsable); var nullValue = default(FormattableAndParsable?); var arrayValue = new FormattableAndParsable[0]; yield return new TheoryDataRow(defaultValue, nullValue, arrayValue); yield return new TheoryDataRow(default(FormattableAndParsable), default(FormattableAndParsable?), new FormattableAndParsable[0]); } } """; #if ROSLYN_LATEST && NET8_0_OR_GREATER var expected = new[] { Verify.Diagnostic("xUnit1047").WithLocation(0).WithArguments("defaultValue", "Formattable?"), Verify.Diagnostic("xUnit1047").WithLocation(1).WithArguments("nullValue", "Formattable?"), Verify.Diagnostic("xUnit1047").WithLocation(2).WithArguments("arrayValue", "Formattable[]"), Verify.Diagnostic("xUnit1047").WithLocation(3).WithArguments("default(Formattable)", "Formattable?"), Verify.Diagnostic("xUnit1047").WithLocation(4).WithArguments("default(Formattable?)", "Formattable?"), Verify.Diagnostic("xUnit1047").WithLocation(5).WithArguments("new Formattable[0]", "Formattable[]"), Verify.Diagnostic("xUnit1047").WithLocation(10).WithArguments("defaultValue", "Parsable?"), Verify.Diagnostic("xUnit1047").WithLocation(11).WithArguments("nullValue", "Parsable?"), Verify.Diagnostic("xUnit1047").WithLocation(12).WithArguments("arrayValue", "Parsable[]"), Verify.Diagnostic("xUnit1047").WithLocation(13).WithArguments("default(Parsable)", "Parsable?"), Verify.Diagnostic("xUnit1047").WithLocation(14).WithArguments("default(Parsable?)", "Parsable?"), Verify.Diagnostic("xUnit1047").WithLocation(15).WithArguments("new Parsable[0]", "Parsable[]"), }; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp11, source, expected); #else // For some reason, 'dotnet format' complains about the indenting of #nullable enable in the source code line // above if the #if statement surrounds the whole method, so we use this "workaround" to do nothing in that case. Assert.NotEqual(string.Empty, source); await Task.Yield(); #endif } [Fact] public async Task Tuples_OnlySupportedByV3_3_0_1() { var source = /* lang=c#-test */ """ #nullable enable using System; using System.Collections.Generic; using Xunit; public class MyClass { public IEnumerable MyTupleMethod() { var value = Tuple.Create("Hello world", 42); yield return new TheoryDataRow({|#0:value|}); yield return new TheoryDataRow({|#1:Tuple.Create("Hello world", 42)|}); yield return new TheoryDataRow>({|#2:value|}); yield return new TheoryDataRow>({|#3:Tuple.Create("Hello world", 42)|}); } public IEnumerable MyValueTupleMethod() { var value = ValueTuple.Create("Hello world", 42); yield return new TheoryDataRow({|#10:value|}); yield return new TheoryDataRow({|#11:ValueTuple.Create("Hello world", 42)|}); yield return new TheoryDataRow>({|#12:value|}); yield return new TheoryDataRow>({|#13:ValueTuple.Create("Hello world", 42)|}); } } public class PossiblySerializableUnsealedClass { } """; var expectedUnsupported = new[] { Verify.Diagnostic("xUnit1047").WithLocation(0).WithArguments("value", "Tuple"), Verify.Diagnostic("xUnit1047").WithLocation(1).WithArguments("Tuple.Create(\"Hello world\", 42)", "Tuple"), Verify.Diagnostic("xUnit1047").WithLocation(2).WithArguments("value", "Tuple"), Verify.Diagnostic("xUnit1047").WithLocation(3).WithArguments("Tuple.Create(\"Hello world\", 42)", "Tuple"), Verify.Diagnostic("xUnit1046").WithLocation(10).WithArguments("value", "(string, int)"), Verify.Diagnostic("xUnit1046").WithLocation(11).WithArguments("ValueTuple.Create(\"Hello world\", 42)", "(string, int)"), Verify.Diagnostic("xUnit1046").WithLocation(12).WithArguments("value", "(string, int)"), Verify.Diagnostic("xUnit1046").WithLocation(13).WithArguments("ValueTuple.Create(\"Hello world\", 42)", "(string, int)"), }; await Verify_v3_Pre301.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expectedUnsupported); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } internal class Analyzer_v3_Pre301 : TheoryDataRowArgumentsShouldBeSerializable { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV3(compilation, new Version(3, 0, 0)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TheoryDataShouldNotUseTheoryDataRowTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class TheoryDataShouldNotUseTheoryDataRowTests { [Fact] public async Task AcceptanceTest() { var source = /* lang=C#-test */ """ using System; using System.Collections.Generic; using Xunit; public class Triggers1 { // Constructors public Triggers1([|TheoryData|] data) { } public Triggers1([|TheoryData>|] data) { } public Triggers1([|TheoryData|] data) { } // Fields private [|TheoryData|] field1 = new [|TheoryData|](); private [|TheoryData>|] field2 = new [|TheoryData>|](); private [|TheoryData|] field3 = new [|TheoryData|](); // Array fields public [|TheoryData|][] field11 = null; public [|TheoryData>|][] field12 = null; public [|TheoryData|][] field13 = null; // Static fields private static [|TheoryData|] field21 = null; private static [|TheoryData>|] field22 = null; private static [|TheoryData|] field23 = null; // Properties public [|TheoryData|] property1 { get; set; } = new [|TheoryData|](); public [|TheoryData>|] property2 { get; set; } = new [|TheoryData>|](); public [|TheoryData|] property3 { get; set; } = new [|TheoryData|](); // Methods public [|TheoryData|] Method1() { return null; } public [|TheoryData>|] Method2() { return null; } public [|TheoryData|] Method3() { return null; } } // Generic constraints class Triggers2 where T : ITheoryDataRow { [|TheoryData|] data = null; } class Triggers3 where T : MyRow { [|TheoryData|] data = null; } public class DoesNotTrigger { IEnumerable field1 = new List(); IEnumerable> field2 = new List>(); IEnumerable field3 = new List(); IEnumerable property1 { get; set; } = new List(); IEnumerable> property2 { get; set; } = new List>(); IEnumerable property3 { get; set; } = new List(); IEnumerable method1() { return null; } IEnumerable> method2() { return null; } IEnumerable method3() { return null; } } public class MyRow : ITheoryDataRow { public object?[] GetData() { return null; } public bool? Explicit { get; } public string? Label { get; } public string? Skip { get; } public Type? SkipType { get; } public string? SkipUnless { get; } public string? SkipWhen { get; } public string? TestDisplayName { get; } public int? Timeout { get; } public Dictionary>? Traits { get; } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TheoryDataTypeArgumentsShouldBeSerializableTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v3_Pre301 = CSharpVerifier; public class TheoryDataTypeArgumentsShouldBeSerializableTests { public static TheoryData TheoryDataClass( string type1, string type2, string type3) => TheoryDataClass(theoryAttribute: "Theory", type1, type2, type3); public static TheoryData TheoryDataClass( string theoryAttribute, string type1, string type2, string type3) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ [{0}] [ClassData(typeof(DerivedClass))] public void TestMethod({1} a, {2} b, {3} c) {{ }} }} public class BaseClass : TheoryData {{ }} public class DerivedClass : BaseClass<{1}, {2}, object, Delegate> {{ }} """, theoryAttribute, type1, type2, type3); return new TheoryData { { source, type1, type2, type3 } }; } public static TheoryData TheoryDataMembers(string type) { var c = string.Format(/* lang=c#-test */ "public sealed class Class : TheoryData<{0}> {{ }}", type); var f = string.Format(/* lang=c#-test */ "public static readonly TheoryData<{0}> Field = new TheoryData<{0}>() {{ }};", type); var m = string.Format(/* lang=c#-test */ "public static TheoryData<{0}> Method(int a, string b) => new TheoryData<{0}>() {{ }};", type); var p = string.Format(/* lang=c#-test */ "public static TheoryData<{0}> Property => new TheoryData<{0}>() {{ }};", type); return new TheoryData { { c, "ClassData(typeof(Class))", type }, { f, "MemberData(nameof(Field))", type }, { m, @"MemberData(nameof(Method), 1, ""2"")", type }, { p, "MemberData(nameof(Property))", type } }; } public static TheoryData TheoryDataMembersWithDiscoveryEnumerationDisabled(string type) { var f = string.Format(/* lang=c#-test */ "public static readonly TheoryData<{0}> Field = new TheoryData<{0}>() {{ }};", type); var m = string.Format(/* lang=c#-test */ "public static TheoryData<{0}> Method(int a, string b) => new TheoryData<{0}>() {{ }};", type); var p = string.Format(/* lang=c#-test */ "public static TheoryData<{0}> Property => new TheoryData<{0}>() {{ }};", type); return new TheoryData { { f, "MemberData(nameof(Field), DisableDiscoveryEnumeration = true)", type }, { m, @"MemberData(nameof(Method), 1, ""2"", DisableDiscoveryEnumeration = true)", type }, { p, "MemberData(nameof(Property), DisableDiscoveryEnumeration = true)", type } }; } public sealed class NoDiagnostic : TheoryDataTypeArgumentsShouldBeSerializableTests { [Fact] public async Task GivenMethodWithoutAttributes_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GivenFact_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task GivenTheory_WithoutTheoryDataAsDataSource_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using System.Collections; using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable Property1 => Array.Empty(); public static DataSource Property2 => new DataSource(); [Theory] [MemberData(nameof(Property1))] [MemberData(nameof(Property2))] public void TestMethod(object a, object b) { } } public class DataSource : IEnumerable { public IEnumerator GetEnumerator() { yield break; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } """; await Verify.VerifyAnalyzer(source); } [Theory] // Serializable via XunitSerializationInfo (v2) or SerializationHelper (v3) [MemberData(nameof(TheoryDataMembers), "Type")] [MemberData(nameof(TheoryDataMembers), "Dictionary>")] [MemberData(nameof(TheoryDataMembers), "string")] [MemberData(nameof(TheoryDataMembers), "string[]")] [MemberData(nameof(TheoryDataMembers), "string[][]")] [MemberData(nameof(TheoryDataMembers), "char")] [MemberData(nameof(TheoryDataMembers), "char?")] [MemberData(nameof(TheoryDataMembers), "byte")] [MemberData(nameof(TheoryDataMembers), "byte?")] [MemberData(nameof(TheoryDataMembers), "sbyte")] [MemberData(nameof(TheoryDataMembers), "sbyte?")] [MemberData(nameof(TheoryDataMembers), "short")] [MemberData(nameof(TheoryDataMembers), "short?")] [MemberData(nameof(TheoryDataMembers), "ushort")] [MemberData(nameof(TheoryDataMembers), "ushort?")] [MemberData(nameof(TheoryDataMembers), "int")] [MemberData(nameof(TheoryDataMembers), "int[]")] [MemberData(nameof(TheoryDataMembers), "int[][]")] [MemberData(nameof(TheoryDataMembers), "int?")] [MemberData(nameof(TheoryDataMembers), "int?[]")] [MemberData(nameof(TheoryDataMembers), "int?[][]")] [MemberData(nameof(TheoryDataMembers), "uint")] [MemberData(nameof(TheoryDataMembers), "uint?")] [MemberData(nameof(TheoryDataMembers), "long")] [MemberData(nameof(TheoryDataMembers), "long?")] [MemberData(nameof(TheoryDataMembers), "ulong")] [MemberData(nameof(TheoryDataMembers), "ulong?")] [MemberData(nameof(TheoryDataMembers), "float")] [MemberData(nameof(TheoryDataMembers), "float?")] [MemberData(nameof(TheoryDataMembers), "double")] [MemberData(nameof(TheoryDataMembers), "double?")] [MemberData(nameof(TheoryDataMembers), "decimal")] [MemberData(nameof(TheoryDataMembers), "decimal?")] [MemberData(nameof(TheoryDataMembers), "bool")] [MemberData(nameof(TheoryDataMembers), "bool?")] [MemberData(nameof(TheoryDataMembers), "DateTime")] [MemberData(nameof(TheoryDataMembers), "DateTime?")] [MemberData(nameof(TheoryDataMembers), "DateTimeOffset")] [MemberData(nameof(TheoryDataMembers), "DateTimeOffset?")] [MemberData(nameof(TheoryDataMembers), "TimeSpan")] [MemberData(nameof(TheoryDataMembers), "TimeSpan?")] [MemberData(nameof(TheoryDataMembers), "BigInteger")] [MemberData(nameof(TheoryDataMembers), "BigInteger?")] #if NET6_0_OR_GREATER [MemberData(nameof(TheoryDataMembers), "DateOnly")] [MemberData(nameof(TheoryDataMembers), "DateOnly[]")] [MemberData(nameof(TheoryDataMembers), "DateOnly?")] [MemberData(nameof(TheoryDataMembers), "DateOnly?[]")] [MemberData(nameof(TheoryDataMembers), "TimeOnly")] [MemberData(nameof(TheoryDataMembers), "TimeOnly[]")] [MemberData(nameof(TheoryDataMembers), "TimeOnly?")] [MemberData(nameof(TheoryDataMembers), "TimeOnly?[]")] #endif // Serializable via XunitSerializationInfo (v2) or via built-in IXunitSerializer (v3) [MemberData(nameof(TheoryDataMembers), "Enum")] [MemberData(nameof(TheoryDataMembers), "SerializableEnumeration")] [MemberData(nameof(TheoryDataMembers), "SerializableEnumeration?")] public async Task GivenTheory_WithSerializableTheoryDataMember_DoesNotTrigger( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using System.Collections.Generic; using System.Numerics; using Xunit; public class TestClass {{ {0} [Theory] [{1}] public void TestMethod({2} parameter) {{ }} }} public enum SerializableEnumeration {{ Zero }} """, member, attribute, type); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(TheoryDataMembers), "IXunitSerializable")] [MemberData(nameof(TheoryDataMembers), "IXunitSerializable[]")] [MemberData(nameof(TheoryDataMembers), "ISerializableInterface")] [MemberData(nameof(TheoryDataMembers), "ISerializableInterface[]")] [MemberData(nameof(TheoryDataMembers), "SerializableClass")] [MemberData(nameof(TheoryDataMembers), "SerializableClass[]")] [MemberData(nameof(TheoryDataMembers), "SerializableStruct")] [MemberData(nameof(TheoryDataMembers), "SerializableStruct[]")] [MemberData(nameof(TheoryDataMembers), "SerializableStruct?")] [MemberData(nameof(TheoryDataMembers), "SerializableStruct?[]")] public async Task GivenTheory_WithIXunitSerializableTheoryDataMember_DoesNotTrigger( string member, string attribute, string type) { var sourceV2 = GetSource("Xunit.Abstractions"); var sourceV3 = GetSource("Xunit.Sdk"); await Verify.VerifyAnalyzerV2(sourceV2); await Verify.VerifyAnalyzerV3(sourceV3); string GetSource(string ns) => string.Format(/* lang=c#-test */ """ using Xunit; using {3}; public class TestClass {{ {0} [Theory] [{1}] public void TestMethod({2} parameter) {{ }} }} public interface ISerializableInterface : IXunitSerializable {{ }} public class SerializableClass : ISerializableInterface {{ public void Deserialize(IXunitSerializationInfo info) {{ }} public void Serialize(IXunitSerializationInfo info) {{ }} }} public struct SerializableStruct : ISerializableInterface {{ public void Deserialize(IXunitSerializationInfo info) {{ }} public void Serialize(IXunitSerializationInfo info) {{ }} }} """, member, attribute, type, ns); } [Theory] [MemberData(nameof(TheoryDataMembers), "ICustomSerialized")] [MemberData(nameof(TheoryDataMembers), "CustomSerialized")] [MemberData(nameof(TheoryDataMembers), "CustomSerializedDerived")] public async Task GivenTheory_WithIXunitSerializerTheoryDataMember_DoesNotTrigger( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; using Xunit.Sdk; [assembly: RegisterXunitSerializer(typeof(CustomSerializer), typeof(ICustomSerialized))] public class TestClass {{ {0} [Theory] [{1}] public void TestMethod({2} parameter) {{ }} }} public interface ICustomSerialized {{ }} public class CustomSerialized : ICustomSerialized {{ }} public class CustomSerializedDerived : CustomSerialized {{ }} public class CustomSerializer : IXunitSerializer {{ public object Deserialize(Type type, string serializedValue) => throw new NotImplementedException(); public bool IsSerializable(Type type, object? value, out string? failureReason) {{ failureReason = null; return true; }} public string Serialize(object value) => throw new NotImplementedException(); }} """, member, attribute, type ); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Theory] [MemberData(nameof(TheoryDataMembers), "Delegate")] [MemberData(nameof(TheoryDataMembers), "Delegate[]")] [MemberData(nameof(TheoryDataMembers), "NonSerializableSealedClass")] [MemberData(nameof(TheoryDataMembers), "NonSerializableStruct")] [MemberData(nameof(TheoryDataMembers), "object")] [MemberData(nameof(TheoryDataMembers), "object[]")] [MemberData(nameof(TheoryDataMembers), "IPossiblySerializableInterface")] [MemberData(nameof(TheoryDataMembers), "PossiblySerializableUnsealedClass")] public async Task GivenTheory_WithNonSerializableTheoryDataMember_WithDiscoveryEnumerationDisabledForTheory_DoesNotTrigger( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ {0} [Theory(DisableDiscoveryEnumeration = true)] [{1}] public void TestMethod({2} parameter) {{ }} }} public sealed class NonSerializableSealedClass {{ }} public struct NonSerializableStruct {{ }} public interface IPossiblySerializableInterface {{ }} public class PossiblySerializableUnsealedClass {{ }} """, member, attribute, type); await Verify.VerifyAnalyzerV3(source); } [Theory] [MemberData(nameof(TheoryDataMembersWithDiscoveryEnumerationDisabled), "Delegate")] [MemberData(nameof(TheoryDataMembersWithDiscoveryEnumerationDisabled), "Delegate[]")] [MemberData(nameof(TheoryDataMembersWithDiscoveryEnumerationDisabled), "NonSerializableSealedClass")] [MemberData(nameof(TheoryDataMembersWithDiscoveryEnumerationDisabled), "NonSerializableStruct")] [MemberData(nameof(TheoryDataMembersWithDiscoveryEnumerationDisabled), "object")] [MemberData(nameof(TheoryDataMembersWithDiscoveryEnumerationDisabled), "object[]")] [MemberData(nameof(TheoryDataMembersWithDiscoveryEnumerationDisabled), "IPossiblySerializableInterface")] [MemberData(nameof(TheoryDataMembersWithDiscoveryEnumerationDisabled), "PossiblySerializableUnsealedClass")] public async Task GivenTheory_WithNonSerializableTheoryDataMember_WithDiscoveryEnumerationDisabledForMemberData_DoesNotTrigger( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ {0} [Theory] [{1}] public void TestMethod({2} parameter) {{ }} }} public sealed class NonSerializableSealedClass {{ }} public struct NonSerializableStruct {{ }} public interface IPossiblySerializableInterface {{ }} public class PossiblySerializableUnsealedClass {{ }} """, member, attribute, type); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(TheoryDataClass), "int", "double", "string")] public async Task GivenTheory_WithSerializableTheoryDataClass_DoesNotTrigger( string source, string _1, string _2, string _3) { await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(TheoryDataClass), "Theory(DisableDiscoveryEnumeration = true)", "Action", "TimeZoneInfo", "TimeZoneInfo.TransitionTime")] [MemberData(nameof(TheoryDataClass), "Theory(DisableDiscoveryEnumeration = true)", "object[]", "Array", "IDisposable")] public async Task GivenTheory_WithNonSerializableTheoryDataClass_WithDiscoveryEnumerationDisabled_DoesNotTrigger( string source, string _1, string _2, string _3) { await Verify.VerifyAnalyzerV3(source); } } public sealed class X1044_AvoidUsingTheoryDataTypeArgumentsThatAreNotSerializable : TheoryDataTypeArgumentsShouldBeSerializableTests { [Theory] [MemberData(nameof(TheoryDataMembers), "Guid")] [MemberData(nameof(TheoryDataMembers), "Guid?")] #if NET6_0_OR_GREATER [MemberData(nameof(TheoryDataMembers), "Index")] [MemberData(nameof(TheoryDataMembers), "Range")] #endif [MemberData(nameof(TheoryDataMembers), "Version")] [MemberData(nameof(TheoryDataMembers), "Version?")] public async Task GivenTheory_WithTypeOnlySupportedInV3_TriggersInV2_DoesNotTriggerInV3( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ {0} [Theory] [{{|#0:{1}|}}] public void TestMethod({2} parameter) {{ }} }} """, member, attribute, type); var expectedV2 = Verify.Diagnostic("xUnit1044").WithLocation(0).WithArguments(type); await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp9, source, expectedV2); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source); } [Theory] [MemberData(nameof(TheoryDataMembers), "Delegate")] [MemberData(nameof(TheoryDataMembers), "Delegate[]")] [MemberData(nameof(TheoryDataMembers), "Func")] [MemberData(nameof(TheoryDataMembers), "Func[]")] [MemberData(nameof(TheoryDataMembers), "NonSerializableSealedClass")] [MemberData(nameof(TheoryDataMembers), "NonSerializableSealedClass[]")] [MemberData(nameof(TheoryDataMembers), "NonSerializableStruct")] [MemberData(nameof(TheoryDataMembers), "NonSerializableStruct[]")] [MemberData(nameof(TheoryDataMembers), "NonSerializableStruct?")] [MemberData(nameof(TheoryDataMembers), "NonSerializableStruct?[]")] public async Task GivenTheory_WithNonSerializableTheoryDataMember_Triggers( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using System.Text; using Xunit; public class TestClass {{ {0} [Theory] [{{|#0:{1}|}}] public void TestMethod({2} parameter) {{ }} }} public sealed class NonSerializableSealedClass {{ }} public struct NonSerializableStruct {{ }} """, member, attribute, type); var expected = Verify.Diagnostic("xUnit1044").WithLocation(0).WithArguments(type); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(TheoryDataClass), "Action", "TimeZoneInfo", "TimeZoneInfo.TransitionTime")] public async Task GivenTheory_WithNonSerializableTheoryDataClass_Triggers( string source, string type1, string type2, string type3) { var expected = new[] { Verify.Diagnostic("xUnit1044").WithSpan(6, 3, 6, 34).WithArguments(type1), Verify.Diagnostic("xUnit1044").WithSpan(6, 3, 6, 34).WithArguments(type2), Verify.Diagnostic("xUnit1044").WithSpan(6, 3, 6, 34).WithArguments(type3), }; await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(TheoryDataMembers), "ValueTuple")] [MemberData(nameof(TheoryDataMembers), "ValueTuple?")] public async Task GivenTheory_WithValueTuple_OnlySupportedInV3_3_0_1( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ {0} [Theory] [{{|#0:{1}|}}] public void TestMethod({2} parameter) {{ }} }} """, member, attribute, type); var expectedUnsupported = Verify.Diagnostic("xUnit1044").WithLocation(0).WithArguments(type.Replace("ValueTuple", "(string, int)")); await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp9, source, expectedUnsupported); await Verify_v3_Pre301.VerifyAnalyzerV3(LanguageVersion.CSharp9, source, expectedUnsupported); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source); } } public sealed class X1045_AvoidUsingTheoryDataTypeArgumentsThatMightNotBeSerializable : TheoryDataTypeArgumentsShouldBeSerializableTests { [Theory] [MemberData(nameof(TheoryDataMembers), "Uri")] [MemberData(nameof(TheoryDataMembers), "Uri?")] public async Task GivenTheory_WithTypeOnlySupportedInV3_TriggersInV2_DoesNotTriggerInV3( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ {0} [Theory] [{{|#0:{1}|}}] public void TestMethod({2} parameter) {{ }} }} """, member, attribute, type); var expectedV2 = Verify.Diagnostic("xUnit1045").WithLocation(0).WithArguments(type); await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp9, source, expectedV2); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source); } [Fact] public async Task IFormattableAndIParseable_TriggersInV2_DoesNotTriggerInV3() { var source = /* lang=c#-test */ """ #nullable enable using System; using System.Diagnostics.CodeAnalysis; using Xunit; public class Formattable : IFormattable { public string ToString(string? format, IFormatProvider? formatProvider) => string.Empty; } public class Parsable : IParsable { public static Parsable Parse(string s, IFormatProvider? provider) => new(); public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Parsable result) { result = new(); return true; } } public class FormattableAndParsable : IFormattable, IParsable { public static FormattableAndParsable Parse(string s, IFormatProvider? provider) => new(); public string ToString(string? format, IFormatProvider? formatProvider) => string.Empty; public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out FormattableAndParsable result) { result = new(); return true; } } public class TestClass { public static readonly TheoryData FormattableData = new TheoryData() { }; public static readonly TheoryData ParsableData = new TheoryData() { }; public static readonly TheoryData FormattableAndParsableData = new TheoryData() { }; [Theory] [{|#0:MemberData(nameof(FormattableData))|}] [{|#1:MemberData(nameof(ParsableData))|}] [{|#2:MemberData(nameof(FormattableAndParsableData))|}] public void TestMethod(object parameter) { } } """; #if ROSLYN_LATEST && NET8_0_OR_GREATER var expectedV2 = new[] { Verify.Diagnostic("xUnit1045").WithLocation(0).WithArguments("Formattable"), Verify.Diagnostic("xUnit1045").WithLocation(1).WithArguments("Parsable"), Verify.Diagnostic("xUnit1045").WithLocation(2).WithArguments("FormattableAndParsable"), }; var expectedV3 = new[] { Verify.Diagnostic("xUnit1045").WithLocation(0).WithArguments("Formattable"), Verify.Diagnostic("xUnit1045").WithLocation(1).WithArguments("Parsable"), }; await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp11, source, expectedV2); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp11, source, expectedV3); #else // For some reason, 'dotnet format' complains about the indenting of #nullable enable in the source code line // above if the #if statement surrounds the whole method, so we use this "workaround" to do nothing in that case. Assert.NotEqual(string.Empty, source); await Task.Yield(); #endif } [Theory] [MemberData(nameof(TheoryDataMembers), "Tuple")] [MemberData(nameof(TheoryDataMembers), "Tuple?")] public async Task GivenTheory_WithTuple_OnlySupportedInV3_3_0_1( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ {0} [Theory] [{{|#0:{1}|}}] public void TestMethod({2} parameter) {{ }} }} """, member, attribute, type); var expectedUnsupported = Verify.Diagnostic("xUnit1045").WithLocation(0).WithArguments(type); await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp9, source, expectedUnsupported); await Verify_v3_Pre301.VerifyAnalyzerV3(LanguageVersion.CSharp9, source, expectedUnsupported); await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, source); } [Theory] [MemberData(nameof(TheoryDataMembers), "object")] [MemberData(nameof(TheoryDataMembers), "object[]")] [MemberData(nameof(TheoryDataMembers), "Array")] [MemberData(nameof(TheoryDataMembers), "Array[]")] [MemberData(nameof(TheoryDataMembers), "ValueType")] [MemberData(nameof(TheoryDataMembers), "ValueType[]")] [MemberData(nameof(TheoryDataMembers), "IEnumerable")] [MemberData(nameof(TheoryDataMembers), "IEnumerable[]")] [MemberData(nameof(TheoryDataMembers), "IEnumerable")] [MemberData(nameof(TheoryDataMembers), "IEnumerable[]")] [MemberData(nameof(TheoryDataMembers), "Dictionary")] [MemberData(nameof(TheoryDataMembers), "Dictionary[]")] [MemberData(nameof(TheoryDataMembers), "IPossiblySerializableInterface")] [MemberData(nameof(TheoryDataMembers), "IPossiblySerializableInterface[]")] [MemberData(nameof(TheoryDataMembers), "PossiblySerializableUnsealedClass")] [MemberData(nameof(TheoryDataMembers), "PossiblySerializableUnsealedClass[]")] public async Task GivenTheory_WithPossiblySerializableTheoryDataMember_Triggers( string member, string attribute, string type) { var source = string.Format(/* lang=c#-test */ """ using System; using System.Collections; using System.Collections.Generic; using Xunit; public class TestClass {{ {0} [Theory] [{{|#0:{1}|}}] public void TestMethod({2} parameter) {{ }} }} public interface IPossiblySerializableInterface {{ }} public class PossiblySerializableUnsealedClass {{ }} """, member, attribute, type); var expected = Verify.Diagnostic("xUnit1045").WithLocation(0).WithArguments(type); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(TheoryDataClass), "object[]", "Array", "IDisposable")] public async Task GivenTheory_WithPossiblySerializableTheoryDataClass_Triggers( string source, string type1, string type2, string type3) { var expected = new[] { Verify.Diagnostic("xUnit1045").WithSpan(6, 3, 6, 34).WithArguments(type1), Verify.Diagnostic("xUnit1045").WithSpan(6, 3, 6, 34).WithArguments(type2), Verify.Diagnostic("xUnit1045").WithSpan(6, 3, 6, 34).WithArguments(type3), }; await Verify.VerifyAnalyzer(source, expected); } } internal class Analyzer_v3_Pre301 : TheoryDataTypeArgumentsShouldBeSerializable { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV3(compilation, new Version(3, 0, 0)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TheoryMethodCannotHaveDefaultParameterTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v2_Pre220 = CSharpVerifier; public class TheoryMethodCannotHaveDefaultParameterTests { [Fact] public async Task TheoryWithDefaultParameter_WhenDefaultValueNotSupported_Triggers() { var source = /* lang=c#-test */ """ class TestClass { [Xunit.Theory] public void TestMethod(int a, string b, string c {|#0:= ""|}) { } } """; var expected = Verify_v2_Pre220.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "c"); await Verify_v2_Pre220.VerifyAnalyzer(source, expected); } [Fact] public async Task TheoryWithDefaultParameter_WhenDefaultValueSupported_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { [Xunit.Theory] public void TestMethod(int a, string b, string c = "") { } } """; await Verify.VerifyAnalyzer(source); } internal class Analyzer_v2_Pre220 : TheoryMethodCannotHaveDefaultParameter { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2(compilation, new Version(2, 1, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TheoryMethodCannotHaveParamsArrayTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v2_Pre220 = CSharpVerifier; public class TheoryMethodCannotHaveParamsArrayTests { [Fact] public async Task TheoryWithParamsArrayAsync_WhenParamsArrayNotSupported_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { [Xunit.Theory] public void TestMethod(int a, string b, {|#0:params string[] c|}) { } } """; var expected = Verify_v2_Pre220.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "c"); await Verify_v2_Pre220.VerifyAnalyzer(source, expected); } [Fact] public async Task TheoryWithParamsArrayAsync_WhenParamsArraySupported_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { [Xunit.Theory] public void TestMethod(int a, string b, params string[] c) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task TheoryWithNonParamsArrayAsync_WhenParamsArrayNotSupported_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { [Xunit.Theory] public void TestMethod(int a, string b, string[] c) { } } """; await Verify_v2_Pre220.VerifyAnalyzer(source); } [Fact] public async Task TheoryWithNonParamsArrayAsync_WhenParamsArraySupported_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { [Xunit.Theory] public void TestMethod(int a, string b, string[] c) { } } """; await Verify.VerifyAnalyzer(source); } internal class Analyzer_v2_Pre220 : TheoryMethodCannotHaveParamsArray { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2(compilation, new Version(2, 1, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TheoryMethodMustHaveTestDataTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class TheoryMethodMustHaveTestDataTests { [Fact] public async Task FactMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task TheoryMethodWithDataAttributes_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData] public void TestMethod1() { } [Theory] [MemberData("")] public void TestMethod2() { } [Theory] [ClassData(typeof(string))] public void TestMethod3() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task TheoryMethodWithCustomDataAttribute_v3_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; using Xunit; using Xunit.Sdk; using Xunit.v3; public class TestClass { [Theory] [MyData] public void TestMethod() { } } public class MyData : Attribute, IDataAttribute { public bool? Explicit => throw new NotImplementedException(); public string? Label => throw new NotImplementedException(); public string? Skip => throw new NotImplementedException(); public Type? SkipType => throw new NotImplementedException(); public string? SkipUnless => throw new NotImplementedException(); public string? SkipWhen => throw new NotImplementedException(); public string? TestDisplayName => throw new NotImplementedException(); public int? Timeout => throw new NotImplementedException(); public string[]? Traits => throw new NotImplementedException(); public ValueTask> GetData( MethodInfo testMethod, DisposalTracker disposalTracker) => throw new NotImplementedException(); public bool SupportsDiscoveryEnumeration() => throw new NotImplementedException(); } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Fact] public async Task TheoryMethodMissingData_Triggers() { var source = /* lang=c#-test */ """ using Xunit; class TestClass { [Theory] public void [|TestMethod|]() { } } """; await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TheoryMethodShouldHaveParametersTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class TheoryMethodShouldHaveParametersTests { [Fact] public async Task FactMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task TheoryMethodWithParameters_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Theory] public void TestMethod(string s) { } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task TheoryMethodWithoutParameters_Triggers() { var source = /* lang=c#-test */ """ class TestClass { [Xunit.Theory] public void [|TestMethod|]() { } } """; await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/TheoryMethodShouldUseAllParametersTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class TheoryMethodShouldUseAllParametersTests { [Fact] public async Task ParameterNotReferenced_Triggers() { var source = /* lang=c#-test */ """ using Xunit; class TestClass { [Theory] void TestMethod(int {|#0:unused|}) { } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "unused"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ParameterUnread_Triggers() { var source = /* lang=c#-test */ """ using System; using Xunit; class TestClass { [Theory] void TestMethod(int {|#0:unused|}) { unused = 3; int.TryParse("123", out unused); } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "unused"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task MultipleUnreadParameters_Triggers() { var source = /* lang=c#-test */ """ using Xunit; class TestClass { [Theory] void TestMethod(int {|#0:foo|}, int {|#1:bar|}, int {|#2:baz|}) { } } """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "foo"), Verify.Diagnostic().WithLocation(1).WithArguments("TestMethod", "TestClass", "bar"), Verify.Diagnostic().WithLocation(2).WithArguments("TestMethod", "TestClass", "baz"), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task SomeUnreadParameters_Triggers() { var source = /* lang=c#-test */ """ using System; using Xunit; class TestClass { [Theory] void TestMethod(int {|#0:foo|}, int bar, int {|#1:baz|}) { Console.WriteLine(bar); baz = 3; } } """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "foo"), Verify.Diagnostic().WithLocation(1).WithArguments("TestMethod", "TestClass", "baz"), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ExpressionBodiedMethod_Triggers() { var source = /* lang=c#-test */ """ using Xunit; class TestClass { [Theory] void TestMethod(int {|#0:unused|}) => Assert.Equal(5, 2 + 2); } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "unused"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ParameterRead_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using Xunit; class TestClass { [Theory] void TestMethod(int used) { Console.WriteLine(used); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ParameterCapturedAsOutParameterInMockSetup_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using Xunit; class TestClass { [Theory] void TestMethod(string used, int usedOut) { // mimicking mock setup use case // var mock = new Mock(); // mock.Setup(m => m.SomeMethod(out used)); Action setup = () => int.TryParse(used, out usedOut); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ExpressionBodiedMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; class TestClass { [Theory] void TestMethod(int used) => Assert.Equal(used, 2 + 2); } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task WhenParameterIsDiscardNamed_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using Xunit; class TestClass { [Theory] void TestMethod(int used, string _, object _1, DateTime _42, double {|#0:_a|}) { Assert.Equal(42, used); } } """; // Only a single warning, for _a; everything else is either used or matches the discard pattern var expected = Verify.Diagnostic().WithLocation(0).WithArguments("TestMethod", "TestClass", "_a"); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task DoesNotCrash_MethodWithoutBody() { var source = /* lang=c#-test */ """ using Xunit; class TestClass { [Theory] extern void TestMethod(int foo); } """; await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X1000/UseCancellationTokenTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class UseCancellationTokenTests { [Fact] public async Task NoCancellationToken_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading; using Xunit; class TestClass { [Fact] public void TestMethod() { Thread.Sleep(1); } } """; await Verify.VerifyAnalyzerV3(source); } [Fact] public async Task NonTestMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; class TestClass { public async Task NonTestMethod() { await Task.Delay(1); } } """; await Verify.VerifyAnalyzerV3(source); } [Fact] public async Task WithAnyCancellationToken_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; class TestClass { [Fact] public void TestMethod() { FunctionWithDefaults(42, TestContext.Current.CancellationToken); FunctionWithDefaults(42, cancellationToken: TestContext.Current.CancellationToken); FunctionWithDefaults(cancellationToken: TestContext.Current.CancellationToken); var token = new CancellationTokenSource().Token; FunctionWithDefaults(42, token); FunctionWithDefaults(42, cancellationToken: token); FunctionWithDefaults(cancellationToken: token); } void FunctionWithDefaults(int _1 = 2112, CancellationToken cancellationToken = default(CancellationToken)) { } } """; await Verify.VerifyAnalyzerV3(source); } [Fact] public async Task WithoutCancellationToken_V2_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; class TestClass { [Fact] public async Task TestMethod() { await Task.Delay(1); } } """; await Verify.VerifyAnalyzerV2(source); } [Fact] public async Task WithoutCancellationToken_WithoutDirectUpgrade_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; class TestClass { [Fact] public void TestMethod() { FunctionWithOverload(42); } void FunctionWithOverload(int _) { } void FunctionWithOverload(CancellationToken _) { } } """; await Verify.VerifyAnalyzerV3(source); } [Fact] public async Task WithoutCancellationToken_V3_Triggers() { var source = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; class TestClass { [Fact] public void TestMethod() { [|FunctionWithDefaults()|]; [|FunctionWithDefaults(42)|]; [|FunctionWithDefaults(42, default)|]; [|FunctionWithDefaults(42, default(CancellationToken))|]; [|FunctionWithDefaults(cancellationToken: default)|]; [|FunctionWithDefaults(cancellationToken: default(CancellationToken))|]; [|FunctionWithOverload(42)|]; [|FunctionWithOverload(42, default)|]; [|FunctionWithOverload(42, default(CancellationToken))|]; } void FunctionWithDefaults(int _1 = 2112, CancellationToken cancellationToken = default) { } void FunctionWithOverload(int _) { } void FunctionWithOverload(int _1, CancellationToken _2) { } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, source); } [Fact] public async Task InsideLambda_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using System.Threading; using System.Threading.Tasks; using Xunit; class TestClass { [Fact] public void TestMethod() { async Task InnerMethod() { await Task.Delay(1); } Func _ = async () => await Task.Delay(1); } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7, source); } [Fact] public async Task InsideAssertionLambda_Triggers() { var source = /* lang=c#-test */ """ using System; using System.Threading; using System.Threading.Tasks; using Xunit; class TestClass { [Fact] public async ValueTask TestMethod() { await Assert.CollectionAsync(Array.Empty(), x => [|Task.Delay(x)|], x => [|Task.Delay(x)|]); await Assert.ThrowsAsync(() => [|Task.Delay(1)|]); await Record.ExceptionAsync(() => [|Task.Delay(1)|]); } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7, source); } [Fact] public async Task WhenOverloadIsObsolete_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System; using System.Threading; using System.Threading.Tasks; using Xunit; class TestClass { [Fact] public void TestMethod() { FunctionWithOverload(42); } void FunctionWithOverload(int _) {{ }} [Obsolete] void FunctionWithOverload(int _1, CancellationToken _2) {{ }} } """; await Verify.VerifyAnalyzerV3(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertCollectionContainsShouldNotUseBoolCheckTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertCollectionContainsShouldNotUseBoolCheckTests { public static TheoryData Collections = [ "new System.Collections.Generic.List()", "new System.Collections.Generic.HashSet()", "new System.Collections.ObjectModel.Collection()", ]; public static TheoryData Enumerables = [ "new int[0]", "System.Linq.Enumerable.Empty()", ]; [Theory] [MemberData(nameof(Collections))] public async Task TrueCollectionContainsCheck_Triggers(string collection) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.True({0}.Contains(1))|}}; }} }} """, collection); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.Contains); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Collections))] public async Task FalseCollectionContainsCheck_Triggers(string collection) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.False({0}.Contains(1))|}}; }} }} """, collection); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.False()", Constants.Asserts.DoesNotContain); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Enumerables))] public async Task TrueLinqContainsCheck_Triggers(string enumerable) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.True({0}.Contains(1))|}}; }} }} """, enumerable); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.Contains); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Enumerables))] public async Task TrueLinqContainsCheckWithEqualityComparer_Triggers(string enumerable) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.True({0}.Contains(1, System.Collections.Generic.EqualityComparer.Default))|}}; }} }} """, enumerable); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.Contains); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Enumerables))] public async Task FalseLinqContainsCheck_Triggers(string enumerable) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.False({0}.Contains(1))|}}; }} }} """, enumerable); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.False()", Constants.Asserts.DoesNotContain); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Enumerables))] public async Task FalseLinqContainsCheckWithEqualityComparer_Triggers(string enumerable) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.False({0}.Contains(1, System.Collections.Generic.EqualityComparer.Default))|}}; }} }} """, enumerable); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.False()", Constants.Asserts.DoesNotContain); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Collections))] public async Task TrueCollectionContainsCheckWithAssertionMessage_DoesNotTrigger(string collection) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.True({0}.Contains(1), "Custom message"); }} }} """, collection); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Collections))] public async Task FalseCollectionContainsCheckWithAssertionMessage_DoesNotTrigger(string collection) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.False({0}.Contains(1), "Custom message"); }} }} """, collection); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Enumerables))] public async Task TrueLinqContainsCheckWithAssertionMessage_DoesNotTrigger(string enumerable) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ Xunit.Assert.True({0}.Contains(1), "Custom message"); }} }} """, enumerable); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Enumerables))] public async Task FalseLinqContainsCheckWithAssertionMessage_DoesNotTrigger(string enumerable) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ Xunit.Assert.False({0}.Contains(1), "Custom message"); }} }} """, enumerable); await Verify.VerifyAnalyzer(source); } [Fact] public async Task CollectionWithDifferentTypeParametersThanICollectionImplementation_ZeroParameters_Triggers() { var source = /* lang=c#-test */ """ using System.Collections.Generic; class IntList : List { } class TestClass { void TestMethod() { [|Xunit.Assert.False(new IntList().Contains(1))|]; } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task CollectionWithDifferentTypeParametersThanICollectionImplementation_TwoParameters_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Collections.Generic; class TestClass { void TestMethod() { Xunit.Assert.False(new Dictionary().ContainsKey(1)); } } """; await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEmptyCollectionCheckShouldNotBeUsedTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class AssertEmptyCollectionCheckShouldNotBeUsedTests { public static TheoryData Collections = [ "new int[0]", "new System.Collections.Generic.List()", "new System.Collections.Generic.HashSet()", "new System.Collections.ObjectModel.Collection()", "System.Linq.Enumerable.Empty()", #if NETCOREAPP3_0_OR_GREATER "default(System.Collections.Generic.IAsyncEnumerable)", #endif ]; [Theory] [MemberData(nameof(Collections))] public async Task CollectionCheckWithoutAction_Triggers(string collection) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ [|Xunit.Assert.Collection({0})|]; [|Xunit.Assert.CollectionAsync({0})|]; }} }} """, collection); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Collections))] public async Task CollectionCheckWithAction_DoesNotTrigger(string collection) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.Collection({0}, i => Xunit.Assert.True(true)); Xunit.Assert.CollectionAsync({0}, async i => {{ await System.Threading.Tasks.Task.Yield(); Xunit.Assert.True(true); }}); }} }} """, collection); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksTests { public static TheoryData GetEnumerables( string typeName, string comparison) => new() { { $"new System.Collections.Generic.List<{typeName}>()", comparison }, { $"new System.Collections.Generic.HashSet<{typeName}>()", comparison }, { $"new System.Collections.ObjectModel.Collection<{typeName}>()", comparison }, { $"new {typeName}[0]", comparison }, { $"System.Linq.Enumerable.Empty<{typeName}>()", comparison }, }; [Theory] [MemberData(nameof(GetEnumerables), "int", "")] [MemberData(nameof(GetEnumerables), "string", "")] public async Task Containers_WithoutWhereClause_DoesNotTrigger( string collection, string _) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.Empty({0}); Xunit.Assert.NotEmpty({0}); }} }} """, collection); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(GetEnumerables), "int", "f > 0")] [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] public async Task Containers_WithWhereClauseWithIndex_DoesNotTrigger( string collection, string comparison) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ Xunit.Assert.Empty({0}.Where((f, i) => {1} && i > 0)); Xunit.Assert.NotEmpty({0}.Where((f, i) => {1} && i > 0)); }} }} """, collection, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(GetEnumerables), "int", "f > 0")] [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] public async Task EnumurableEmptyCheck_WithChainedLinq_DoesNotTrigger( string collection, string comparison) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ Xunit.Assert.Empty({0}.Where(f => {1}).Select(f => f)); Xunit.Assert.NotEmpty({0}.Where(f => {1}).Select(f => f)); }} }} """, collection, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(GetEnumerables), "int", "f > 0")] [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] public async Task Containers_WithWhereClause_Triggers( string collection, string comparison) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ {{|xUnit2029:Xunit.Assert.Empty({0}.Where(f => {1}))|}}; {{|xUnit2030:Xunit.Assert.NotEmpty({0}.Where(f => {1}))|}}; }} }} """, collection, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("")] [InlineData("123")] [InlineData(@"abc\n\t\\\""")] public async Task Strings_WithWhereClause_Triggers(string sampleString) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ {{|xUnit2029:Xunit.Assert.Empty("{0}".Where(f => f > 0))|}}; {{|xUnit2030:Xunit.Assert.NotEmpty("{0}".Where(f => f > 0))|}}; }} }} """, sampleString); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckTests { public static TheoryData Methods = [ Constants.Asserts.True, Constants.Asserts.False, ]; [Theory] [MemberData(nameof(Methods))] public async Task ForLinqAnyCheck_Triggers(string method) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ [|Xunit.Assert.{0}(new [] {{ 1 }}.Any(i => true))|]; }} }} """, method); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEqualGenericShouldNotBeUsedForStringValueTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class AssertEqualGenericShouldNotBeUsedForStringValueTests { public static TheoryData Data = new() { { "true.ToString()", "\"True\"" }, { "1.ToString()", "\"1\"" }, { "\"\"", "null" }, { "null", "\"\"" }, { "\"\"", "\"\"" }, { "\"abc\"", "\"abc\"" }, { "\"TestMethod\"", "nameof(TestMethod)" }, }; [Theory] [MemberData(nameof(Data))] public async Task StringEqualityCheckWithoutGenericType_DoesNotTrigger( string expected, string value) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.Equal({0}, {1}); }} }} """, expected, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Data))] public async Task StringEqualityCheckWithGenericType_Triggers( string expected, string value) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ [|Xunit.Assert.Equal({0}, {1})|]; }} }} """, expected, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Data))] public async Task StrictStringEqualityCheck_Triggers( string expected, string value) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ [|Xunit.Assert.StrictEqual({0}, {1})|]; }} }} """, expected, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Data))] public async Task StrictStringEqualityCheckWithGenericType_Triggers( string expected, string value) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ [|Xunit.Assert.StrictEqual({0}, {1})|]; }} }} """, expected, value); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEqualLiteralValueShouldBeFirstTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class AssertEqualLiteralValueShouldBeFirstTests { [Fact] public async Task WhenConstantOrLiteralUsedForBothArguments_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { Xunit.Assert.Equal("TestMethod", nameof(TestMethod)); } } """; await Verify.VerifyAnalyzer(source); } public static TheoryData TypesAndValues = new() { { "int", "0" }, { "int", "0.0" }, { "int", "sizeof(int)" }, { "int", "default(int)" }, { "string", "null" }, { "string", "\"\"" }, { "string", "nameof(TestMethod)" }, { "System.Type", "typeof(string)" }, { "System.AttributeTargets", "System.AttributeTargets.Constructor" }, }; [Theory] [MemberData(nameof(TypesAndValues))] public async Task ExpectedConstantOrLiteralValueAsFirstArgument_DoesNotTrigger( string type, string value) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ var v = default({0}); Xunit.Assert.Equal({1}, v); }} }} """, type, value); await Verify.VerifyAnalyzer(source); } [Fact] public async Task ConstantsUsedInStringConstructorAsFirstArgument_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { Xunit.Assert.Equal(new string(' ', 4), " "); } } """; await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(TypesAndValues))] public async Task ExpectedConstantOrLiteralValueAsSecondArgument_Triggers( string type, string value) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ var v = default({0}); {{|#0:Xunit.Assert.Equal(v, {1})|}}; }} }} """, type, value); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(value, "Assert.Equal(expected, actual)", "TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(true)] [InlineData(false)] public async Task ExpectedConstantOrLiteralValueAsNamedExpectedArgument_DoesNotTrigger(bool useAlternateForm) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ var v = default(int); Xunit.Assert.Equal({0}actual: v, {0}expected: 0); }} }} """, useAlternateForm ? "@" : ""); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(TypesAndValues))] public async Task ExpectedConstantOrLiteralValueAsNamedExpectedArgument_Triggers( string type, string value) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ var v = default({0}); {{|#0:Xunit.Assert.Equal(actual: {1}, expected: v)|}}; }} }} """, type, value); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(value, "Assert.Equal(expected, actual)", "TestMethod", "TestClass"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData("Equal", "{|CS1739:act|}", "exp")] [InlineData("{|CS1501:Equal|}", "expected", "expected")] [InlineData("{|CS1501:Equal|}", "actual", "actual")] [InlineData("Equal", "{|CS1739:foo|}", "bar")] public async Task DoesNotFindWarningWhenArgumentsAreNotNamedCorrectly( string methodName, string firstArgumentName, string secondArgumentName) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ var v = default(int); Xunit.Assert.{0}({1}: 1, {2}: v); }} }} """, methodName, firstArgumentName, secondArgumentName); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEqualPrecisionShouldBeInRangeTest.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class AssertEqualPrecisionShouldBeInRangeTest { static readonly string Template = /* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {0} }} }} """; [Theory] [InlineData(0)] [InlineData(1)] [InlineData(8)] [InlineData(14)] [InlineData(15)] public async Task DoesNotFindError_ForDoubleArgumentWithPrecisionProvidedInRange(int precision) { var source = string.Format( Template, $"double num = 0.133d; Xunit.Assert.Equal(0.13d, num, {precision});" ); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData(int.MinValue)] [InlineData(-2000)] [InlineData(-1)] [InlineData(16)] [InlineData(17000)] [InlineData(int.MaxValue)] public async Task FindsError_ForDoubleArgumentWithPrecisionProvidedOutOfRange(int precision) { var source = string.Format( Template, $"double num = 0.133d; Xunit.Assert.Equal(0.13d, num, {{|#0:{precision}|}});" ); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("[0..15]", "double"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(14)] [InlineData(27)] [InlineData(28)] public async Task DoesNotFindError_ForDecimalArgumentWithPrecisionProvidedInRange(int precision) { var source = string.Format( Template, $"decimal num = 0.133m; Xunit.Assert.Equal(0.13m, num, {precision});" ); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData(int.MinValue)] [InlineData(-2000)] [InlineData(-1)] [InlineData(29)] [InlineData(30000)] [InlineData(int.MaxValue)] public async Task FindsError_ForDecimalArgumentWithPrecisionProvidedOutOfRange(int precision) { var source = string.Format( Template, $"decimal num = 0.133m; Xunit.Assert.Equal(0.13m, num, {{|#0:{precision}|}});" ); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("[0..28]", "decimal"); await Verify.VerifyAnalyzer(source, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEqualShouldNotBeUsedForBoolLiteralCheckTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertEqualShouldNotBeUsedForBoolLiteralCheckTests { public static TheoryData Methods = [ Constants.Asserts.Equal, Constants.Asserts.NotEqual, Constants.Asserts.StrictEqual, Constants.Asserts.NotStrictEqual, ]; [Theory] [InlineData(Constants.Asserts.Equal, Constants.Asserts.True)] [InlineData(Constants.Asserts.StrictEqual, Constants.Asserts.True)] [InlineData(Constants.Asserts.NotEqual, Constants.Asserts.False)] [InlineData(Constants.Asserts.NotStrictEqual, Constants.Asserts.False)] public async Task ForFirstBoolLiteral_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ bool val = true; {{|#0:Xunit.Assert.{0}(true, val)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(Constants.Asserts.Equal, Constants.Asserts.False)] [InlineData(Constants.Asserts.NotEqual, Constants.Asserts.True)] public async Task ForFirstBoolLiteral_WithCustomComparer_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ bool val = false; {{|#0:Xunit.Assert.{0}(false, val, System.Collections.Generic.EqualityComparer.Default)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods))] public async Task ForFirstBoolLiteral_ObjectOverload_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ object val = false; Xunit.Assert.{0}(true, val); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForOtherLiteral_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ int val = 1; Xunit.Assert.{0}(1, val); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForSecondBoolLiteral_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ bool val = false; Xunit.Assert.{0}(val, true); }} }} """, method); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEqualShouldNotBeUsedForCollectionSizeCheckTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertEqualShouldNotBeUsedForCollectionSizeCheckTests { public static TheoryData AllowedCollections = [ "new System.ArraySegment().Count", "Microsoft.Extensions.Primitives.StringValues.Empty.Count", ]; public static TheoryData DisallowedCollections = [ "new int[0].Length", "new System.Collections.ArrayList().Count", "new System.Collections.Generic.List().Count", "new System.Collections.Generic.HashSet().Count", "System.Collections.Immutable.ImmutableArray.Create().Length", "new System.Collections.ObjectModel.Collection().Count", "new System.Collections.Generic.List().AsReadOnly().Count", "System.Linq.Enumerable.Empty().Count()", ]; public static TheoryData DisallowedCollectionInterfaces = [ "ICollection", "ICollection", "IReadOnlyCollection", ]; [Theory] [MemberData(nameof(AllowedCollections))] public async Task AllowedCollection_DoesNotTrigger(string collection) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ Xunit.Assert.Equal(0, {0}); Xunit.Assert.Equal(1, {0}); Xunit.Assert.NotEqual(0, {0}); }} }} """, collection); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(AllowedCollections))] [MemberData(nameof(DisallowedCollections))] public async Task AllowedCheck_DoesNotTrigger(string collection) { // Anything that's non-zero for Equal/NotEqual and non-one for Equal is allowed var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ Xunit.Assert.Equal(2, {0}); Xunit.Assert.NotEqual(1, {0}); Xunit.Assert.NotEqual(2, {0}); }} }} """, collection); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(DisallowedCollections))] public async Task InvalidCheckWithConcreteType_Triggers(string collection) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.Equal(0, {0})|}}; {{|#1:Xunit.Assert.Equal(1, {0})|}}; {{|#2:Xunit.Assert.NotEqual(0, {0})|}}; }} }} """, collection); var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("Assert.Equal()", Constants.Asserts.Empty), Verify.Diagnostic().WithLocation(1).WithArguments("Assert.Equal()", Constants.Asserts.Single), Verify.Diagnostic().WithLocation(2).WithArguments("Assert.NotEqual()", Constants.Asserts.NotEmpty), }; await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(DisallowedCollectionInterfaces))] public async Task InvalidCheckWithInterfaceType_Triggers(string @interface) { var source = string.Format(/* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; class TestClass {{ void TestMethod() {{ {0} collection = null; {{|#0:Xunit.Assert.Equal(0, collection.Count)|}}; {{|#1:Xunit.Assert.Equal(1, collection.Count)|}}; {{|#2:Xunit.Assert.NotEqual(0, collection.Count)|}}; }} }} """, @interface); var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("Assert.Equal()", Constants.Asserts.Empty), Verify.Diagnostic().WithLocation(1).WithArguments("Assert.Equal()", Constants.Asserts.Single), Verify.Diagnostic().WithLocation(2).WithArguments("Assert.NotEqual()", Constants.Asserts.NotEmpty), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task InvalidCheckWithCustomNonGenericCollection_Triggers() { var source = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; class IntCollection : ICollection { public int Count { get { throw null; } } public bool IsReadOnly { get { throw null; } } public void Add(int item) { throw null; } public void Clear() { throw null; } public bool Contains(int item) { throw null; } public void CopyTo(int[] array, int arrayIndex) { throw null; } public IEnumerator GetEnumerator() { throw null; } public bool Remove(int item) { throw null; } IEnumerator IEnumerable.GetEnumerator() { throw null; } } class TestClass { void TestMethod() { {|#0:Assert.Equal(0, new IntCollection().Count)|}; {|#1:Assert.Equal(1, new IntCollection().Count)|}; {|#2:Assert.NotEqual(0, new IntCollection().Count)|}; } } """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("Assert.Equal()", Constants.Asserts.Empty), Verify.Diagnostic().WithLocation(1).WithArguments("Assert.Equal()", Constants.Asserts.Single), Verify.Diagnostic().WithLocation(2).WithArguments("Assert.NotEqual()", Constants.Asserts.NotEmpty), }; await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task OverridingCountMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ using System.Collections.Generic; interface IIntCollection : ICollection { new int Count { get; } } interface ICustomCollection : ICollection { new int Count { get; } } interface ICustomDictionary : ICollection> { new int Count { get; } } class TestClass { void TestMethod() { Xunit.Assert.Equal(1, ((IIntCollection)null).Count); Xunit.Assert.Equal(1, ((ICustomCollection)null).Count); Xunit.Assert.Equal(1, ((ICustomDictionary)null).Count); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForNonIntArguments_DoesNotCrash() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { Xunit.Assert.Equal('b', new int[0].Length); } } """; await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEqualShouldNotBeUsedForNullCheckTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertEqualShouldNotBeUsedForNullCheckTests { public static TheoryData Methods_All = [ Constants.Asserts.Equal, Constants.Asserts.NotEqual, Constants.Asserts.StrictEqual, Constants.Asserts.NotStrictEqual, Constants.Asserts.Same, Constants.Asserts.NotSame, ]; public static TheoryData Methods_Equal_WithReplacement = new() { { Constants.Asserts.Equal, Constants.Asserts.Null }, { Constants.Asserts.NotEqual, Constants.Asserts.NotNull } }; [Theory] [MemberData(nameof(Methods_Equal_WithReplacement))] public async Task ForFirstNullLiteral_StringOverload_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ string val = null; {{|#0:Xunit.Assert.{0}(null, val)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_Equal_WithReplacement))] public async Task ForFirstNullLiteral_StringOverload_WithCustomComparer_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ string val = null; {{|#0:Xunit.Assert.{0}(null, val, System.StringComparer.Ordinal)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(Constants.Asserts.Equal, Constants.Asserts.Null)] [InlineData(Constants.Asserts.StrictEqual, Constants.Asserts.Null)] [InlineData(Constants.Asserts.Same, Constants.Asserts.Null)] [InlineData(Constants.Asserts.NotEqual, Constants.Asserts.NotNull)] [InlineData(Constants.Asserts.NotStrictEqual, Constants.Asserts.NotNull)] [InlineData(Constants.Asserts.NotSame, Constants.Asserts.NotNull)] public async Task ForFirstNullLiteral_ObjectOverload_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ object val = null; {{|#0:Xunit.Assert.{0}(null, val)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_Equal_WithReplacement))] public async Task ForFirstNullLiteral_ObjectOverload_WithCustomComparer_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ object val = null; {{|#0:Xunit.Assert.{0}(null, val, System.Collections.Generic.EqualityComparer.Default)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(Constants.Asserts.Equal, Constants.Asserts.Null)] [InlineData(Constants.Asserts.NotEqual, Constants.Asserts.NotNull)] [InlineData(Constants.Asserts.StrictEqual, Constants.Asserts.Null)] [InlineData(Constants.Asserts.NotStrictEqual, Constants.Asserts.NotNull)] public async Task ForFirstNullLiteral_GenericOverload_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ TestClass val = null; {{|#0:Xunit.Assert.{0}(null, val)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_Equal_WithReplacement))] public async Task ForFirstNullLiteral_GenericOverload_WithCustomComparer_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ TestClass val = null; {{|#0:Xunit.Assert.{0}(null, val, System.Collections.Generic.EqualityComparer.Default)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_All))] public async Task ForOtherLiteral_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ int val = 1; Xunit.Assert.{0}(1, val); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods_All))] public async Task ForSecondNullLiteral_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ string val = null; Xunit.Assert.{0}(val, null); }} }} """, method); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertEqualsShouldNotBeUsedTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertEqualsShouldNotBeUsedTests { [Theory] [InlineData(nameof(object.Equals), Constants.Asserts.Equal)] [InlineData(nameof(object.ReferenceEquals), Constants.Asserts.Same)] public async Task WhenProhibitedMethodIsUsed_Triggers( string method, string replacement) { var source = $@" class TestClass {{ void TestMethod() {{ {{|#0:{{|CS0619:Xunit.Assert.{method}(null, null)|}}|}}; }} }}"; var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertIsTypeShouldNotBeUsedForAbstractTypeTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v2_Pre2_9_3 = CSharpVerifier; using Verify_v3_Pre0_6_0 = CSharpVerifier; public class AssertIsTypeShouldNotBeUsedForAbstractTypeTests { public static TheoryData Methods = ["IsType", "IsNotType"]; public static TheoryData MethodsWithReplacements = new() { { "IsType", "Assert.IsAssignableFrom", false }, { "IsType", "exactMatch: false", true }, { "IsNotType", "Assert.IsNotAssignableFrom", false }, { "IsNotType", "exactMatch: false", true }, }; [Theory] [MemberData(nameof(MethodsWithReplacements))] public async Task Interface_Triggers( string method, string replacement, bool supportsInexactTypeAssertions) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; class TestClass {{ void TestMethod() {{ {{|#0:Assert.{0}(new object())|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("interface", "System.IDisposable", replacement); if (supportsInexactTypeAssertions) await Verify.VerifyAnalyzer(source, expected); else { await Verify_v2_Pre2_9_3.VerifyAnalyzer(source, expected); await Verify_v3_Pre0_6_0.VerifyAnalyzer(source, expected); } } [Theory] [MemberData(nameof(Methods))] public async Task Interface_WithExactMatchFlag_TriggersForTrue(string method) { // We can only trigger when we know the literal true is being used; anything else, // we let the runtime figure it out. var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; class TestClass {{ void TestMethod() {{ var flag = true; {{|#0:Assert.{0}(new object(), true)|}}; {{|#1:Assert.{0}(new object(), exactMatch: true)|}}; Assert.{0}(new object(), flag); }} }} """, method); var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("interface", "System.IDisposable", "exactMatch: false"), Verify.Diagnostic().WithLocation(1).WithArguments("interface", "System.IDisposable", "exactMatch: false"), }; await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(MethodsWithReplacements))] public async Task AbstractClass_Triggers( string method, string replacement, bool supportsInexactTypeAssertions) { var source = string.Format(/* lang=c#-test */ """ using System.IO; using Xunit; class TestClass {{ void TestMethod() {{ {{|#0:Assert.{0}(new object())|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("abstract class", "System.IO.Stream", replacement); if (supportsInexactTypeAssertions) await Verify.VerifyAnalyzer(source, expected); else { await Verify_v2_Pre2_9_3.VerifyAnalyzer(source, expected); await Verify_v3_Pre0_6_0.VerifyAnalyzer(source, expected); } } [Theory] [MemberData(nameof(Methods))] public async Task AbstractClass_WithExactMatchFlag_TriggersForTrue(string method) { // We can only trigger when we know the literal true is being used; anything else, // we let the runtime figure it out. var source = string.Format(/* lang=c#-test */ """ using System.IO; using Xunit; class TestClass {{ void TestMethod() {{ var flag = true; {{|#0:Assert.{0}(new object(), true)|}}; {{|#1:Assert.{0}(new object(), exactMatch: true)|}}; Assert.{0}(new object(), flag); }} }} """, method); var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("abstract class", "System.IO.Stream", "exactMatch: false"), Verify.Diagnostic().WithLocation(1).WithArguments("abstract class", "System.IO.Stream", "exactMatch: false"), }; await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(MethodsWithReplacements))] public async Task UsingStatic_Triggers( string method, string replacement, bool supportsInexactTypeAssertions) { var source = string.Format(/* lang=c#-test */ """ using System; using static Xunit.Assert; class TestClass {{ void TestMethod() {{ {{|#0:{0}(new object())|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("interface", "System.IDisposable", replacement); if (supportsInexactTypeAssertions) await Verify.VerifyAnalyzer(source, expected); else { await Verify_v2_Pre2_9_3.VerifyAnalyzer(source, expected); await Verify_v3_Pre0_6_0.VerifyAnalyzer(source, expected); } } [Theory] [MemberData(nameof(Methods))] public async Task NonAbstractClass_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ using Xunit; class TestClass {{ void TestMethod() {{ var flag = true; Assert.{0}(new object()); Assert.{0}(new object(), flag); Assert.{0}(new object(), exactMatch: flag); Assert.{0}(new object(), true); Assert.{0}(new object(), exactMatch: true); Assert.{0}(new object(), false); Assert.{0}(new object(), exactMatch: false); }} }} """, method); await Verify.VerifyAnalyzer(source); } internal class Analyzer_v2_Pre2_9_3 : AssertIsTypeShouldNotBeUsedForAbstractType { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2(compilation, new Version(2, 9, 2)); } internal class Analyzer_v3_Pre0_6_0 : AssertIsTypeShouldNotBeUsedForAbstractType { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV3(compilation, new Version(0, 5, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertIsTypeShouldUseGenericOverloadTypeTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; #if NETCOREAPP using Microsoft.CodeAnalysis.CSharp; #endif public class AssertIsTypeShouldUseGenericOverloadTypeTests { public static TheoryData Methods = [ "IsType", "IsNotType", "IsAssignableFrom", ]; [Theory] [MemberData(nameof(Methods))] public async Task ForNonGenericCall_Triggers(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}(typeof(int), 1)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("int"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods))] public async Task ForGenericCall_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}(1); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("IsType")] [InlineData("IsNotType")] public async Task ForGenericCall_WithExactMatchFlag_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}(1, false); Xunit.Assert.{0}(typeof(int), true); }} }} """, method); await Verify.VerifyAnalyzer(source); } #if NETCOREAPP public class StaticAbstractInterfaceMethods { #if ROSLYN_LATEST // C# 11 is required for static abstract methods const string methodCode = /* lang=c#-test */ "static abstract void Method();"; const string codeTemplate = /* lang=c#-test */ """ using Xunit; public interface IParentClass {{ {0} }} public interface IClass : IParentClass {{ {1} }} public class Class : IClass {{ public static void Method() {{ }} }} public abstract class TestClass {{ [Fact] public void TestMethod() {{ var data = new Class(); Assert.IsAssignableFrom(typeof(IClass), data); }} }} """; [Fact] public async Task ForStaticAbstractInterfaceMembers_DoesNotTrigger() { string source = string.Format(codeTemplate, string.Empty, methodCode); await Verify.VerifyAnalyzer(LanguageVersion.CSharp11, source); } [Fact] public async Task ForNestedStaticAbstractInterfaceMembers_DoesNotTrigger() { string source = string.Format(codeTemplate, methodCode, string.Empty); await Verify.VerifyAnalyzer(LanguageVersion.CSharp11, source); } #endif [Theory] [InlineData("static", "", "{ }")] [InlineData("", "abstract", ";")] public async Task ForNotStaticAbstractInterfaceMembers_Triggers( string staticModifier, string abstractModifier, string methodBody) { string source = string.Format(/* lang=c#-test */ """ using Xunit; public interface IClass {{ {0} {1} void Method() {2} }} public class Class : IClass {{ public {0} void Method() {{ }} }} public abstract class TestClass {{ [Fact] public void TestMethod() {{ var data = new Class(); {{|#0:Assert.IsAssignableFrom(typeof(IClass), data)|}}; }} }} """, staticModifier, abstractModifier, methodBody); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("IClass"); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source, expected); } } #endif } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertNullShouldNotBeCalledOnValueTypesTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class AssertNullShouldNotBeCalledOnValueTypesTests { public static TheoryData Methods = [ "Null", "NotNull", ]; [Theory] [MemberData(nameof(Methods))] public async Task ForValueType_Triggers(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ int val = 1; {{|#0:Xunit.Assert.{0}(val)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", "int"); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods))] public async Task ForNullableValueType_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ int? val = 1; Xunit.Assert.{0}(val); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForNullableReferenceType_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ string val = null; Xunit.Assert.{0}(val); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForPointerType_v3_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; class TestClass { unsafe void TestMethod() { var value = 42; var ptr = &value; Assert.Null(ptr); Assert.NotNull(ptr); } } """; await Verify.VerifyAnalyzerV3(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForClassConstrainedGenericTypes_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class Class where T : class {{ public void Method(T arg) {{ Xunit.Assert.{0}(arg); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForInterfaceConstrainedGenericTypes_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ interface IDo {{ }} class Class where T : IDo {{ public void Method(System.Collections.Generic.IEnumerable collection) {{ foreach (T item in collection) {{ Xunit.Assert.{0}(item); }} }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForUnconstrainedGenericTypes_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class Class {{ public void Method(System.Collections.Generic.IEnumerable collection) {{ foreach (T item in collection) {{ Xunit.Assert.{0}(item); }} }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] // https://github.com/xunit/xunit/issues/2395 public async Task ForUserDefinedImplicitConversion_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ public void TestMethod() {{ Xunit.Assert.{0}((MyBuggyInt)42); Xunit.Assert.{0}((MyBuggyInt)(int?)42); Xunit.Assert.{0}((MyBuggyIntBase)42); Xunit.Assert.{0}((MyBuggyIntBase)(int?)42); }} }} public abstract class MyBuggyIntBase {{ public static implicit operator MyBuggyIntBase(int i) => new MyBuggyInt(); }} public class MyBuggyInt : MyBuggyIntBase {{ public MyBuggyInt() {{ }} }} """, method); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertRegexMatchShouldNotUseBoolLiteralCheckTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertRegexMatchShouldNotUseBoolLiteralCheckTests { public static TheoryData Methods_WithReplacement = new() { { Constants.Asserts.True, Constants.Asserts.Matches }, { Constants.Asserts.False, Constants.Asserts.DoesNotMatch }, }; [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task ForStaticRegexIsMatch_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}(System.Text.RegularExpressions.Regex.IsMatch("abc", "\\w*"))|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task ForInstanceRegexIsMatchWithInlineConstructedRegex_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}(new System.Text.RegularExpressions.Regex("abc").IsMatch("\\w*"))|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task ForInstanceRegexIsMatchWithConstructedRegexVariable_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ var regex = new System.Text.RegularExpressions.Regex("abc"); {{|#0:Xunit.Assert.{0}(regex.IsMatch("\\w*"))|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertSameShouldNotBeCalledOnValueTypesTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertSameShouldNotBeCalledOnValueTypesTests { public static TheoryData Methods_WithReplacement = new() { { Constants.Asserts.Same, Constants.Asserts.Equal }, { Constants.Asserts.NotSame, Constants.Asserts.NotEqual }, }; [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task TwoValueParameters_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ int a = 0; {{|#0:Xunit.Assert.{0}(0, a)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", "int", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task FirstValueParameters_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ object a = 0; {{|#0:Xunit.Assert.{0}(0, a)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", "int", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task SecondValueParameters_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ object a = 0; {{|#0:Xunit.Assert.{0}(a, 0)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", "int", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_WithReplacement))] // https://github.com/xunit/xunit/issues/2395 public async Task UserDefinedImplicitConversion_DoesNotTrigger( string method, string _) { var source = string.Format(/* lang=c#-test */ """ public class TestClass {{ public void TestMethod() {{ var o = new object(); Xunit.Assert.{0}((MyBuggyInt)42, o); Xunit.Assert.{0}((MyBuggyInt)(int?)42, o); Xunit.Assert.{0}((MyBuggyIntBase)42, o); Xunit.Assert.{0}((MyBuggyIntBase)(int?)42, o); Xunit.Assert.{0}(o, (MyBuggyInt)42); Xunit.Assert.{0}(o, (MyBuggyInt)(int?)42); Xunit.Assert.{0}(o, (MyBuggyIntBase)42); Xunit.Assert.{0}(o, (MyBuggyIntBase)(int?)42); }} }} public abstract class MyBuggyIntBase {{ public static implicit operator MyBuggyIntBase(int i) => new MyBuggyInt(); }} public class MyBuggyInt : MyBuggyIntBase {{ public MyBuggyInt() {{ }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task FirstValueParametersIfSecondIsNull_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}(0, null)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", "int", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task SecondValueParametersIfFirstIsNull_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}(null, 0)|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", "int", replacement); await Verify.VerifyAnalyzer(source, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertSingleShouldBeUsedForSingleParameterTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using Xunit; using Verify = CSharpVerifier; public class AssertSingleShouldBeUsedForSingleParameterTests { [Fact] public async ValueTask EnumerableAcceptanceTest() { var code = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { {|#0:Assert.Collection( default(IEnumerable), item => Assert.NotNull(item) )|}; Assert.Collection( default(IEnumerable), item => Assert.NotNull(item), item => Assert.NotNull(item) ); await {|#1:Assert.CollectionAsync( default(IEnumerable>), async item => Assert.NotNull(item) )|}; await Assert.CollectionAsync( default(IEnumerable>), async item => Assert.Equal(42, await item), async item => Assert.Equal(2112, await item) ); } } """; var expected = new DiagnosticResult[] { Verify.Diagnostic().WithLocation(0).WithArguments("Collection"), Verify.Diagnostic().WithLocation(1).WithArguments("CollectionAsync"), }; await Verify.VerifyAnalyzer(code, expected); } #if NETCOREAPP3_0_OR_GREATER [Fact] public async ValueTask AsyncEnumerableAcceptanceTest() { var code = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { {|#0:Assert.Collection( default(IAsyncEnumerable), item => Assert.NotNull(item) )|}; Assert.Collection( default(IAsyncEnumerable), item => Assert.NotNull(item), item => Assert.NotNull(item) ); await {|#1:Assert.CollectionAsync( default(IAsyncEnumerable>), async item => Assert.NotNull(item) )|}; await Assert.CollectionAsync( default(IAsyncEnumerable>), async item => Assert.Equal(42, await item), async item => Assert.Equal(2112, await item) ); } } """; var expected = new DiagnosticResult[] { Verify.Diagnostic().WithLocation(0).WithArguments("Collection"), Verify.Diagnostic().WithLocation(1).WithArguments("CollectionAsync"), }; await Verify.VerifyAnalyzer(code, expected); } #endif // NETCOREAPP3_0_OR_GREATER } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertSingleShouldUseTwoArgumentCallTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class AssertSingleShouldUseTwoArgumentCallTests { public static TheoryData GetEnumerables( string typeName, string comparison) => AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksTests.GetEnumerables(typeName, comparison); [Theory] [MemberData(nameof(GetEnumerables), "int", "")] [MemberData(nameof(GetEnumerables), "string", "")] public async Task Containers_WithoutWhereClause_DoesNotTrigger( string collection, string _) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.Single({0}); }} }} """, collection); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(GetEnumerables), "int", "f > 0")] [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] public async Task Containers_WithWhereClauseWithIndex_DoesNotTrigger( string collection, string comparison) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ Xunit.Assert.Single({0}.Where((f, i) => {1} && i > 0)); }} }} """, collection, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(GetEnumerables), "int", "f > 0")] [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] public async Task EnumurableEmptyCheck_WithChainedLinq_DoesNotTrigger( string collection, string comparison) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ Xunit.Assert.Single({0}.Where(f => {1}).Select(f => f)); }} }} """, collection, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(GetEnumerables), "int", "f > 0")] [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] public async Task Containers_WithWhereClause_Triggers( string collection, string comparison) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ [|Xunit.Assert.Single({0}.Where(f => {1}))|]; }} }} """, collection, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [InlineData("")] [InlineData("123")] [InlineData(@"abc\n\t\\\""")] public async Task Strings_WithWhereClause_Triggers(string sampleString) { var source = string.Format(/* lang=c#-test */ """ using System.Linq; class TestClass {{ void TestMethod() {{ [|Xunit.Assert.Single("{0}".Where(f => f > 0))|]; }} }} """, sampleString); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertStringEqualityCheckShouldNotUseBoolCheckTest.cs ================================================ using System; using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertStringEqualityCheckShouldNotUseBoolCheckTest { public static TheoryData Methods_WithReplacement = new() { { Constants.Asserts.True, Constants.Asserts.Equal }, { Constants.Asserts.False, Constants.Asserts.NotEqual }, }; public static TheoryData SupportedStringComparisons = [ StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase, ]; public static TheoryData UnsupportedStringComparisons = [ StringComparison.CurrentCulture, StringComparison.CurrentCultureIgnoreCase, StringComparison.InvariantCulture, StringComparison.InvariantCultureIgnoreCase, ]; public static TheoryData AllStringComparisons = [ StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase, StringComparison.CurrentCulture, StringComparison.CurrentCultureIgnoreCase, StringComparison.InvariantCulture, StringComparison.InvariantCultureIgnoreCase, ]; [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task ForInstanceEqualsCheck_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}("abc".Equals("a"))|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(SupportedStringComparisons))] public async Task ForTrueInstanceEqualsCheck_WithSupportedStringComparison_Triggers(StringComparison comparison) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.True("abc".Equals("a", System.StringComparison.{0}))|}}; }} }} """, comparison); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.Equal); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(UnsupportedStringComparisons))] public async Task ForTrueInstanceEqualsCheck_WithUnsupportedStringComparison_DoesNotTrigger(StringComparison comparison) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.True("abc".Equals("a", System.StringComparison.{0})); }} }} """, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(AllStringComparisons))] public async Task ForFalseInstanceEqualsCheck_WithStringComparison_DoesNotTrigger(StringComparison comparison) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.False("abc".Equals("a", System.StringComparison.{0})); }} }} """, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods_WithReplacement))] public async Task ForStaticEqualsCheck_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}(System.String.Equals("abc", "a"))|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(SupportedStringComparisons))] public async Task ForTrueStaticEqualsCheck_WithSupportedStringComparison_Triggers(StringComparison comparison) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.True(System.String.Equals("abc", "a", System.StringComparison.{0}))|}}; }} }} """, comparison); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.Equal); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(UnsupportedStringComparisons))] public async Task ForTrueStaticEqualsCheck_WithUnsupportedStringComparison_DoesNotTrigger(StringComparison comparison) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.True(System.String.Equals("abc", "a", System.StringComparison.{0})); }} }} """, comparison); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(AllStringComparisons))] public async Task ForFalseStaticEqualsCheck_WithStringComparison_DoesNotTrigger(StringComparison comparison) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.False(System.String.Equals("abc", "a", System.StringComparison.{0})); }} }} """, comparison); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertSubstringCheckShouldNotUseBoolCheckTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertSubstringCheckShouldNotUseBoolCheckTests { public static TheoryData Methods = [ Constants.Asserts.True, Constants.Asserts.False, ]; [Theory] [InlineData(Constants.Asserts.True, Constants.Asserts.Contains)] [InlineData(Constants.Asserts.False, Constants.Asserts.DoesNotContain)] public async Task ForBooleanContainsCheck_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}("abc".Contains("a"))|}}; }} }} """, method); var expected = Verify.Diagnostic().WithLocation(0).WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanContainsCheck_WithUserMessage_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".Contains("a"), "message"); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForBooleanTrueStartsWithCheck_Triggers() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { {|#0:Xunit.Assert.True("abc".StartsWith("a"))|}; } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.StartsWith); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ForBooleanTrueStartsWithCheck_WithStringComparison_Triggers() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { {|#0:Xunit.Assert.True("abc".StartsWith("a", System.StringComparison.CurrentCulture))|}; } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.StartsWith); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ForBooleanFalseStartsWithCheck_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { Xunit.Assert.False("abc".StartsWith("a")); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForBooleanFalseStartsWithCheck_WithStringComparison_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { Xunit.Assert.False("abc".StartsWith("a", System.StringComparison.CurrentCulture)); } } """; await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanStartsWithCheck_WithUserMessage_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".StartsWith("a"), "message"); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanStartsWithCheck_WithStringComparison_AndUserMessage_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".StartsWith("a", System.StringComparison.CurrentCulture), "message"); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanStartsWithCheck_WithBoolAndCulture_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".StartsWith("a", true, System.Globalization.CultureInfo.CurrentCulture)); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanStartsWithCheck_WithBoolAndCulture_AndUserMessage_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".StartsWith("a", true, System.Globalization.CultureInfo.CurrentCulture), "message"); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForBooleanTrueEndsWithCheck_Triggers() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { {|#0:Xunit.Assert.True("abc".EndsWith("a"))|}; } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.EndsWith); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ForBooleanTrueEndsWithCheck_WithStringComparison_Triggers() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { {|#0:Xunit.Assert.True("abc".EndsWith("a", System.StringComparison.CurrentCulture))|}; } } """; var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.True()", Constants.Asserts.EndsWith); await Verify.VerifyAnalyzer(source, expected); } [Fact] public async Task ForBooleanFalseEndsWithCheck_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { Xunit.Assert.False("abc".EndsWith("a")); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForBooleanFalseEndsWithCheck_WithStringComparison_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { Xunit.Assert.False("abc".EndsWith("a", System.StringComparison.CurrentCulture)); } } """; await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanEndsWithCheck_WithUserMessage_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".EndsWith("a"), "message"); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanEndsWithCheck_WithStringComparison_AndUserMessage_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".EndsWith("a", System.StringComparison.CurrentCulture), "message"); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanEndsWithCheck_WithBoolAndCulture_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".EndsWith("a", true, System.Globalization.CultureInfo.CurrentCulture)); }} }} """, method); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(Methods))] public async Task ForBooleanEndsWithCheck_WithBoolAndCulture_AndUserMessage_DoesNotTrigger(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ Xunit.Assert.{0}("abc".EndsWith("a", true, System.Globalization.CultureInfo.CurrentCulture), "message"); }} }} """, method); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertThrowsShouldNotBeUsedForAsyncThrowsCheckTests { public static TheoryData NonAsyncLambdas = [ "ThrowingMethod", "() => 1", ]; public static TheoryData AsyncLambdas = [ "(System.Func)ThrowingMethod", "() => System.Threading.Tasks.Task.Delay(0)", "(System.Func)(async () => await System.Threading.Tasks.Task.Delay(0))", "(System.Func)(async () => await System.Threading.Tasks.Task.Delay(0).ConfigureAwait(false))", ]; [Theory] [MemberData(nameof(NonAsyncLambdas))] public async Task Throws_NonGeneric_WithNonAsyncLambda_DoesNotTrigger(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Action ThrowingMethod = () => {{ throw new System.NotImplementedException(); }}; void TestMethod() {{ Xunit.Assert.Throws(typeof(System.NotImplementedException), {0}); }} }} """, lambda); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NonAsyncLambdas))] public async Task Throws_Generic_WithNonAsyncLambda_DoesNotTrigger(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Action ThrowingMethod = () => {{ throw new System.NotImplementedException(); }}; void TestMethod() {{ Xunit.Assert.Throws({0}); }} }} """, lambda); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NonAsyncLambdas))] public async Task Throws_Generic_WithNamedArgumentException_WithNonAsyncLambda_DoesNotTrigger(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Action ThrowingMethod = () => {{ throw new System.NotImplementedException(); }}; void TestMethod() {{ Xunit.Assert.Throws("param1", {0}); }} }} """, lambda); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(AsyncLambdas))] public async Task Throws_NonGeneric_WithAsyncLambda_Triggers(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Threading.Tasks.Task ThrowingMethod() {{ throw new System.NotImplementedException(); }} void TestMethod() {{ {{|#0:{{|CS0619:Xunit.Assert.Throws(typeof(System.NotImplementedException), {0})|}}|}}; }} }} """, lambda); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.Throws()", Constants.Asserts.ThrowsAsync); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(AsyncLambdas))] public async Task Throws_Generic_WithAsyncLambda_Triggers(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Threading.Tasks.Task ThrowingMethod() {{ throw new System.NotImplementedException(); }} void TestMethod() {{ {{|#0:{{|CS0619:Xunit.Assert.Throws({0})|}}|}}; }} }} """, lambda); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.Throws()", Constants.Asserts.ThrowsAsync); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(AsyncLambdas))] public async Task Throws_Generic_WithNamedArgumentException_WithAsyncLambda_Triggers(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Threading.Tasks.Task ThrowingMethod() {{ throw new System.NotImplementedException(); }} void TestMethod() {{ {{|#0:{{|CS0619:Xunit.Assert.Throws("param1", {0})|}}|}}; }} }} """, lambda); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.Throws()", Constants.Asserts.ThrowsAsync); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(AsyncLambdas))] public async Task ThrowsAsync_NonGeneric_WithAsyncLambda_DoesNotTrigger(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Threading.Tasks.Task ThrowingMethod() {{ throw new System.NotImplementedException(); }} async System.Threading.Tasks.Task TestMethod() {{ await Xunit.Assert.ThrowsAsync(typeof(System.NotImplementedException), {0}); }} }} """, lambda); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(AsyncLambdas))] public async Task ThrowsAsync_Generic_WithAsyncLambda_DoesNotTrigger(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Threading.Tasks.Task ThrowingMethod() {{ throw new System.NotImplementedException(); }} async void TestMethod() {{ await Xunit.Assert.ThrowsAsync({0}); }} }} """, lambda); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(NonAsyncLambdas))] public async Task ThrowsAny_WithNonAsyncLambda_DoesNotTrigger(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Action ThrowingMethod = () => {{ throw new System.NotImplementedException(); }}; void TestMethod() {{ Xunit.Assert.ThrowsAny({0}); }} }} """, lambda); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(AsyncLambdas))] public async Task ThrowsAny_WithAsyncLambda_Triggers(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Threading.Tasks.Task ThrowingMethod() {{ throw new System.NotImplementedException(); }} void TestMethod() {{ {{|#0:{{|CS0619:Xunit.Assert.ThrowsAny({0})|}}|}}; }} }} """, lambda); var expected = Verify.Diagnostic().WithLocation(0).WithArguments("Assert.ThrowsAny()", Constants.Asserts.ThrowsAnyAsync); await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(AsyncLambdas))] public async Task ThrowsAnyAsync_WithAsyncLambda_DoesNotTrigger(string lambda) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Threading.Tasks.Task ThrowingMethod() {{ throw new System.NotImplementedException(); }} async void TestMethod() {{ await Xunit.Assert.ThrowsAnyAsync({0}); }} }} """, lambda); await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssertThrowsShouldUseGenericOverloadCheckTests.cs ================================================ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertThrowsShouldUseGenericOverloadCheckTests { public static TheoryData Methods = [ Constants.Asserts.Throws, Constants.Asserts.ThrowsAsync, ]; [Theory] [MemberData(nameof(Methods))] public async Task ForThrowsCheck_WithExceptionParameter_OnThrowingMethod_Triggers(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ System.Threading.Tasks.Task ThrowingMethod() {{ throw new System.NotImplementedException(); }} void TestMethod() {{ {{|#0:Xunit.Assert.{0}(typeof(System.NotImplementedException), (System.Func)ThrowingMethod)|}}; }} }} """, method); var expected = new List { Verify.Diagnostic().WithLocation(0).WithArguments(method, "System.NotImplementedException"), }; if (method == Constants.Asserts.Throws) expected.Add(DiagnosticResult.CompilerError("CS0619").WithLocation(0).WithArguments("Xunit.Assert.Throws(System.Type, System.Func)", "You must call Assert.ThrowsAsync (and await the result) when testing async code.")); await Verify.VerifyAnalyzer(source, [.. expected]); } [Theory] [MemberData(nameof(Methods))] public async Task ForThrowsCheck_WithExceptionParameter_OnThrowingLambda_Triggers(string method) { var source = string.Format(/* lang=c#-test */ """ class TestClass {{ void TestMethod() {{ {{|#0:Xunit.Assert.{0}(typeof(System.NotImplementedException), () => System.Threading.Tasks.Task.Delay(0))|}}; }} }} """, method); var expected = new List { Verify.Diagnostic().WithLocation(0).WithArguments(method, "System.NotImplementedException"), }; if (method == Constants.Asserts.Throws) expected.Add(DiagnosticResult.CompilerError("CS0619").WithLocation(0).WithArguments("Xunit.Assert.Throws(System.Type, System.Func)", "You must call Assert.ThrowsAsync (and await the result) when testing async code.")); await Verify.VerifyAnalyzer(source, expected.ToArray()); } [Fact] public async Task ForThrowsCheck_WithExceptionTypeArgument_OnThrowingMethod_TriggersCompilerError() { var source = /* lang=c#-test */ """ class TestClass { System.Threading.Tasks.Task ThrowingMethod() { throw new System.NotImplementedException(); } void TestMethod() { {|CS0619:Xunit.Assert.Throws((System.Func)ThrowingMethod)|}; } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForThrowsAsyncCheck_WithExceptionTypeArgument_OnThrowingMethod_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { System.Threading.Tasks.Task ThrowingMethod() { throw new System.NotImplementedException(); } async System.Threading.Tasks.Task TestMethod() { await Xunit.Assert.ThrowsAsync((System.Func)ThrowingMethod); } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForThrowsCheck_WithExceptionTypeArgument_OnThrowingLambda_TriggersCompilerError() { var source = /* lang=c#-test */ """ class TestClass { void TestMethod() { {|CS0619:Xunit.Assert.Throws(() => System.Threading.Tasks.Task.Delay(0))|}; } } """; await Verify.VerifyAnalyzer(source); } [Fact] public async Task ForThrowsAsyncCheck_WithExceptionTypeArgument_OnThrowingLambda_DoesNotTrigger() { var source = /* lang=c#-test */ """ class TestClass { async System.Threading.Tasks.Task TestMethod() { await Xunit.Assert.ThrowsAsync(() => System.Threading.Tasks.Task.Delay(0)); } } """; await Verify.VerifyAnalyzer(source); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AssignableFromAssertionIsConfusinglyNamedTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v2_Pre2_9_3 = CSharpVerifier; using Verify_v3_Pre0_6_0 = CSharpVerifier; public class AssignableFromAssertionIsConfusinglyNamedTests { public static TheoryData Methods = new() { { "IsAssignableFrom", "IsType" }, { "IsNotAssignableFrom", "IsNotType"}, }; [Theory] [MemberData(nameof(Methods))] public async Task WhenReplacementAvailable_Triggers( string method, string replacement) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; class TestClass {{ void TestMethod() {{ {{|#0:Assert.{0}(new object())|}}; {{|#1:Assert.{0}(typeof(object), new object())|}}; }} }} """, method); var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments(method, replacement), Verify.Diagnostic().WithLocation(1).WithArguments(method, replacement), }; await Verify.VerifyAnalyzer(source, expected); } [Theory] [MemberData(nameof(Methods))] public async Task WhenReplacementNotAvailable_DoesNotTriggers( string method, string _) { var source = string.Format(/* lang=c#-test */ """ using System; using Xunit; class TestClass {{ void TestMethod() {{ Assert.{0}(new object()); Assert.{0}(typeof(object), new object()); }} }} """, method); await Verify_v2_Pre2_9_3.VerifyAnalyzer(source); await Verify_v3_Pre0_6_0.VerifyAnalyzer(source); } internal class Analyzer_v2_Pre2_9_3 : AssignableFromAssertionIsConfusinglyNamed { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2(compilation, new Version(2, 9, 2)); } internal class Analyzer_v3_Pre0_6_0 : AssignableFromAssertionIsConfusinglyNamed { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV3(compilation, new Version(0, 5, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/AsyncAssertsShouldBeAwaitedTests.cs ================================================ using System.Globalization; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class AsyncAssertsShouldBeAwaitedTests { [Fact] public async Task UnawaitedNonAssertion_DoesNotTrigger() { var code = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { Task.Delay(1); } } """; await Verify.VerifyAnalyzer(code); } readonly string codeTemplate = /* lang=c#-test */ """ using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading.Tasks; using Xunit; public class TestClass : INotifyPropertyChanged {{ public int Property {{ get; set; }} public event PropertyChangedEventHandler? PropertyChanged; public event EventHandler? SimpleEvent; public event EventHandler? SimpleIntEvent; [Fact] public async Task TestMethod() {{ {0} }} }} public static class MyTaskExtensions {{ public static void ConsumeTask(this Task t) {{ }} }} """; public static TheoryData AsyncAssertions = new() { /* lang=c#-test */ { "AllAsync", "Assert.AllAsync(default(IEnumerable), i => Task.FromResult(true))" }, #if NETCOREAPP3_0_OR_GREATER /* lang=c#-test */ { "AllAsync", "Assert.AllAsync(default(IAsyncEnumerable), i => Task.FromResult(true))" }, #endif /* lang=c#-test */ { "CollectionAsync", "Assert.CollectionAsync(default(IEnumerable))" }, #if NETCOREAPP3_0_OR_GREATER /* lang=c#-test */ { "CollectionAsync", "Assert.CollectionAsync(default(IAsyncEnumerable))" }, #endif /* lang=c#-test */ { "PropertyChangedAsync", "Assert.PropertyChangedAsync(this, nameof(Property), async () => throw new DivideByZeroException())" }, /* lang=c#-test */ { "RaisesAnyAsync", "Assert.RaisesAnyAsync(eh => SimpleEvent += eh, eh => SimpleEvent -= eh, async () => throw new DivideByZeroException())" }, /* lang=c#-test */ { "RaisesAnyAsync", "Assert.RaisesAnyAsync(eh => SimpleIntEvent += eh, eh => SimpleIntEvent -= eh, async () => throw new DivideByZeroException())" }, /* lang=c#-test */ { "RaisesAsync", "Assert.RaisesAsync(eh => SimpleIntEvent += eh, eh => SimpleIntEvent -= eh, async () => throw new DivideByZeroException())" }, /* lang=c#-test */ { "ThrowsAnyAsync", "Assert.ThrowsAnyAsync(async () => throw new DivideByZeroException())" }, /* lang=c#-test */ { "ThrowsAsync", "Assert.ThrowsAsync(typeof(DivideByZeroException), async () => throw new DivideByZeroException())" }, /* lang=c#-test */ { "ThrowsAsync", "Assert.ThrowsAsync(async () => throw new DivideByZeroException())" }, /* lang=c#-test */ { "ThrowsAsync", "Assert.ThrowsAsync(\"argName\", async () => throw new DivideByZeroException())" }, }; [Theory] [MemberData(nameof(AsyncAssertions))] public async Task AwaitedAssert_DoesNotTrigger( string _, string assertion) { var code = string.Format(CultureInfo.InvariantCulture, codeTemplate, $"await {assertion};"); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, code); } [Theory] [MemberData(nameof(AsyncAssertions))] public async Task AssertionWithConsumption_DoesNotTrigger( string _, string assertion) { var code = string.Format(CultureInfo.InvariantCulture, codeTemplate, $"MyTaskExtensions.ConsumeTask({assertion});"); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, code); } [Theory] [MemberData(nameof(AsyncAssertions))] public async Task AssertionWithConsumptionViaExtension_DoesNotTrigger( string _, string assertion) { var code = string.Format(CultureInfo.InvariantCulture, codeTemplate, $"{assertion}.ConsumeTask();"); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, code); } [Theory] [MemberData(nameof(AsyncAssertions))] public async Task AssertionWithStoredTask_DoesNotTrigger( string _, string assertion) { var code = string.Format(CultureInfo.InvariantCulture, codeTemplate, $"var task = {assertion};"); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, code); } [Theory] [MemberData(nameof(AsyncAssertions))] public async Task AssertionWithoutAwait_Triggers( string assertionName, string assertion) { var code = string.Format(CultureInfo.InvariantCulture, codeTemplate, $"{{|#0:{assertion}|}};"); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(assertionName); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, code, expected); } [Theory] [MemberData(nameof(AsyncAssertions))] public async Task AssertionWithUnawaitedContinuation_Triggers( string assertionName, string assertion) { var code = string.Format(CultureInfo.InvariantCulture, codeTemplate, $"{{|#0:{assertion}|}}.ContinueWith(t => {{ }});"); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(assertionName); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, code, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/BooleanAssertsShouldNotBeNegatedTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class BooleanAssertsShouldNotBeNegatedTests { [Theory] [InlineData("False", "True")] [InlineData("True", "False")] public async Task NegatedBooleanAssertion_Triggers( string assertion, string replacement) { var code = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ bool condition = true; {{|#0:Assert.{0}(!condition)|}}; }} }} """, assertion); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(assertion, replacement); await Verify.VerifyAnalyzer(code, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v3_Pre_301 = CSharpVerifier; public class BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckTests { public class X2024_BooleanAssertionsShouldNotBeUsedForSimpleEqualityCheck { public static MatrixTheoryData MethodOperator = new( [Constants.Asserts.True, Constants.Asserts.False], ["==", "!="] ); [Theory] [MemberData(nameof(MethodOperator))] public async Task ComparingAgainstNonLiteral_DoesNotTrigger( string method, string @operator) { var source = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ public void TestMethod() {{ var value1 = 42; var value2 = 2112; var value3 = new {{ innerValue = 2600 }}; Assert.{0}(value1 {1} value2); Assert.{0}(value1 {1} value3.innerValue); }} }} """, method, @operator); await Verify.VerifyAnalyzer(source); } public static MatrixTheoryData MethodOperatorValue = new( [Constants.Asserts.True, Constants.Asserts.False], ["==", "!="], ["\"bacon\"", "'5'", "5", "5l", "5.0d", "5.0f", "5.0m", "MyEnum.Bacon"] ); [Theory] [MemberData(nameof(MethodOperatorValue))] public async Task ComparingAgainstLiteral_WithMessage_DoesNotTrigger( string method, string @operator, string value) { var source = string.Format(/* lang=c#-test */ """ using Xunit; public enum MyEnum {{ None, Bacon, Veggie }} public class TestClass {{ public void TestMethod() {{ var value = {2}; Assert.{0}(value {1} {2}, "message"); Assert.{0}({2} {1} value, "message"); }} }} """, method, @operator, value); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(MethodOperatorValue))] public async Task ComparingAgainstLiteral_WithoutMessage_Triggers( string method, string @operator, string value) { var source = string.Format(/* lang=c#-test */ """ using Xunit; public enum MyEnum {{ None, Bacon, Veggie }} public class TestClass {{ public void TestMethod() {{ var value = {2}; {{|#0:Assert.{0}(value {1} {2})|}}; {{|#1:Assert.{0}({2} {1} value)|}}; }} }} """, method, @operator, value); var suggestedAssert = (method, @operator) switch { (Constants.Asserts.True, "==") or (Constants.Asserts.False, "!=") => Constants.Asserts.Equal, (_, _) => Constants.Asserts.NotEqual, }; var expected = new[] { Verify.Diagnostic("xUnit2024").WithLocation(0).WithArguments(method, suggestedAssert), Verify.Diagnostic("xUnit2024").WithLocation(1).WithArguments(method, suggestedAssert), }; await Verify.VerifyAnalyzer(source, expected); } public static MatrixTheoryData MethodOperatorType = new( [Constants.Asserts.True, Constants.Asserts.False], ["==", "!="], ["string", "int", "object", "MyEnum"] ); [Theory] [MemberData(nameof(MethodOperatorType))] public async Task ComparingAgainstNull_WithMessage_DoesNotTrigger( string method, string @operator, string type) { var source = string.Format(/* lang=c#-test */ """ using Xunit; public enum MyEnum {{ None, Bacon, Veggie }} public class TestClass {{ {2}? field = default; public void TestMethod() {{ Assert.{0}(field {1} null, "Message"); Assert.{0}(null {1} field, "Message"); }} }} """, method, @operator, type); await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source); } [Theory] [MemberData(nameof(MethodOperatorType))] public async Task ComparingAgainstNull_WithoutMessage_Triggers( string method, string @operator, string type) { var source = string.Format(/* lang=c#-test */ """ using Xunit; public enum MyEnum {{ None, Bacon, Veggie }} public class TestClass {{ {2}? field = default; public void TestMethod() {{ {{|#0:Assert.{0}(field {1} null)|}}; {{|#1:Assert.{0}(null {1} field)|}}; }} }} """, method, @operator, type); var suggestedAssert = (method, @operator) switch { (Constants.Asserts.True, "==") or (Constants.Asserts.False, "!=") => Constants.Asserts.Null, (_, _) => Constants.Asserts.NotNull, }; var expected = new[] { Verify.Diagnostic("xUnit2024").WithLocation(0).WithArguments(method, suggestedAssert), Verify.Diagnostic("xUnit2024").WithLocation(1).WithArguments(method, suggestedAssert), }; await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source, expected); } [Fact] public async Task ComparingAgainstNullPointer_DoesNotTrigger() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public unsafe void TestMethod() { var value = 42; var ptr = &value; Assert.True(ptr == null); Assert.True(null == ptr); Assert.True(ptr != null); Assert.True(null != ptr); Assert.False(ptr == null); Assert.False(null == ptr); Assert.False(ptr != null); Assert.False(null != ptr); } } """; await Verify.VerifyAnalyzerV2(source); await Verify_v3_Pre_301.VerifyAnalyzerV3(source); } [Fact] public async Task ComparingAgainstNullPointer_v3_301_Triggers() { var source = /* lang=c#-test */ """ using Xunit; public class TestClass { public unsafe void TestMethod() { var value = 42; var ptr = &value; {|#0:Assert.True(ptr == null)|}; {|#1:Assert.True(null == ptr)|}; {|#2:Assert.True(ptr != null)|}; {|#3:Assert.True(null != ptr)|}; {|#10:Assert.False(ptr == null)|}; {|#11:Assert.False(null == ptr)|}; {|#12:Assert.False(ptr != null)|}; {|#13:Assert.False(null != ptr)|}; } } """; var expected = new[] { Verify.Diagnostic("xUnit2024").WithLocation(0).WithArguments("True", "Null"), Verify.Diagnostic("xUnit2024").WithLocation(1).WithArguments("True", "Null"), Verify.Diagnostic("xUnit2024").WithLocation(2).WithArguments("True", "NotNull"), Verify.Diagnostic("xUnit2024").WithLocation(3).WithArguments("True", "NotNull"), Verify.Diagnostic("xUnit2024").WithLocation(10).WithArguments("False", "NotNull"), Verify.Diagnostic("xUnit2024").WithLocation(11).WithArguments("False", "NotNull"), Verify.Diagnostic("xUnit2024").WithLocation(12).WithArguments("False", "Null"), Verify.Diagnostic("xUnit2024").WithLocation(13).WithArguments("False", "Null"), }; await Verify.VerifyAnalyzerV3(source, expected); } } public class X2025_BooleanAssertionCanBeSimplified { public static MatrixTheoryData MethodOperatorValue = new( [Constants.Asserts.True, Constants.Asserts.False], ["==", "!="], ["true", "false"] ); [Theory] [MemberData(nameof(MethodOperatorValue))] public async Task ComparingAgainstBooleanLiteral_Triggers( string method, string @operator, string value) { var source = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ bool field = {2}; void TestMethod() {{ {{|#0:Assert.{0}(field {1} {2})|}}; {{|#1:Assert.{0}(field {1} {2}, "Message")|}}; {{|#2:Assert.{0}({2} {1} field)|}}; {{|#3:Assert.{0}({2} {1} field, "Message")|}}; }} }} """, method, @operator, value); var expected = new[] { Verify.Diagnostic("xUnit2025").WithLocation(0).WithArguments(method), Verify.Diagnostic("xUnit2025").WithLocation(1).WithArguments(method), Verify.Diagnostic("xUnit2025").WithLocation(2).WithArguments(method), Verify.Diagnostic("xUnit2025").WithLocation(3).WithArguments(method), }; await Verify.VerifyAnalyzer(source, expected); } } public class Analyzer_v3_Pre301 : BooleanAssertsShouldNotBeUsedForSimpleEqualityCheck { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV3(compilation, new(3, 0, 0)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/DoNotUseAssertEmptyWithProblematicTypesTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; public class DoNotUseAssertEmptyWithProblematicTypesTests { public static TheoryData ProblematicTypes = new() { /* lang=c#-test */ { "StringValues.Empty", "Microsoft.Extensions.Primitives.StringValues", "it is implicitly cast to a string, not a collection" }, /* lang=c#-test */ { "new ArraySegment()", "System.ArraySegment", "its implementation of GetEnumerator() can throw" }, }; [Theory] [InlineData(/* lang=c#-test */ "new int[0]")] [InlineData(/* lang=c#-test */ "new List()")] [InlineData(/* lang=c#-test */ "new Dictionary()")] public async Task NonProblematicCollection_DoesNotTrigger(string invocation) { var source = string.Format(/* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public class TestClass {{ public void TestMethod() {{ Assert.Empty({0}); Assert.NotEmpty({0}); }} }} """, invocation); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(ProblematicTypes))] public async Task ConvertingToCollection_DoesNotTrigger( string invocation, string _1, string _2) { var source = string.Format(/* lang=c#-test */ """ using System; using System.Linq; using Microsoft.Extensions.Primitives; using Xunit; public class TestClass {{ public void TestMethod() {{ Assert.Empty({0}.ToArray()); Assert.NotEmpty({0}.ToArray()); }} }} """, invocation); await Verify.VerifyAnalyzer(source); } [Theory] [MemberData(nameof(ProblematicTypes))] public async Task UsingProblematicType_Triggers( string invocation, string typeName, string problem) { var source = string.Format(/* lang=c#-test */ """ using System; using Microsoft.Extensions.Primitives; using Xunit; public class TestClass {{ public void TestMethod() {{ {{|#0:Assert.Empty({0})|}}; {{|#1:Assert.NotEmpty({0})|}}; }} }} """, invocation); var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("Empty", typeName, problem), Verify.Diagnostic().WithLocation(1).WithArguments("NotEmpty", typeName, problem), }; await Verify.VerifyAnalyzer(source, expected); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/SetEqualityAnalyzerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; public class SetEqualityAnalyzerTests { const string customSetAndComparer = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; public class MySet : ISet { public int Count => throw new System.NotImplementedException(); public bool IsReadOnly => throw new System.NotImplementedException(); public bool Add(int item) => throw new System.NotImplementedException(); public void Clear() => throw new System.NotImplementedException(); public bool Contains(int item) => throw new System.NotImplementedException(); public void CopyTo(int[] array, int arrayIndex) => throw new System.NotImplementedException(); public void ExceptWith(IEnumerable other) => throw new System.NotImplementedException(); public IEnumerator GetEnumerator() => throw new System.NotImplementedException(); public void IntersectWith(IEnumerable other) => throw new System.NotImplementedException(); public bool IsProperSubsetOf(IEnumerable other) => throw new System.NotImplementedException(); public bool IsProperSupersetOf(IEnumerable other) => throw new System.NotImplementedException(); public bool IsSubsetOf(IEnumerable other) => throw new System.NotImplementedException(); public bool IsSupersetOf(IEnumerable other) => throw new System.NotImplementedException(); public bool Overlaps(IEnumerable other) => throw new System.NotImplementedException(); public bool Remove(int item) => throw new System.NotImplementedException(); public bool SetEquals(IEnumerable other) => throw new System.NotImplementedException(); public void SymmetricExceptWith(IEnumerable other) => throw new System.NotImplementedException(); public void UnionWith(IEnumerable other) => throw new System.NotImplementedException(); void ICollection.Add(int item) => throw new System.NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException(); } public class MyComparer : IEqualityComparer { public bool Equals(int x, int y) => throw new System.NotImplementedException(); public int GetHashCode(int obj) => throw new System.NotImplementedException(); } """; public class X2026_SetsMustBeComparedWithEqualityComparer { public static MatrixTheoryData MethodWithCollectionCreationData => new( /* lang=c#-test */ ["Equal", "NotEqual"], /* lang=c#-test */ ["new HashSet()", "new HashSet().ToImmutableHashSet()", "new MySet()"], /* lang=c#-test */ ["new HashSet()", "new HashSet().ToImmutableHashSet()", "new MySet()"] ); [Theory] [MemberData(nameof(MethodWithCollectionCreationData))] public async Task WithCollectionComparer_DoesNotTrigger( string method, string collection1, string collection2) { var code = string.Format(/* lang=c#-test */ """ using Xunit; using System.Collections.Generic; using System.Collections.Immutable; public class TestClass {{ [Fact] public void TestMethod() {{ var collection1 = {1}; var collection2 = {2}; Assert.{0}(collection1, collection2, (IEnumerable e1, IEnumerable e2) => true); }} }} """, method, collection1, collection2); await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, [code, customSetAndComparer]); } [Theory] [MemberData(nameof(MethodWithCollectionCreationData))] public async Task WithEqualityComparer_DoesNotTrigger( string method, string collection1, string collection2) { var code = string.Format(/* lang=c#-test */ """ using Xunit; using System.Collections.Generic; using System.Collections.Immutable; public class TestEqualityComparer : IEqualityComparer {{ public bool Equals(int x, int y) {{ return true; }} public int GetHashCode(int obj) {{ return 0; }} }} public class TestClass {{ [Fact] public void TestMethod() {{ var collection1 = {1}; var collection2 = {2}; Assert.{0}(collection1, collection2, new TestEqualityComparer()); }} }} """, method, collection1, collection2); await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, [code, customSetAndComparer]); } [Theory] [MemberData(nameof(MethodWithCollectionCreationData))] public async Task WithComparerLambda_Triggers( string method, string collection1, string collection2) { var code = string.Format(/* lang=c#-test */ """ using Xunit; using System.Collections.Generic; using System.Collections.Immutable; public class TestClass {{ [Fact] public void TestMethod() {{ var collection1 = {1}; var collection2 = {2}; {{|#0:Assert.{0}(collection1, collection2, (int e1, int e2) => true)|}}; }} }} """, method, collection1, collection2); var expected = Verify.Diagnostic("xUnit2026").WithLocation(0).WithArguments(method); await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, [code, customSetAndComparer], expected); } #if ROSLYN_LATEST // C# 10 is required for local functions public static MatrixTheoryData ComparerFunctionData() => new( /* lang=c#-test */ ["Equal", "NotEqual"], /* lang=c#-test */ ["(int e1, int e2) => true", "FuncComparer", "LocalFunc", "funcDelegate"], /* lang=c#-test */ ["new HashSet()", "new HashSet().ToImmutableHashSet()", "new MySet()"], /* lang=c#-test */ ["new HashSet()", "new HashSet().ToImmutableHashSet()", "new MySet()"] ); [Theory] [MemberData(nameof(ComparerFunctionData))] public async Task WithComparerFunction_Triggers( string method, string comparerFuncSyntax, string collection1, string collection2) { var code = string.Format(/* lang=c#-test */ """ using Xunit; using System.Collections.Generic; using System.Collections.Immutable; public class TestClass {{ private bool FuncComparer(int obj1, int obj2) {{ return true; }} private delegate bool FuncDelegate(int obj1, int obj2); [Fact] public void TestMethod() {{ var collection1 = {2}; var collection2 = {3}; bool LocalFunc(int obj1, int obj2) {{ return true; }} var funcDelegate = FuncComparer; {{|#0:Assert.{0}(collection1, collection2, {1})|}}; }} }} """, method, comparerFuncSyntax, collection1, collection2); var expected = Verify.Diagnostic("xUnit2026").WithLocation(0).WithArguments(method); await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, new[] { code, customSetAndComparer }, expected); } #endif } public class X2027_SetsShouldNotBeComparedToLinearContainers { public static MatrixTheoryData MethodAndLinearContainers => new( /* lang=c#-test */ ["Equal", "NotEqual"], /* lang=c#-test */ ["new List()", "new SortedSet()", "new HashSet().OrderBy(x => x)", "new MySet().OrderBy(x => x)"] ); [Theory] [MemberData(nameof(MethodAndLinearContainers))] public async Task LinearContainers_DoesNotTrigger( string method, string collection) { var code = string.Format(/* lang=c#-test */ """ using Xunit; using System.Collections.Generic; using System.Linq; public class TestClass {{ [Fact] public void TestMethod() {{ var collection1 = new List(); var collection2 = {1}; Assert.{0}(collection1, collection2); Assert.{0}(collection1, collection2, (int e1, int e2) => true); Assert.{0}(collection1, collection2, new MyComparer()); }} }} """, method, collection); await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, [code, customSetAndComparer]); } [Fact] public async Task CastedSet_DoesNotTrigger() { var code = /* lang=c#-test */ """ using Xunit; using System.Collections.Generic; public class TestClass { [Fact] public void TestMethod() { var expected = new HashSet { "bar", "foo" }; var actual = new HashSet { "foo", "bar" }; Assert.Equal(expected, actual); Assert.Equal(expected, (ISet)actual); Assert.Equal((ISet)expected, actual); Assert.Equal((ISet)expected, (ISet)actual); } } """; await Verify.VerifyAnalyzer(code); } public static MatrixTheoryData MethodAndTypeAndInitializer => new( /* lang=c#-test */ ["Equal", "NotEqual"], /* lang=c#-test */ [ ("System.Collections.Generic.HashSet", "new HashSet()"), ("System.Collections.Immutable.ImmutableHashSet", "new HashSet().ToImmutableHashSet()"), ("MySet", "new MySet()") ] ); [Theory] [MemberData(nameof(MethodAndTypeAndInitializer), DisableDiscoveryEnumeration = true)] public async Task SetWithLinearContainer_Triggers( string method, (string type, string initializer) collection) { var code = string.Format(/* lang=c#-test */ """ using Xunit; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; public class TestClass {{ [Fact] public void TestMethod() {{ var collection1 = new List(); var collection2 = {1}; {{|#0:Assert.{0}(collection1, collection2)|}}; {{|#1:Assert.{0}(collection1, collection2, (int e1, int e2) => true)|}}; {{|#2:Assert.{0}(collection1, collection2, new MyComparer())|}}; {{|#3:Assert.{0}(collection2, collection1)|}}; {{|#4:Assert.{0}(collection2, collection1, (int e1, int e2) => true)|}}; {{|#5:Assert.{0}(collection2, collection1, new MyComparer())|}}; }} }} """, method, collection.initializer); var expected = new[] { Verify.Diagnostic("xUnit2027").WithLocation(0).WithArguments("System.Collections.Generic.List", collection.type), Verify.Diagnostic("xUnit2027").WithLocation(1).WithArguments("System.Collections.Generic.List", collection.type), Verify.Diagnostic("xUnit2027").WithLocation(2).WithArguments("System.Collections.Generic.List", collection.type), Verify.Diagnostic("xUnit2027").WithLocation(3).WithArguments(collection.type, "System.Collections.Generic.List"), Verify.Diagnostic("xUnit2027").WithLocation(4).WithArguments(collection.type, "System.Collections.Generic.List"), Verify.Diagnostic("xUnit2027").WithLocation(5).WithArguments(collection.type, "System.Collections.Generic.List"), }; await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, [code, customSetAndComparer], expected); } } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X2000/UseAssertFailInsteadOfBooleanAssertTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_Unsupported = CSharpVerifier; public class UseAssertFailInsteadOfBooleanAssertTests { const string codeTemplate = /* lang=c#-test */ """ public class TestClass {{ [Xunit.Fact] public void TestMethod() {{ {{|#0:Xunit.Assert.{0}({1}, "failure message")|}}; }} }} """; [Theory] [InlineData(Constants.Asserts.True, "false")] [InlineData(Constants.Asserts.False, "true")] public async Task OppositeTestWithMessage_Prev25_DoesNotTrigger( string assertion, string targetValue) { var source = string.Format(codeTemplate, assertion, targetValue); await Verify_Unsupported.VerifyAnalyzer(source); } [Theory] [InlineData(Constants.Asserts.True, "false")] [InlineData(Constants.Asserts.False, "true")] public async Task OppositeTestWithMessage_v25_Triggers( string assertion, string targetValue) { var source = string.Format(codeTemplate, assertion, targetValue); var expected = Verify.Diagnostic().WithLocation(0).WithArguments(assertion, targetValue); await Verify.VerifyAnalyzer(source, expected); } [Theory] [InlineData(Constants.Asserts.True, "true")] [InlineData(Constants.Asserts.False, "false")] public async Task SameTestWithMessage_DoesNotTrigger( string assertion, string targetValue) { var source = string.Format(codeTemplate, assertion, targetValue); await Verify.VerifyAnalyzer(source); } [Fact] public async Task NonConstantInvocation_DoesNotTrigger() { var source = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { var value = (1 != 2); Xunit.Assert.False(value, "failure message"); } } """; await Verify.VerifyAnalyzer(source); } internal class Analyzer_Pre25 : UseAssertFailInsteadOfBooleanAssert { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2Assert(compilation, new Version(2, 4, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X3000/CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Verify_WithAbstractions = CSharpVerifier; using Verify_WithExecution = CSharpVerifier; using Verify_WithRunnerUtility = CSharpVerifier; public class CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests { public class WithAbstractions { readonly static string Template = /* lang=c#-test */ """ using Xunit.Abstractions; public class {{|#0:MyClass|}}: {0} {{ }} """; public static TheoryData Interfaces = [ // Discovery and execution messages MemberCount("IMessageSink", 1), MemberCount("IMessageSinkMessage", 0), // Reflection MemberCount("IAssemblyInfo", 5), MemberCount("IAttributeInfo", 3), MemberCount("IMethodInfo", 11), MemberCount("IParameterInfo", 2), MemberCount("ITypeInfo", 13), // Test cases MemberCount("ITest", 2), MemberCount("ITestAssembly", 4), MemberCount("ITestCase", 9), MemberCount("ITestClass", 4), MemberCount("ITestCollection", 6), MemberCount("ITestMethod", 4), // Test frameworks MemberCount("ISourceInformation", 4), MemberCount("ISourceInformationProvider", 2), MemberCount("ITestFramework", 4), MemberCount("ITestFrameworkDiscoverer", 6), MemberCount("ITestFrameworkExecutor", 4), ]; [Fact] public async Task NoInterfaces_DoesNotTrigger() { var source = "public class Foo { }"; await Verify_WithAbstractions.VerifyAnalyzerV2(source); } [Theory] [MemberData(nameof(Interfaces))] public async Task InterfaceWithoutBaseClass_Triggers(string @interface) { var source = string.Format(Template, @interface); var expected = Verify_WithAbstractions.Diagnostic().WithLocation(0).WithArguments("MyClass"); await Verify_WithAbstractions.VerifyAnalyzerV2(source, expected); } internal class Analyzer : CrossAppDomainClassesMustBeLongLivedMarshalByRefObject { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2Abstractions(compilation); } } public class WithExecution { readonly static string Template = /* lang=c#-test */ """ using Xunit.Abstractions; public class Foo {{ }} public class MyLLMBRO: Xunit.LongLivedMarshalByRefObject {{ }} public class {{|#0:MyClass|}}: {0} {{ }} """; public static TheoryData Interfaces = new(WithAbstractions.Interfaces) { MemberCount("Xunit.Sdk.IXunitTestCase", 13) }; public static TheoryData InterfacesWithBaseClasses { get { var result = new TheoryData(); foreach (var @interface in Interfaces) { result.Add(@interface.Data, "MyLLMBRO"); result.Add(@interface.Data, "Xunit.LongLivedMarshalByRefObject"); } return result; } } [Fact] public async Task NoInterfaces_DoesNotTrigger() { var source = /* lang=c#-test */ "public class Foo { }"; await Verify_WithExecution.VerifyAnalyzerV2(source); } [Fact] public async Task WithXunitTestCase_DoesNotTrigger() { var source = string.Format(Template, "Xunit.Sdk.XunitTestCase"); await Verify_WithExecution.VerifyAnalyzerV2(source); } [Theory] [MemberData(nameof(InterfacesWithBaseClasses))] public async Task CompatibleBaseClass_DoesNotTrigger( string @interface, string baseClass) { var source = string.Format(Template, $"{baseClass}, {@interface}"); await Verify_WithExecution.VerifyAnalyzerV2(source); } [Theory] [MemberData(nameof(Interfaces))] public async Task InterfaceWithoutBaseClass_Triggers(string @interface) { var source = string.Format(Template, @interface); var expected = Verify_WithExecution.Diagnostic().WithLocation(0).WithArguments("MyClass"); await Verify_WithExecution.VerifyAnalyzerV2(source, expected); } [Theory] [MemberData(nameof(Interfaces))] public async Task IncompatibleBaseClass_Triggers(string @interface) { var source = string.Format(Template, $"Foo, {@interface}"); var expected = Verify_WithExecution.Diagnostic().WithLocation(0).WithArguments("MyClass"); await Verify_WithExecution.VerifyAnalyzerV2(source, expected); } internal class Analyzer : CrossAppDomainClassesMustBeLongLivedMarshalByRefObject { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2Execution(compilation); } } public class WithRunnerUtility { readonly static string Template = /* lang=c#-test */ """ using Xunit.Abstractions; public class Foo {{ }} public class MyLLMBRO: Xunit.Sdk.LongLivedMarshalByRefObject {{ }} public class {{|#0:MyClass|}}: {0} {{ }} """; public static TheoryData Interfaces = WithAbstractions.Interfaces; public static TheoryData InterfacesWithBaseClasses { get { var result = new TheoryData(); foreach (var @interface in Interfaces) { result.Add(@interface.Data, "MyLLMBRO"); result.Add(@interface.Data, "Xunit.Sdk.LongLivedMarshalByRefObject"); } return result; } } [Fact] public async Task NoInterfaces_DoesNotTrigger() { var source = /* lang=c#-test */ "public class Foo { }"; await Verify_WithRunnerUtility.VerifyAnalyzerV2RunnerUtility(source); } [Theory] [MemberData(nameof(InterfacesWithBaseClasses))] public async Task CompatibleBaseClass_DoesNotTrigger( string @interface, string baseClass) { var source = string.Format(Template, $"{baseClass}, {@interface}"); await Verify_WithRunnerUtility.VerifyAnalyzerV2RunnerUtility(source); } [Theory] [MemberData(nameof(Interfaces))] public async Task InterfaceWithoutBaseClass_Triggers(string @interface) { var source = string.Format(Template, @interface); var expected = Verify_WithRunnerUtility.Diagnostic().WithLocation(0).WithArguments("MyClass"); await Verify_WithRunnerUtility.VerifyAnalyzerV2RunnerUtility(source, expected); } [Theory] [MemberData(nameof(Interfaces))] public async Task IncompatibleBaseClass_Triggers(string @interface) { var source = string.Format(Template, $"Foo, {@interface}"); var expected = Verify_WithRunnerUtility.Diagnostic().WithLocation(0).WithArguments("MyClass"); await Verify_WithRunnerUtility.VerifyAnalyzerV2RunnerUtility(source, expected); } internal class Analyzer : CrossAppDomainClassesMustBeLongLivedMarshalByRefObject { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2RunnerUtility(compilation); } } public static string MemberCount( string memberName, int memberCount) { var result = memberName; while (memberCount-- > 0) result = $"{{|CS0535:{result}|}}"; return result; } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X3000/DoNotTestForConcreteTypeOfJsonSerializableTypesTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Verify = CSharpVerifier; namespace Xunit.Analyzers; public class DoNotTestForConcreteTypeOfJsonSerializableTypesTests { [Fact] public async Task AcceptanceTest() { var code = /* lang=c#-test */ """ using Xunit; using System.Collections.Generic; using System.Linq; public class GenericClass { } public class TheClass { /// /// Testing XMLDOC references . /// public void TheMethod() { var message = new object(); var collection = new List(); // Direct construction _ = new MyMessage { PropertyValue = 2112 }; static MyMessage Create(int propertyValue) => new() { PropertyValue = propertyValue }; // Non-serialized type _ = message is IMyMessage; _ = message is not IMyMessage; _ = message as IMyMessage; _ = (IMyMessage)message; _ = typeof(IMyMessage); _ = collection.OfType(); _ = default(IEnumerable); _ = new GenericClass(); _ = new GenericClass(); // Serialized type _ = [|message is MyMessage|]; _ = [|message is not MyMessage|]; _ = [|message as MyMessage|]; _ = [|(MyMessage)message|]; _ = [|typeof(MyMessage)|]; _ = collection.[|OfType|](); _ = default([|IEnumerable|]); _ = new [|GenericClass|](); _ = new [|GenericClass|](); } } """; var messagePartial1 = /* lang=c#-test */ """ using Xunit.Sdk; public interface IMyMessage { } [JsonTypeID("MyMessage")] sealed partial class MyMessage : IMyMessage { } """; var messagePartial2 = /* lang=c#-test */ """ public partial class MyMessage { public int PropertyValue { get; set; } }; """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp9, [messagePartial1, messagePartial2, code]); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X3000/FactAttributeDerivedClassesShouldProvideSourceInformationConstructorTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; using Verify_v3_Pre300 = CSharpVerifier; public class FactAttributeDerivedClassesShouldProvideSourceInformationConstructorTests { [Fact] public async Task v2_OlderV3_DoesNotTrigger() { var code = /* lang=c#-test */ """ using Xunit; public class MyFactAttribute : FactAttribute { } public class MyTheoryAttribute : TheoryAttribute { } """; await Verify.VerifyAnalyzerV2(code); await Verify_v3_Pre300.VerifyAnalyzerV3(code); } [Fact] public async Task v3_Triggers() { #if ROSLYN_LATEST var code = /* lang=c#-test */ """ using System.Runtime.CompilerServices; using Xunit; public class {|xUnit3003:MyFactAttribute|} : FactAttribute { } public class MyFactWithCtorArgs([CallerFilePath] string? foo = null, [CallerLineNumber] int bar = -1) : FactAttribute(foo, bar) { } public class {|xUnit3003:MyTheoryAttribute|} : TheoryAttribute { } public class MyTheoryWithCtorArgs( int x, string y, [CallerFilePath] string? sourceFilePath = null, [CallerLineNumber] int sourceLineNumber = -1) : TheoryAttribute(sourceFilePath, sourceLineNumber) { } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp12, code); #else var code = /* lang=c#-test */ """ using System.Runtime.CompilerServices; using Xunit; public class {|xUnit3003:MyFactAttribute|} : FactAttribute { } public class MyFactWithCtorArgs : FactAttribute { public MyFactWithCtorArgs([CallerFilePath] string? foo = null, [CallerLineNumber] int bar = -1) : base(foo, bar) { } } public class {|xUnit3003:MyTheoryAttribute|} : TheoryAttribute { } public class MyTheoryWithCtorArgs : TheoryAttribute { public MyTheoryWithCtorArgs(int x, string y, [CallerFilePath] string? foo = null, [CallerLineNumber] int bar = -1) : base(foo, bar) { } } """; await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, code); #endif } internal class Analyzer_v3_Pre300 : FactAttributeDerivedClassesShouldProvideSourceInformationConstructor { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV3(compilation, new Version(2, 999, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Analyzers/X3000/SerializableClassMustHaveParameterlessConstructorTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers; using VerifyV2 = CSharpVerifier; using VerifyV3 = CSharpVerifier; public class SerializableClassMustHaveParameterlessConstructorTests { public class JsonTypeID { [Fact] public async Task JsonTypeIDAcceptanceTest() { var source = /* lang=c#-test */ """ using Xunit.Sdk; public class NonSerializedClass { } [JsonTypeID("1")] public class SerializedWithImplicitCtor { } [JsonTypeID("2")] public class SerializedWithExplicitCtor { public SerializedWithExplicitCtor() { } } [JsonTypeID("3")] public class {|#0:SerializedWithNoMatchingCtor|} { public SerializedWithNoMatchingCtor(int _) { } } [JsonTypeID("4")] public class {|#1:SerializedWithNonPublicCtor|} { protected SerializedWithNonPublicCtor() { } } """; var expected = new[] { VerifyV3.Diagnostic().WithLocation(0).WithArguments("SerializedWithNoMatchingCtor", "Xunit.Sdk.JsonTypeIDAttribute"), VerifyV3.Diagnostic().WithLocation(1).WithArguments("SerializedWithNonPublicCtor", "Xunit.Sdk.JsonTypeIDAttribute"), }; await VerifyV3.VerifyAnalyzerV3(source, expected); } } public class RunnerReporter { static readonly string Template = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit.Runner.Common; using Xunit.Sdk; public {1} {{|#0:MyRunnerReporter|}} : IRunnerReporter {{ {0} public bool CanBeEnvironmentallyEnabled => false; public string Description => string.Empty; public bool ForceNoLogo => false; public bool IsEnvironmentallyEnabled => false; public string? RunnerSwitch => "unused"; public ValueTask CreateMessageHandler( IRunnerLogger logger, IMessageSink? diagnosticMessageSink) => throw new NotImplementedException(); }} """; [Fact] public async Task ImplicitConstructor_DoesNotTrigger() { var source = string.Format(Template, string.Empty, "class"); await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Fact] public async Task WrongConstructor_Triggers() { var source = string.Format(Template, /* lang=c#-test */ "public MyRunnerReporter(int x) { }", "class"); var expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("MyRunnerReporter", "Xunit.Runner.Common.IRunnerReporter"); await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } [Fact] public async Task WrongConstructorOnAbstractClass_DoesNotTrigger() { var source = string.Format(Template, /* lang=c#-test */ "public MyRunnerReporter(int x) { }", "abstract class"); await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Fact] public async Task NonPublicConstructor_Triggers() { var source = string.Format(Template, /* lang=c#-test */ "protected MyRunnerReporter() { }", "class"); var expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("MyRunnerReporter", "Xunit.Runner.Common.IRunnerReporter"); await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } [Fact] public async Task NonPublicConstructorOnAbstractClass_DoesNotTrigger() { await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, string.Format(Template, /* lang=c#-test */ "protected MyRunnerReporter() { }", "abstract class")); } [Fact] public async Task PublicParameterlessConstructor_DoesNotTrigger() { await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, string.Format(Template, /* lang=c#-test */ "public MyRunnerReporter() { }", "class")); } } public class XunitSerializable { static readonly string Template = /* lang=c#-test */ """ using {2}; public interface IMySerializer : IXunitSerializable {{ }} public {3} {{|#0:Foo|}} : {0} {{ {1} public void Deserialize(IXunitSerializationInfo info) {{ }} public void Serialize(IXunitSerializationInfo info) {{ }} }} """; public static TheoryData Interfaces = [ "IXunitSerializable", "IMySerializer" ]; [Theory] [MemberData(nameof(Interfaces))] public async Task ImplicitConstructors_DoesNotTrigger(string @interface) { await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, "", "Xunit.Abstractions", "class")); await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, "", "Xunit.Sdk", "class")); } [Theory] [MemberData(nameof(Interfaces))] public async Task WrongConstructor_Triggers(string @interface) { var v2Source = string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Abstractions", "class"); var v2Expected = VerifyV2.Diagnostic().WithLocation(0).WithArguments("Foo", "Xunit.Abstractions.IXunitSerializable"); await VerifyV2.VerifyAnalyzerV2(v2Source, v2Expected); var v3Source = string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Sdk", "class"); var v3Expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("Foo", "Xunit.Sdk.IXunitSerializable"); await VerifyV3.VerifyAnalyzerV3(v3Source, v3Expected); } [Theory] [MemberData(nameof(Interfaces))] public async Task WrongConstructorOnAbstractClass_DoesNotTrigger(string @interface) { await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Abstractions", "abstract class")); await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Sdk", "abstract class")); } [Theory] [MemberData(nameof(Interfaces))] public async Task NonPublicConstructor_Triggers(string @interface) { var v2Source = string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Abstractions", "class"); var v2Expected = VerifyV2.Diagnostic().WithLocation(0).WithArguments("Foo", "Xunit.Abstractions.IXunitSerializable"); await VerifyV2.VerifyAnalyzerV2(v2Source, v2Expected); var v3Source = string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Sdk", "class"); var v3Expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("Foo", "Xunit.Sdk.IXunitSerializable"); await VerifyV3.VerifyAnalyzerV3(v3Source, v3Expected); } [Theory] [MemberData(nameof(Interfaces))] public async Task NonPublicConstructorOnAbstractClass_DoesNotTrigger(string @interface) { await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Abstractions", "abstract class")); await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Sdk", "abstract class")); } [Theory] [MemberData(nameof(Interfaces))] public async Task PublicParameterlessConstructor_DoesNotTrigger(string @interface) { await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, /* lang=c#-test */ "public Foo() { }", "Xunit.Abstractions", "class")); await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, /* lang=c#-test */ "public Foo() { }", "Xunit.Sdk", "class")); } } public class XunitSerializer { static readonly string Template = /* lang=c#-test */ """ using System; using Xunit.Sdk; public {1} {{|#0:MySerializer|}} : IXunitSerializer {{ {0} public object Deserialize(Type type, string serializedValue) => null!; public bool IsSerializable(Type type, object? value, out string? failureReason) {{ failureReason = null; return true; }} public string Serialize(object value) => string.Empty; }} """; [Fact] public async Task ImplicitConstructor_DoesNotTrigger() { var source = string.Format(Template, string.Empty, "class"); await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } [Fact] public async Task WrongConstructor_Triggers() { var source = string.Format(Template, /* lang=c#-test */ "public MySerializer(int x) { }", "class"); var expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("MySerializer", "Xunit.Sdk.IXunitSerializer"); await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } [Fact] public async Task WrongConstructorOnAbstractClass_DoesNotTrigger() { await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, string.Format(Template, /* lang=c#-test */ "public MySerializer(int x) { }", "abstract class")); } [Fact] public async Task NonPublicConstructor_Triggers() { var source = string.Format(Template, /* lang=c#-test */ "protected MySerializer() { }", "class"); var expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("MySerializer", "Xunit.Sdk.IXunitSerializer"); await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected); } [Fact] public async Task NonPublicConstructorOnAbstractClass_DoesNotTrigger() { await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, string.Format(Template, /* lang=c#-test */ "protected MySerializer() { }", "abstract class")); } [Fact] public async Task PublicParameterlessConstructor_DoesNotTrigger() { var source = string.Format(Template, /* lang=c#-test */ "public MySerializer() { }", "class"); await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source); } } public class V2Analyzer : SerializableClassMustHaveParameterlessConstructor { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2Abstractions(compilation); } public class V3Analyzer : SerializableClassMustHaveParameterlessConstructor { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV3(compilation); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/ClassDataAttributeMustPointAtValidClassFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class ClassDataAttributeMustPointAtValidClassFixerTests { [Fact] public async Task AddsIEnumerable() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestData { } public class TestClass { [Theory] [{|xUnit1007:ClassData(typeof(TestData))|}] public void TestMethod(int _) { } } """; var afterV2 = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestData : {|CS0535:{|CS0535:IEnumerable|}|} { } public class TestClass { [Theory] [ClassData(typeof(TestData))] public void TestMethod(int _) { } } """; var afterV3 = afterV2.Replace("ClassData(typeof(TestData))", "{|xUnit1050:ClassData(typeof(TestData))|}"); await Verify.VerifyCodeFixV2(before, afterV2, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass); await Verify.VerifyCodeFixV3(before, afterV3, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass); } [Fact] public async Task ConvertsParameterlessConstructorToPublic() { var before = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; public class TestData : IEnumerable { TestData() { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class TestClass { [Theory] [{|xUnit1007:ClassData(typeof(TestData))|}] public void TestMethod(int _) { } } """; var afterV2 = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; public class TestData : IEnumerable { public TestData() { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class TestClass { [Theory] [ClassData(typeof(TestData))] public void TestMethod(int _) { } } """; var afterV3 = afterV2.Replace("ClassData(typeof(TestData))", "{|xUnit1050:ClassData(typeof(TestData))|}"); await Verify.VerifyCodeFixV2(before, afterV2, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass); await Verify.VerifyCodeFixV3(before, afterV3, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass); } [Fact] public async Task AddsPublicParameterlessConstructor() { var before = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; public class TestData : IEnumerable { TestData(int _) { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class TestClass { [Theory] [{|xUnit1007:ClassData(typeof(TestData))|}] public void TestMethod(int _) { } } """; #if ROSLYN_LATEST // They seem to have removed the extra blank line somewhere between 4.11 and 4.14 var afterV2 = """ using System.Collections; using System.Collections.Generic; using Xunit; public class TestData : IEnumerable { TestData(int _) { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; public TestData() { } } public class TestClass { [Theory] [ClassData(typeof(TestData))] public void TestMethod(int _) { } } """; #else var afterV2 = """ using System.Collections; using System.Collections.Generic; using Xunit; public class TestData : IEnumerable { TestData(int _) { } public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; public TestData() { } } public class TestClass { [Theory] [ClassData(typeof(TestData))] public void TestMethod(int _) { } } """; #endif var afterV3 = afterV2.Replace("ClassData(typeof(TestData))", "{|xUnit1050:ClassData(typeof(TestData))|}"); await Verify.VerifyCodeFixV2(before, afterV2, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass); await Verify.VerifyCodeFixV3(before, afterV3, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass); } [Fact] public async Task RemovesAbstractModifierFromDataClass() { var before = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; public abstract class TestData : IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class TestClass { [Theory] [{|xUnit1007:ClassData(typeof(TestData))|}] public void TestMethod(int _) { } } """; var afterV2 = /* lang=c#-test */ """ using System.Collections; using System.Collections.Generic; using Xunit; public class TestData : IEnumerable { public IEnumerator GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class TestClass { [Theory] [ClassData(typeof(TestData))] public void TestMethod(int _) { } } """; var afterV3 = afterV2.Replace("ClassData(typeof(TestData))", "{|xUnit1050:ClassData(typeof(TestData))|}"); await Verify.VerifyCodeFixV2(before, afterV2, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass); await Verify.VerifyCodeFixV3(before, afterV3, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/CollectionDefinitionClassesMustBePublicFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class CollectionDefinitionClassesMustBePublicFixerTests { [Fact] public async Task FixAll_MakesAllClassesPublic() { var before = /* lang=c#-test */ """ [Xunit.CollectionDefinition("MyCollection1")] class [|CollectionDefinitionClass1|] { } [Xunit.CollectionDefinition("MyCollection2")] internal class [|CollectionDefinitionClass2|] { } """; var after = /* lang=c#-test */ """ [Xunit.CollectionDefinition("MyCollection1")] public class CollectionDefinitionClass1 { } [Xunit.CollectionDefinition("MyCollection2")] public class CollectionDefinitionClass2 { } """; await Verify.VerifyCodeFixFixAll(before, after, CollectionDefinitionClassesMustBePublicFixer.Key_MakeCollectionDefinitionClassPublic); } [Fact] public async Task ForPartialClassDeclarations_MakesSingleDeclarationPublic() { var before = /* lang=c#-test */ """ [Xunit.CollectionDefinition("MyCollection")] partial class [|CollectionDefinitionClass|] { } partial class CollectionDefinitionClass { } """; var after = /* lang=c#-test */ """ [Xunit.CollectionDefinition("MyCollection")] public partial class CollectionDefinitionClass { } partial class CollectionDefinitionClass { } """; await Verify.VerifyCodeFixFixAll(before, after, CollectionDefinitionClassesMustBePublicFixer.Key_MakeCollectionDefinitionClassPublic); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/ConvertToFactFixTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify_X1003 = CSharpVerifier; using Verify_X1006 = CSharpVerifier; public class ConvertToFactFixTests { [Fact] public async Task FixAll_From_X1003() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] public void [|TestMethod1|](int a) { } [Theory] public void [|TestMethod2|](string b) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod1(int a) { } [Fact] public void TestMethod2(string b) { } } """; await Verify_X1003.VerifyCodeFixFixAll(before, after, ConvertToFactFix.Key_ConvertToFact); } [Fact] public async Task FixAll_From_X1006() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] public void [|TestMethod1|]() { } [Theory] public void [|TestMethod2|]() { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod1() { } [Fact] public void TestMethod2() { } } """; await Verify_X1006.VerifyCodeFixFixAll(before, after, ConvertToFactFix.Key_ConvertToFact); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/ConvertToTheoryFixTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify_X1001 = CSharpVerifier; using Verify_X1005 = CSharpVerifier; public class ConvertToTheoryFixTests { [Fact] public async Task FixAll_From_X1001() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void [|TestMethod1|](int a) { } [Fact] public void [|TestMethod2|](string b) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] public void TestMethod1(int a) { } [Theory] public void TestMethod2(string b) { } } """; await Verify_X1001.VerifyCodeFixFixAll(before, after, ConvertToTheoryFix.Key_ConvertToTheory); } [Fact] public async Task FixAll_From_X1005() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] [InlineData(1)] public void [|TestMethod1|]() { } [Fact] [InlineData("hello")] public void [|TestMethod2|]() { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1)] public void TestMethod1() { } [Theory] [InlineData("hello")] public void TestMethod2() { } } """; await Verify_X1005.VerifyCodeFixFixAll(before, after, ConvertToTheoryFix.Key_ConvertToTheory); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/DataAttributeShouldBeUsedOnATheoryFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class DataAttributeShouldBeUsedOnATheoryFixerTests { [Fact] public async Task FixAll_MarkAsTheory() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [InlineData(1)] public void [|TestMethod1|]() { } [InlineData("hello")] public void [|TestMethod2|]() { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1)] public void TestMethod1() { } [Theory] [InlineData("hello")] public void TestMethod2() { } } """; await Verify.VerifyCodeFixFixAll(before, after, DataAttributeShouldBeUsedOnATheoryFixer.Key_MarkAsTheory); } [Fact] public async Task FixAll_RemoveDataAttributes() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [InlineData(1)] public void [|TestMethod1|]() { } [InlineData("hello")] public void [|TestMethod2|]() { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { public void TestMethod1() { } public void TestMethod2() { } } """; await Verify.VerifyCodeFixFixAll(before, after, DataAttributeShouldBeUsedOnATheoryFixer.Key_RemoveDataAttributes); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/DoNotUseAsyncVoidForTestMethodsFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; namespace Xunit.Analyzers; public class DoNotUseAsyncVoidForTestMethodsFixerTests { [Fact] public async Task FixAll_ConvertsMultipleMethodsToTask() { var beforeV2 = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async void {|xUnit1048:TestMethod1|}() { await Task.Yield(); } [Fact] public async void {|xUnit1048:TestMethod2|}() { await Task.Yield(); } } """; var beforeV3 = beforeV2.Replace("xUnit1048", "xUnit1049"); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod1() { await Task.Yield(); } [Fact] public async Task TestMethod2() { await Task.Yield(); } } """; await Verify.VerifyCodeFixV2FixAll(beforeV2, after, DoNotUseAsyncVoidForTestMethodsFixer.Key_ConvertToTask); await Verify.VerifyCodeFixV3FixAll(beforeV3, after, DoNotUseAsyncVoidForTestMethodsFixer.Key_ConvertToTask); } [Fact] public async Task FixAll_ConvertsMultipleMethodsToValueTask() { var before = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async void {|xUnit1049:TestMethod1|}() { await Task.Yield(); } [Fact] public async void {|xUnit1049:TestMethod2|}() { await Task.Yield(); } } """; var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async ValueTask TestMethod1() { await Task.Yield(); } [Fact] public async ValueTask TestMethod2() { await Task.Yield(); } } """; await Verify.VerifyCodeFixV3FixAll(before, after, DoNotUseAsyncVoidForTestMethodsFixer.Key_ConvertToValueTask); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/DoNotUseConfigureAwaitFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class DoNotUseConfigureAwaitFixerTests { [Fact] public async Task FixAll_RemovesAllConfigureAwaitCalls() { var before = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod1() { await Task.Delay(1).[|ConfigureAwait(false)|]; } [Fact] public async Task TestMethod2() { var task = Task.FromResult(42); await task.[|ConfigureAwait(false)|]; } } """; var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod1() { await Task.Delay(1); } [Fact] public async Task TestMethod2() { var task = Task.FromResult(42); await task; } } """; await Verify.VerifyCodeFixFixAll(before, after, DoNotUseConfigureAwaitFixer.Key_RemoveConfigureAwait); } [Fact] public async Task FixAll_ReplacesAllConfigureAwaitArguments() { var before = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod1() { await Task.Delay(1).[|ConfigureAwait(false)|]; } [Fact] public async Task TestMethod2() { var task = Task.FromResult(42); await task.[|ConfigureAwait(false)|]; } } """; var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod1() { await Task.Delay(1).ConfigureAwait(true); } [Fact] public async Task TestMethod2() { var task = Task.FromResult(42); await task.ConfigureAwait(true); } } """; await Verify.VerifyCodeFixFixAll(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } public class ConfigureAwait_Boolean { public static TheoryData InvalidValues = [ "false", // Literal false "1 == 2", // Logical false (we don't compute) "1 == 1", // Logical true (we don't compute) "booleanVar", // Reference value (we don't do lookup) ]; public class RemoveConfigureAwait { [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task Task_Async(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; await Task.Delay(1).[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var booleanVar = true; await Task.Delay(1); } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_RemoveConfigureAwait); } [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task Task_NonAsync(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var booleanVar = true; Task.Delay(1).[|ConfigureAwait({0})|].GetAwaiter().GetResult(); }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { var booleanVar = true; Task.Delay(1).GetAwaiter().GetResult(); } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_RemoveConfigureAwait); } [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task TaskOfT(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var task = Task.FromResult(42); await task.[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var booleanVar = true; var task = Task.FromResult(42); await task; } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_RemoveConfigureAwait); } [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task ValueTask(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var valueTask = default(ValueTask); await valueTask.[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var booleanVar = true; var valueTask = default(ValueTask); await valueTask; } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_RemoveConfigureAwait); } [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task ValueTaskOfT(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var valueTask = default(ValueTask); await valueTask.[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var booleanVar = true; var valueTask = default(ValueTask); await valueTask; } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_RemoveConfigureAwait); } } public class ReplaceConfigureAwait { [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task Task_Async(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; await Task.Delay(1).[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var booleanVar = true; await Task.Delay(1).ConfigureAwait(true); } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task Task_NonAsync(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var booleanVar = true; Task.Delay(1).[|ConfigureAwait({0})|].GetAwaiter().GetResult(); }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { var booleanVar = true; Task.Delay(1).ConfigureAwait(true).GetAwaiter().GetResult(); } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task TaskOfT(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var task = Task.FromResult(42); await task.[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var booleanVar = true; var task = Task.FromResult(42); await task.ConfigureAwait(true); } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task ValueTask(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var valueTask = default(ValueTask); await valueTask.[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var booleanVar = true; var valueTask = default(ValueTask); await valueTask.ConfigureAwait(true); } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } [Theory] [MemberData(nameof(InvalidValues), MemberType = typeof(ConfigureAwait_Boolean))] public async Task ValueTaskOfT(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var booleanVar = true; var valueTask = default(ValueTask); await valueTask.[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod() { var booleanVar = true; var valueTask = default(ValueTask); await valueTask.ConfigureAwait(true); } } """; await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } } } #if NETCOREAPP public class ConfigureAwait_ConfigureAwaitOptions { public static TheoryData InvalidValues = [ // Literal values /* lang=c#-test */ "ConfigureAwaitOptions.None", /* lang=c#-test */ "ConfigureAwaitOptions.SuppressThrowing", /* lang=c#-test */ "ConfigureAwaitOptions.ForceYielding | ConfigureAwaitOptions.SuppressThrowing", // Reference values (we don't do lookup) /* lang=c#-test */ "enumVar", ]; [Theory] [MemberData(nameof(InvalidValues))] public async Task Task_Async(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; await Task.Delay(1).[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; await Task.Delay(1).ConfigureAwait({0} | ConfigureAwaitOptions.ContinueOnCapturedContext); }} }} """, argumentValue); await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } [Theory] [MemberData(nameof(InvalidValues))] public async Task Task_NonAsync(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; Task.Delay(1).[|ConfigureAwait({0})|].GetAwaiter().GetResult(); }} }} """, argumentValue); var after = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; Task.Delay(1).ConfigureAwait({0} | ConfigureAwaitOptions.ContinueOnCapturedContext).GetAwaiter().GetResult(); }} }} """, argumentValue); await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } [Theory] [MemberData(nameof(InvalidValues))] public async Task TaskOfT(string argumentValue) { var before = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; var task = Task.FromResult(42); await task.[|ConfigureAwait({0})|]; }} }} """, argumentValue); var after = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [Fact] public async Task TestMethod() {{ var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; var task = Task.FromResult(42); await task.ConfigureAwait({0} | ConfigureAwaitOptions.ContinueOnCapturedContext); }} }} """, argumentValue); await Verify.VerifyCodeFix(before, after, DoNotUseConfigureAwaitFixer.Key_ReplaceArgumentValue); } } #endif } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/FactMethodMustNotHaveParametersFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class FactMethodMustNotHaveParametersFixerTests { [Fact] public async Task FixAll_RemovesParametersFromAllMethods() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void [|TestMethod1|](int x) { } [Fact] public void [|TestMethod2|](string a, int b) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod1() { } [Fact] public void TestMethod2() { } } """; await Verify.VerifyCodeFixFixAll(before, after, FactMethodMustNotHaveParametersFixer.Key_RemoveParameters); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/FactMethodShouldNotHaveTestDataFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class FactMethodShouldNotHaveTestDataFixerTests { [Fact] public async Task FixAll_RemovesDataAttributesFromAllMethods() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] [InlineData(1)] public void [|TestMethod1|](int x) { } [Fact] [InlineData("a")] public void [|TestMethod2|](string s) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod1(int x) { } [Fact] public void TestMethod2(string s) { } } """; await Verify.VerifyCodeFixFixAll(before, after, FactMethodShouldNotHaveTestDataFixer.Key_RemoveDataAttributes); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/InlineDataMustMatchTheoryParameters_ExtraValueFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class InlineDataMustMatchTheoryParameters_ExtraValueFixerTests { [Fact] public async Task FixAll_RemovesMultipleUnusedDataValues() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(42, {|xUnit1011:21.12|})] public void TestMethod1(int a) { } [Theory] [InlineData(1, {|xUnit1011:"extra"|})] public void TestMethod2(int a) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(42)] public void TestMethod1(int a) { } [Theory] [InlineData(1)] public void TestMethod2(int a) { } } """; await Verify.VerifyCodeFixFixAll(before, after, InlineDataMustMatchTheoryParameters_ExtraValueFixer.Key_RemoveExtraDataValue); } [Fact] public async Task RemovesUnusedData() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(42, {|xUnit1011:21.12|})] public void TestMethod(int a) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(42)] public void TestMethod(int a) { } } """; await Verify.VerifyCodeFix(before, after, InlineDataMustMatchTheoryParameters_ExtraValueFixer.Key_RemoveExtraDataValue); } [Theory] [InlineData("21.12", "double")] [InlineData(@"""Hello world""", "string")] public async Task AddsParameterWithCorrectType( string value, string valueType) { var before = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ [Theory] [InlineData(42, {{|xUnit1011:{0}|}})] public void TestMethod(int a) {{ }} }} """, value); var after = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ [Theory] [InlineData(42, {0})] public void TestMethod(int a, {1} p) {{ }} }} """, value, valueType); await Verify.VerifyCodeFix(before, after, InlineDataMustMatchTheoryParameters_ExtraValueFixer.Key_AddTheoryParameter); } [Fact] public async Task AddsParameterWithNonConflictingName() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(42, {|xUnit1011:21.12|})] public void TestMethod(int p) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(42, 21.12)] public void TestMethod(int p, double p_2) { } } """; await Verify.VerifyCodeFix(before, after, InlineDataMustMatchTheoryParameters_ExtraValueFixer.Key_AddTheoryParameter); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameterFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameterFixerTests { [Fact] public async Task FixAll_MakesMultipleParametersNullable() { var before = /* lang=c#-test */ """ #nullable enable using Xunit; public class TestClass { [Theory] [InlineData({|xUnit1012:null|}, {|xUnit1012:null|})] public void TestMethod1(int a, int b) { } [Theory] [InlineData(42, {|xUnit1012:null|})] public void TestMethod2(int a, object b) { } } """; var after = /* lang=c#-test */ """ #nullable enable using Xunit; public class TestClass { [Theory] [InlineData(null, null)] public void TestMethod1(int? a, int? b) { } [Theory] [InlineData(42, null)] public void TestMethod2(int a, object? b) { } } """; await Verify.VerifyCodeFixFixAll(LanguageVersion.CSharp8, before, after, InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameterFixer.Key_MakeParameterNullable); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/InlineDataMustMatchTheoryParameters_TooFewValuesFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class InlineDataMustMatchTheoryParameters_TooFewValuesFixerTests { [Theory] [InlineData("bool", "false")] [InlineData("char", "'\\0'")] [InlineData("double", "0D")] [InlineData("float", "0F")] [InlineData("int", "0")] [InlineData("string", "\"\"")] [InlineData("object", "null")] [InlineData("Color", "default(Color)")] public async Task MakesParameterNullable( string valueType, string defaultValue) { var before = string.Format(/* lang=c#-test */ """ using Xunit; public enum Color {{ Red, Green, Blue }} public class TestClass {{ [Theory] [{{|xUnit1009:InlineData|}}] public void TestMethod({0} p) {{ }} }} """, valueType); var after = string.Format(/* lang=c#-test */ """ using Xunit; public enum Color {{ Red, Green, Blue }} public class TestClass {{ [Theory] [InlineData({1})] public void TestMethod({0} p) {{ }} }} """, valueType, defaultValue); await Verify.VerifyCodeFix(before, after, InlineDataMustMatchTheoryParameters_TooFewValuesFixer.Key_AddDefaultValues); } [Fact] public async Task FixAll_AddsDefaultValuesToMultipleAttributes() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [{|xUnit1009:InlineData|}] public void TestMethod1(int x) { } [Theory] [{|xUnit1009:InlineData|}] public void TestMethod2(string a, bool b) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(0)] public void TestMethod1(int x) { } [Theory] [InlineData("", false)] public void TestMethod2(string a, bool b) { } } """; await Verify.VerifyCodeFixFixAll(before, after, InlineDataMustMatchTheoryParameters_TooFewValuesFixer.Key_AddDefaultValues); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/InlineDataShouldBeUniqueWithinTheoryFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class InlineDataShouldBeUniqueWithinTheoryFixerTests { [Fact] public async Task FixAll_RemovesDuplicateData() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1)] [[|InlineData(1)|]] public void TestMethod1(int x) { } [Theory] [InlineData("a")] [[|InlineData("a")|]] public void TestMethod2(string s) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1)] public void TestMethod1(int x) { } [Theory] [InlineData("a")] public void TestMethod2(string s) { } } """; await Verify.VerifyCodeFixFixAll(before, after, InlineDataShouldBeUniqueWithinTheoryFixer.Key_RemoveDuplicateInlineData); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/LocalFunctionsCannotBeTestFunctionsFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class LocalFunctionsCannotBeTestFunctionsFixerTests { [Fact] public async Task FixAll_RemovesAttributesFromAllLocalFunctions() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { public void Method() { [[|Fact|]] void LocalFunction1() { } [[|Theory|]] void LocalFunction2() { } } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { public void Method() { void LocalFunction1() { } void LocalFunction2() { } } } """; await Verify.VerifyCodeFixFixAll(LanguageVersion.CSharp9, before, after, LocalFunctionsCannotBeTestFunctionsFixer.Key_RemoveAttribute); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/MemberDataShouldReferenceValidMember_ExtraValueFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class MemberDataShouldReferenceValidMember_ExtraValueFixerTests { [Fact] public async Task FixAll_RemovesMultipleUnusedDataValues() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData1(int n) => new TheoryData(); public static TheoryData TestData2(int n) => new TheoryData(); [Theory] [MemberData(nameof(TestData1), 42, {|xUnit1036:21.12|})] public void TestMethod1(int a) { } [Theory] [MemberData(nameof(TestData2), 42, {|xUnit1036:99.9|})] public void TestMethod2(int a) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData1(int n) => new TheoryData(); public static TheoryData TestData2(int n) => new TheoryData(); [Theory] [MemberData(nameof(TestData1), 42)] public void TestMethod1(int a) { } [Theory] [MemberData(nameof(TestData2), 42)] public void TestMethod2(int a) { } } """; await Verify.VerifyCodeFixFixAll(before, after, MemberDataShouldReferenceValidMember_ExtraValueFixer.Key_RemoveExtraDataValue); } [Fact] public async Task RemovesUnusedData() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData(int n) => new TheoryData(); [Theory] [MemberData(nameof(TestData), 42, {|xUnit1036:21.12|})] public void TestMethod(int a) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData(int n) => new TheoryData(); [Theory] [MemberData(nameof(TestData), 42)] public void TestMethod(int a) { } } """; await Verify.VerifyCodeFix(before, after, MemberDataShouldReferenceValidMember_ExtraValueFixer.Key_RemoveExtraDataValue); } [Theory] [InlineData("21.12", "double")] [InlineData(@"""Hello world""", "string")] public async Task AddsParameterWithCorrectType( string value, string valueType) { var before = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ public static TheoryData TestData(int n) => new TheoryData(); [Theory] [MemberData(nameof(TestData), 42, {{|xUnit1036:{0}|}})] public void TestMethod(int a) {{ }} }} """, value); var after = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ public static TheoryData TestData(int n, {1} p) => new TheoryData(); [Theory] [MemberData(nameof(TestData), 42, {0})] public void TestMethod(int a) {{ }} }} """, value, valueType); await Verify.VerifyCodeFix(before, after, MemberDataShouldReferenceValidMember_ExtraValueFixer.Key_AddMethodParameter); } [Fact] public async Task AddsParameterWithNonConflictingName() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData(int p) => new TheoryData(); [Theory] [MemberData(nameof(TestData), 42, {|xUnit1036:21.12|})] public void TestMethod(int n) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData(int p, double p_2) => new TheoryData(); [Theory] [MemberData(nameof(TestData), 42, 21.12)] public void TestMethod(int n) { } } """; await Verify.VerifyCodeFix(before, after, MemberDataShouldReferenceValidMember_ExtraValueFixer.Key_AddMethodParameter); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/MemberDataShouldReferenceValidMember_NameOfFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class MemberDataShouldReferenceValidMember_NameOfFixerTests { [Fact] public async Task FixAll_ConvertsAllStringsToNameOf() { var before = /* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData DataSource1 = new TheoryData(); public static TheoryData DataSource2 = new TheoryData(); [Theory] [MemberData({|xUnit1014:"DataSource1"|})] public void TestMethod1(int a) { } [Theory] [MemberData({|xUnit1014:"DataSource2"|})] public void TestMethod2(int a) { } } """; var after = /* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData DataSource1 = new TheoryData(); public static TheoryData DataSource2 = new TheoryData(); [Theory] [MemberData(nameof(DataSource1))] public void TestMethod1(int a) { } [Theory] [MemberData(nameof(DataSource2))] public void TestMethod2(int a) { } } """; await Verify.VerifyCodeFixFixAll(before, after, MemberDataShouldReferenceValidMember_NameOfFixer.Key_UseNameof); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixerTests { [Fact] public async Task FixAll_MakesMultipleParametersNullable() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData1(int n, int k) => new TheoryData(); public static TheoryData TestData2(int n, int k) => new TheoryData(); [Theory] [MemberData(nameof(TestData1), 42, {|xUnit1034:null|})] public void TestMethod1(int a) { } [Theory] [MemberData(nameof(TestData2), 42, {|xUnit1034:null|})] public void TestMethod2(int a) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData1(int n, int? k) => new TheoryData(); public static TheoryData TestData2(int n, int? k) => new TheoryData(); [Theory] [MemberData(nameof(TestData1), 42, null)] public void TestMethod1(int a) { } [Theory] [MemberData(nameof(TestData2), 42, null)] public void TestMethod2(int a) { } } """; await Verify.VerifyCodeFixFixAll(before, after, MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixer.Key_MakeParameterNullable); } [Fact] public async Task MakesParameterNullable() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData(int n, int k) => new TheoryData(); [Theory] [MemberData(nameof(TestData), 42, {|xUnit1034:null|})] public void TestMethod(int a) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { public static TheoryData TestData(int n, int? k) => new TheoryData(); [Theory] [MemberData(nameof(TestData), 42, null)] public void TestMethod(int a) { } } """; await Verify.VerifyCodeFix(before, after, MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixer.Key_MakeParameterNullable); } [Fact] public async Task MakesReferenceParameterNullable() { var before = /* lang=c#-test */ """ #nullable enable using Xunit; public class TestClass { public static TheoryData TestData(int n, string k) => new TheoryData { n }; [Theory] [MemberData(nameof(TestData), 42, {|xUnit1034:null|})] public void TestMethod(int a) { } } """; var after = /* lang=c#-test */ """ #nullable enable using Xunit; public class TestClass { public static TheoryData TestData(int n, string? k) => new TheoryData { n }; [Theory] [MemberData(nameof(TestData), 42, null)] public void TestMethod(int a) { } } """; await Verify.VerifyCodeFix(LanguageVersion.CSharp8, before, after, MemberDataShouldReferenceValidMember_NullShouldNotBeUsedForIncompatibleParameterFixer.Key_MakeParameterNullable); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/MemberDataShouldReferenceValidMember_ParamsForNonMethodFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class MemberDataShouldReferenceValidMember_ParamsForNonMethodFixerTests { [Fact] public async Task FixAll_RemovesParametersFromNonMethodMemberData() { var before = /* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData DataSource1 = new TheoryData(); public static TheoryData DataSource2 = new TheoryData(); [Theory] [MemberData(nameof(DataSource1), {|xUnit1021:"abc", 123|})] public void TestMethod1(int a) { } [Theory] [MemberData(nameof(DataSource2), {|xUnit1021:"xyz"|})] public void TestMethod2(string b) { } } """; var after = /* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData DataSource1 = new TheoryData(); public static TheoryData DataSource2 = new TheoryData(); [Theory] [MemberData(nameof(DataSource1))] public void TestMethod1(int a) { } [Theory] [MemberData(nameof(DataSource2))] public void TestMethod2(string b) { } } """; await Verify.VerifyCodeFixFixAll(before, after, MemberDataShouldReferenceValidMember_ParamsForNonMethodFixer.Key_RemoveArgumentsFromMemberData); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/MemberDataShouldReferenceValidMember_ReturnTypeFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class MemberDataShouldReferenceValidMember_ReturnTypeFixerTests { [Fact] public async Task FixAll_ChangesReturnTypeOnMultipleMembers() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable Data1 => null; public static IEnumerable Data2 => null; [Theory] [{|xUnit1019:MemberData(nameof(Data1))|}] public void TestMethod1(int a) { } [Theory] [{|xUnit1019:MemberData(nameof(Data2))|}] public void TestMethod2(int a) { } } """; var after = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable Data1 => null; public static IEnumerable Data2 => null; [Theory] [{|xUnit1042:MemberData(nameof(Data1))|}] public void TestMethod1(int a) { } [Theory] [{|xUnit1042:MemberData(nameof(Data2))|}] public void TestMethod2(int a) { } } """; await Verify.VerifyCodeFixFixAll(before, after, MemberDataShouldReferenceValidMember_ReturnTypeFixer.Key_ChangeMemberReturnType_ObjectArray); } [Fact] public async Task ChangesReturnType_ObjectArray() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable Data => null; [Theory] [{|xUnit1019:MemberData(nameof(Data))|}] public void TestMethod(int a) { } } """; var after = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable Data => null; [Theory] [{|xUnit1042:MemberData(nameof(Data))|}] public void TestMethod(int a) { } } """; await Verify.VerifyCodeFix(before, after, MemberDataShouldReferenceValidMember_ReturnTypeFixer.Key_ChangeMemberReturnType_ObjectArray); } [Fact] public async Task ChangesReturnType_TheoryDataRow() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable Data => null; [Theory] [{|xUnit1019:MemberData(nameof(Data))|}] public void TestMethod(int a) { } } """; var after = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static IEnumerable Data => null; [Theory] [{|xUnit1042:MemberData(nameof(Data))|}] public void TestMethod(int a) { } } """; await Verify.VerifyCodeFixV3(before, after, MemberDataShouldReferenceValidMember_ReturnTypeFixer.Key_ChangeMemberReturnType_ITheoryDataRow); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/MemberDataShouldReferenceValidMember_StaticFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class MemberDataShouldReferenceValidMember_StaticFixerTests { [Fact] public async Task FixAll_MarksMultipleDataMembersAsStatic() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public TheoryData TestData1 => null; public TheoryData TestData2 => null; [Theory] [{|xUnit1017:MemberData(nameof(TestData1))|}] public void TestMethod1(int x) { } [Theory] [{|xUnit1017:MemberData(nameof(TestData2))|}] public void TestMethod2(string x) { } } """; var after = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData TestData1 => null; public static TheoryData TestData2 => null; [Theory] [MemberData(nameof(TestData1))] public void TestMethod1(int x) { } [Theory] [MemberData(nameof(TestData2))] public void TestMethod2(string x) { } } """; await Verify.VerifyCodeFixFixAll(before, after, MemberDataShouldReferenceValidMember_StaticFixer.Key_MakeMemberStatic); } [Fact] public async Task MarksDataMemberAsStatic() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public TheoryData TestData => null; [Theory] [{|xUnit1017:MemberData(nameof(TestData))|}] public void TestMethod(int x) { } } """; var after = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData TestData => null; [Theory] [MemberData(nameof(TestData))] public void TestMethod(int x) { } } """; await Verify.VerifyCodeFix(before, after, MemberDataShouldReferenceValidMember_StaticFixer.Key_MakeMemberStatic); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/MemberDataShouldReferenceValidMember_VisibilityFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class MemberDataShouldReferenceValidMember_VisibilityFixerTests { [Fact] public async Task FixAll_SetsPublicModifierOnMultipleMembers() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { static TheoryData TestData1 => null; internal static TheoryData TestData2 => null; [Theory] [{|xUnit1016:MemberData(nameof(TestData1))|}] public void TestMethod1(int x) { } [Theory] [{|xUnit1016:MemberData(nameof(TestData2))|}] public void TestMethod2(string x) { } } """; // Roslyn 3.11 inserts a blank line after a member whose accessibility modifiers are changed #if ROSLYN_LATEST var after = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData TestData1 => null; public static TheoryData TestData2 => null; [Theory] [MemberData(nameof(TestData1))] public void TestMethod1(int x) { } [Theory] [MemberData(nameof(TestData2))] public void TestMethod2(string x) { } } """; #else var after = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData TestData1 => null; public static TheoryData TestData2 => null; [Theory] [MemberData(nameof(TestData1))] public void TestMethod1(int x) { } [Theory] [MemberData(nameof(TestData2))] public void TestMethod2(string x) { } } """; #endif await Verify.VerifyCodeFixFixAll(before, after, MemberDataShouldReferenceValidMember_VisibilityFixer.Key_MakeMemberPublic); } [Theory] [InlineData("")] [InlineData("protected ")] [InlineData("internal ")] public async Task SetsPublicModifier(string badModifier) { var before = string.Format(/* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass {{ {0}static TheoryData TestData => null; [Theory] [{{|xUnit1016:MemberData(nameof(TestData))|}}] public void TestMethod(int x) {{ }} }} """, badModifier); var after = /* lang=c#-test */ """ using System.Collections.Generic; using Xunit; public class TestClass { public static TheoryData TestData => null; [Theory] [MemberData(nameof(TestData))] public void TestMethod(int x) { } } """; await Verify.VerifyCodeFix(before, after, MemberDataShouldReferenceValidMember_VisibilityFixer.Key_MakeMemberPublic); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/PublicMethodShouldBeMarkedAsTestFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class PublicMethodShouldBeMarkedAsTestFixerTests { [Fact] public async Task FixAll_AddsFactToAllPublicMethodsWithoutParameters() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { } public void [|TestMethod2|]() { } public void [|TestMethod3|]() { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { } [Fact] public void TestMethod2() { } [Fact] public void TestMethod3() { } } """; await Verify.VerifyCodeFixFixAll(before, after, PublicMethodShouldBeMarkedAsTestFixer.Key_ConvertToFact); } [Fact] public async Task FixAll_AddsTheoryToAllPublicMethodsWithParameters() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { } public void [|TestMethod2|](int _) { } public void [|TestMethod3|](string _) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { } [Theory] public void TestMethod2(int _) { } [Theory] public void TestMethod3(string _) { } } """; await Verify.VerifyCodeFixFixAll(before, after, PublicMethodShouldBeMarkedAsTestFixer.Key_ConvertToTheory); } [Fact] public async Task FixAll_MakesAllMethodsInternal() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { } public void [|TestMethod2|]() { } public void [|TestMethod3|]() { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { } internal void TestMethod2() { } internal void TestMethod3() { } } """; await Verify.VerifyCodeFixFixAll(before, after, PublicMethodShouldBeMarkedAsTestFixer.Key_MakeMethodInternal); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/RemoveMethodParameterFixTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Xunit.Analyzers.Fixes; using Verify_X1022 = CSharpVerifier; using Verify_X1026 = CSharpVerifier; public class RemoveMethodParameterFixTests { [Fact] public async Task X1022_RemoveParamsArray() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1, 2, 3)] public void TestMethod([|params int[] values|]) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1, 2, 3)] public void TestMethod() { } } """; await Verify_X1022.VerifyCodeFix(before, after, RemoveMethodParameterFix.Key_RemoveParameter); } [Fact] public async Task X1026_RemovesUnusedParameter() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1)] public void TestMethod(int [|arg|]) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1)] public void TestMethod() { } } """; await Verify_X1026.VerifyCodeFix(before, after, RemoveMethodParameterFix.Key_RemoveParameter); } [Fact] public async Task X1026_DoesNotCrashWhenParameterDeclarationIsMissing() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1, 1)] public void Test1(int x, {|CS1001:{|CS1031:{|xUnit1026:|})|}|} { var x1 = x; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1, 1)] public void Test1(int x, {|CS1001:{|CS1031:{|xUnit1026:|})|}|} { var x1 = x; } } """; await Verify_X1026.VerifyCodeFix(before, after, RemoveMethodParameterFix.Key_RemoveParameter); } internal class Analyzer_X1022 : TheoryMethodCannotHaveParamsArray { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2(compilation, new Version(2, 1, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/TestClassCannotBeNestedInGenericClassFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class TestClassCannotBeNestedInGenericClassFixerTests { [Fact] public async Task MovesTestClassOutOfGenericParent() { const string before = /* lang=c#-test */ """ public abstract class OpenGenericType { public class [|NestedTestClass|] { [Xunit.Fact] public void TestMethod() { } } } """; const string after = /* lang=c#-test */ """ public abstract class OpenGenericType { } public class NestedTestClass { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyCodeFix(before, after, TestClassCannotBeNestedInGenericClassFixer.Key_ExtractTestClass); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/TestClassMustBePublicFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class TestClassMustBePublicFixerTests { [Fact] public async Task FixAll_MakesAllClassesPublic() { var before = /* lang=c#-test */ """ class [|TestClass1|] { [Xunit.Fact] public void TestMethod() { } } internal class [|TestClass2|] { [Xunit.Fact] public void TestMethod() { } } """; var after = /* lang=c#-test */ """ public class TestClass1 { [Xunit.Fact] public void TestMethod() { } } public class TestClass2 { [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyCodeFixFixAll(before, after, TestClassMustBePublicFixer.Key_MakeTestClassPublic); } [Fact] public async Task ForPartialClassDeclarations_MakesSingleDeclarationPublic() { var before = /* lang=c#-test */ """ partial class [|TestClass|] { [Xunit.Fact] public void TestMethod1() {} } partial class TestClass { [Xunit.Fact] public void TestMethod2() {} } """; var after = /* lang=c#-test */ """ public partial class TestClass { [Xunit.Fact] public void TestMethod1() {} } partial class TestClass { [Xunit.Fact] public void TestMethod2() {} } """; await Verify.VerifyCodeFix(before, after, TestClassMustBePublicFixer.Key_MakeTestClassPublic); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/TestClassShouldHaveTFixtureArgumentFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class TestClassShouldHaveTFixtureArgumentFixerTests { [Fact] public async Task ForClassWithoutField_GenerateFieldAndConstructor() { var before = /* lang=c#-test */ """ public class FixtureData { } public class [|TestClass|]: Xunit.IClassFixture { [Xunit.Fact] public void TestMethod() { } } """; var after = /* lang=c#-test */ """ public class FixtureData { } public class TestClass: Xunit.IClassFixture { private readonly FixtureData _fixtureData; public TestClass(FixtureData fixtureData) { _fixtureData = fixtureData; } [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyCodeFix(before, after, TestClassShouldHaveTFixtureArgumentFixer.Key_GenerateConstructor); } [Fact] public async Task ForGenericTFixture_GenerateFieldAndConstructor() { var before = /* lang=c#-test */ """ public class FixtureData { } public class [|TestClass|]: Xunit.IClassFixture> { [Xunit.Fact] public void TestMethod() { } } """; var after = /* lang=c#-test */ """ public class FixtureData { } public class TestClass: Xunit.IClassFixture> { private readonly FixtureData _fixtureData; public TestClass(FixtureData fixtureData) { _fixtureData = fixtureData; } [Xunit.Fact] public void TestMethod() { } } """; await Verify.VerifyCodeFix(before, after, TestClassShouldHaveTFixtureArgumentFixer.Key_GenerateConstructor); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/TestMethodMustNotHaveMultipleFactAttributesFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class TestMethodMustNotHaveMultipleFactAttributesFixerTests { [Fact] public async Task FixAll_RemovesDuplicateAttributes() { var before = /* lang=c#-test */ """ using Xunit; public class FactDerivedAttribute : FactAttribute { } public class TestClass { [Fact] [{|CS0579:Fact|}] public void [|TestMethod1|]() { } [Fact] [{|CS0579:Fact|}] public void [|TestMethod2|]() { } } """; var after = /* lang=c#-test */ """ using Xunit; public class FactDerivedAttribute : FactAttribute { } public class TestClass { [Fact] public void TestMethod1() { } [Fact] public void TestMethod2() { } } """; await Verify.VerifyCodeFixFixAll(before, after, TestMethodMustNotHaveMultipleFactAttributesFixer.Key_KeepAttribute("Fact")); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/TestMethodShouldNotBeSkippedFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class TestMethodShouldNotBeSkippedFixerTests { [Fact] public async Task FixAll_RemovesSkipFromAllMethods() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact([|Skip = "Don't run this"|])] public void TestMethod1() { } [Fact([|Skip = "Also skipped"|])] public void TestMethod2() { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod1() { } [Fact] public void TestMethod2() { } } """; await Verify.VerifyCodeFixFixAll(before, after, TestMethodShouldNotBeSkippedFixer.Key_RemoveSkipArgument); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/TheoryDataShouldNotUseTheoryDataRowFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class TheoryDataShouldNotUseTheoryDataRowFixerTests { const string myRowSource = /* lang=c#-test */ """ public class MyRow : ITheoryDataRow { public object?[] GetData() { return null; } public bool? Explicit { get; } public string? Label { get; } public string? Skip { get; } public Type? SkipType { get; } public string? SkipUnless { get; } public string? SkipWhen { get; } public string? TestDisplayName { get; } public int? Timeout { get; } public Dictionary>? Traits { get; } } """; [Fact] public async Task FixAll_ConvertsFixableTheoryDataToIEnumerable() { var before = /* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public class TestClass { private [|TheoryData|] field1; private [|TheoryData>|] field2; private [|TheoryData|] field3; public [|TheoryData|] property1 { get; set; } public [|TheoryData>|] property2 { get; set; } public [|TheoryData|] property3 { get; set; } public [|TheoryData|] method1() { [|TheoryData|] data; return null; } public [|TheoryData>|] method2() { [|TheoryData>|] data; return null; } public [|TheoryData|] method3() { [|TheoryData|] data; return null; } } """ + myRowSource; var after = /* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public class TestClass { private IEnumerable field1; private IEnumerable> field2; private IEnumerable field3; public IEnumerable property1 { get; set; } public IEnumerable> property2 { get; set; } public IEnumerable property3 { get; set; } public IEnumerable method1() { IEnumerable data; return null; } public IEnumerable> method2() { IEnumerable> data; return null; } public IEnumerable method3() { IEnumerable data; return null; } } """ + myRowSource; await Verify.VerifyCodeFixV3FixAll(LanguageVersion.CSharp9, before, after, TheoryDataShouldNotUseTheoryDataRowFixer.Key_UseIEnumerable); } [Fact] public async Task AcceptanceTest_Unfixable() { var before = /* lang=c#-test */ """ using System; using System.Collections.Generic; using Xunit; public class TestClass { private [|TheoryData|] field11 = new(); private [|TheoryData>|] field12 = new(); private [|TheoryData|] field13 = new(); private [|TheoryData|] field21; private [|TheoryData, int>|] field22; private [|TheoryData|] field23; public [|TheoryData|] property11 { get; set; } = new(); public [|TheoryData>|] property12 { get; set; } = new(); public [|TheoryData|] property13 { get; set; } = new(); public [|TheoryData|] property21 { get; set; } public [|TheoryData, int>|] property22 { get; set; } public [|TheoryData|] property23 { get; set; } public [|TheoryData|] method11() { [|TheoryData|] data; return null; } public [|TheoryData, int>|] method12() { [|TheoryData, int>|] data; return null; } public [|TheoryData|] method13() { [|TheoryData|] data; return null; } } """ + myRowSource; await Verify.VerifyCodeFixV3(LanguageVersion.CSharp9, before, after: before, TheoryDataShouldNotUseTheoryDataRowFixer.Key_UseIEnumerable); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/TheoryMethodCannotHaveDefaultParameterFixerTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Xunit.Analyzers.Fixes; using Verify_v2_Pre220 = CSharpVerifier; public class TheoryMethodCannotHaveDefaultParameterFixerTests { [Fact] public async Task FixAll_RemovesDefaultParameterValues() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1, "a")] public void TestMethod1(int x [|= 0|], string y [|= "default"|]) { } [Theory] [InlineData(true)] public void TestMethod2(bool b [|= false|]) { } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Theory] [InlineData(1, "a")] public void TestMethod1(int x, string y) { } [Theory] [InlineData(true)] public void TestMethod2(bool b) { } } """; await Verify_v2_Pre220.VerifyCodeFixV2FixAll(before, after, TheoryMethodCannotHaveDefaultParameterFixer.Key_RemoveParameterDefault); } internal class Analyzer : TheoryMethodCannotHaveDefaultParameter { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2(compilation, new Version(2, 1, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X1000/UseCancellationTokenFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class UseCancellationTokenFixerTests { [Fact] public async Task FixAll_AppliesAllFixesInDocument() { var before = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod1() { await [|Task.Delay(1)|]; } [Fact] public async Task TestMethod2() { await [|Task.Delay(1)|]; await [|Task.Delay(2)|]; } [Fact] public void TestMethod3() { [|FunctionWithOverload(42)|]; [|FunctionWithDefaults()|]; } void FunctionWithOverload(int _) { } void FunctionWithOverload(int _1, CancellationToken _2) { } void FunctionWithDefaults(int _1 = 2112, CancellationToken cancellationToken = default(CancellationToken)) { } } """; var after = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task TestMethod1() { await Task.Delay(1, TestContext.Current.CancellationToken); } [Fact] public async Task TestMethod2() { await Task.Delay(1, TestContext.Current.CancellationToken); await Task.Delay(2, TestContext.Current.CancellationToken); } [Fact] public void TestMethod3() { FunctionWithOverload(42, TestContext.Current.CancellationToken); FunctionWithDefaults(cancellationToken: TestContext.Current.CancellationToken); } void FunctionWithOverload(int _) { } void FunctionWithOverload(int _1, CancellationToken _2) { } void FunctionWithDefaults(int _1 = 2112, CancellationToken cancellationToken = default(CancellationToken)) { } } """; await Verify.VerifyCodeFixV3FixAll(before, after, UseCancellationTokenFixer.Key_UseCancellationTokenArgument); } [Fact] public async Task UseCancellationTokenArgument() { var before = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { [|FunctionWithOverload(42)|]; [|FunctionWithOverload(42, default(CancellationToken))|]; [|FunctionWithDefaults()|]; [|FunctionWithDefaults(42)|]; [|FunctionWithDefaults(cancellationToken: default(CancellationToken))|]; [|FunctionWithDefaults(42, cancellationToken: default(CancellationToken))|]; } void FunctionWithOverload(int _) { } void FunctionWithOverload(int _1, CancellationToken _2) { } void FunctionWithDefaults(int _1 = 2112, CancellationToken cancellationToken = default(CancellationToken)) { } } """; var after = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { FunctionWithOverload(42, TestContext.Current.CancellationToken); FunctionWithOverload(42, TestContext.Current.CancellationToken); FunctionWithDefaults(cancellationToken: TestContext.Current.CancellationToken); FunctionWithDefaults(42, TestContext.Current.CancellationToken); FunctionWithDefaults(cancellationToken: TestContext.Current.CancellationToken); FunctionWithDefaults(42, cancellationToken: TestContext.Current.CancellationToken); } void FunctionWithOverload(int _) { } void FunctionWithOverload(int _1, CancellationToken _2) { } void FunctionWithDefaults(int _1 = 2112, CancellationToken cancellationToken = default(CancellationToken)) { } } """; await Verify.VerifyCodeFixV3(before, after, UseCancellationTokenFixer.Key_UseCancellationTokenArgument); } [Fact] public async Task UseCancellationTokenArgument_AliasTestContext() { var before = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using MyContext = Xunit.TestContext; public class TestClass { [Xunit.Fact] public void TestMethod() { [|Function()|]; } void Function(CancellationToken token = default(CancellationToken)) { } } """; var after = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using MyContext = Xunit.TestContext; public class TestClass { [Xunit.Fact] public void TestMethod() { Function(MyContext.Current.CancellationToken); } void Function(CancellationToken token = default(CancellationToken)) { } } """; await Verify.VerifyCodeFixV3(before, after, UseCancellationTokenFixer.Key_UseCancellationTokenArgument); } [Fact] public async Task UseCancellationTokenArgument_ParamsArgument() { var before = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using MyContext = Xunit.TestContext; public class TestClass { [Xunit.Fact] public void TestMethod() { [|Function(1, 2, 3)|]; } void Function(params int[] integers) { } void Function(int[] integers, CancellationToken token = default(CancellationToken)) { } } """; var after = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using MyContext = Xunit.TestContext; public class TestClass { [Xunit.Fact] public void TestMethod() { Function(new int[] { 1, 2, 3 }, MyContext.Current.CancellationToken); } void Function(params int[] integers) { } void Function(int[] integers, CancellationToken token = default(CancellationToken)) { } } """; await Verify.VerifyCodeFixV3(before, after, UseCancellationTokenFixer.Key_UseCancellationTokenArgument); } [Fact] public async Task UseCancellationTokenArgument_ParamsArgumentAfterRegularArguments() { var before = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using MyContext = Xunit.TestContext; public class TestClass { [Xunit.Fact] public void TestMethod() { [|Function("hello", System.Guid.NewGuid(), System.Guid.NewGuid())|]; } void Function(string str, params System.Guid[] guids) { } void Function(string str, System.Guid[] guids, CancellationToken token = default(CancellationToken)) { } } """; var after = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using MyContext = Xunit.TestContext; public class TestClass { [Xunit.Fact] public void TestMethod() { Function("hello", new System.Guid[] { System.Guid.NewGuid(), System.Guid.NewGuid() }, MyContext.Current.CancellationToken); } void Function(string str, params System.Guid[] guids) { } void Function(string str, System.Guid[] guids, CancellationToken token = default(CancellationToken)) { } } """; await Verify.VerifyCodeFixV3(before, after, UseCancellationTokenFixer.Key_UseCancellationTokenArgument); } [Fact] public async Task UseCancellationTokenArgument_ParamsArgumentWithNoValues() { var before = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using MyContext = Xunit.TestContext; public class TestClass { [Xunit.Fact] public void TestMethod() { [|Function()|]; } void Function(params int[] integers) { } void Function(int[] integers, CancellationToken token = default(CancellationToken)) { } } """; var after = /* lang=c#-test */ """ using System.Threading; using System.Threading.Tasks; using MyContext = Xunit.TestContext; public class TestClass { [Xunit.Fact] public void TestMethod() { Function(new int[] { }, MyContext.Current.CancellationToken); } void Function(params int[] integers) { } void Function(int[] integers, CancellationToken token = default(CancellationToken)) { } } """; await Verify.VerifyCodeFixV3(before, after, UseCancellationTokenFixer.Key_UseCancellationTokenArgument); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertCollectionContainsShouldNotUseBoolCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertCollectionContainsShouldNotUseBoolCheckFixerTests { const string template = /* lang=c#-test */ """ using System; using System.Linq; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var items = new[] {{ "a", "b", "c" }}; {0}; }} }} """; [Theory] [InlineData( /* lang=c#-test */ @"[|Assert.True(items.Contains(""b""))|]", /* lang=c#-test */ @"Assert.Contains(""b"", items)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(items.Contains(""b"", StringComparer.Ordinal))|]", /* lang=c#-test */ @"Assert.Contains(""b"", items, StringComparer.Ordinal)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(items.Contains(""b"", null))|]", /* lang=c#-test */ @"Assert.Contains(""b"", items)")] [InlineData( /* lang=c#-test */ @"[|Assert.False(items.Contains(""b""))|]", /* lang=c#-test */ @"Assert.DoesNotContain(""b"", items)")] [InlineData( /* lang=c#-test */ @"[|Assert.False(items.Contains(""b"", StringComparer.Ordinal))|]", /* lang=c#-test */ @"Assert.DoesNotContain(""b"", items, StringComparer.Ordinal)")] [InlineData( /* lang=c#-test */ @"[|Assert.False(items.Contains(""b"", null))|]", /* lang=c#-test */ @"Assert.DoesNotContain(""b"", items)")] public async Task ReplacesBooleanAssert( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertCollectionContainsShouldNotUseBoolCheckFixer.Key_UseAlternateAssert); } [Fact] public async Task FixAll_ReplacesAllBooleanAsserts() { var before = /* lang=c#-test */ """ using System; using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var items = new[] { "a", "b", "c" }; [|Assert.True(items.Contains("b"))|]; [|Assert.False(items.Contains("b"))|]; } } """; var after = /* lang=c#-test */ """ using System; using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var items = new[] { "a", "b", "c" }; Assert.Contains("b", items); Assert.DoesNotContain("b", items); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertCollectionContainsShouldNotUseBoolCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEmptyCollectionCheckShouldNotBeUsedFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEmptyCollectionCheckShouldNotBeUsedFixerTests { [Fact] public async Task FixAll_UsesEmptyCheck() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var collection1 = new[] { 1, 2, 3 }; var collection2 = new[] { 4, 5, 6 }; [|Assert.Collection(collection1)|]; [|Assert.Collection(collection2)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var collection1 = new[] { 1, 2, 3 }; var collection2 = new[] { 4, 5, 6 }; Assert.Empty(collection1); Assert.Empty(collection2); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEmptyCollectionCheckShouldNotBeUsedFixer.Key_UseAssertEmpty); } [Fact] public async Task FixAll_AddsElementInspector() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var collection1 = new[] { 1, 2, 3 }; var collection2 = new[] { 4, 5, 6 }; [|Assert.Collection(collection1)|]; [|Assert.Collection(collection2)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var collection1 = new[] { 1, 2, 3 }; var collection2 = new[] { 4, 5, 6 }; Assert.Collection(collection1, x => { }); Assert.Collection(collection2, x => { }); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEmptyCollectionCheckShouldNotBeUsedFixer.Key_AddElementInspector); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixerTests { const string template = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var list = new[] {{ -1, 0, 1, 2 }}; {0}; }} public bool IsEven(int num) => num % 2 == 0; }} """; [Theory] [InlineData( /* lang=c#-test */ "{|xUnit2029:Assert.Empty(list.Where(f => f > 0))|}", /* lang=c#-test */ "Assert.DoesNotContain(list, f => f > 0)")] [InlineData( /* lang=c#-test */ "{|xUnit2029:Assert.Empty(list.Where(n => n == 1))|}", /* lang=c#-test */ "Assert.DoesNotContain(list, n => n == 1)")] [InlineData( /* lang=c#-test */ "{|xUnit2029:Assert.Empty(list.Where(IsEven))|}", /* lang=c#-test */ "Assert.DoesNotContain(list, IsEven)")] public async Task FixerReplacesAssertEmptyWithAssertDoesNotContain( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixer.Key_UseDoesNotContain); } [Theory] [InlineData( /* lang=c#-test */ "{|xUnit2030:Assert.NotEmpty(list.Where(f => f > 0))|}", /* lang=c#-test */ "Assert.Contains(list, f => f > 0)")] [InlineData( /* lang=c#-test */ "{|xUnit2030:Assert.NotEmpty(list.Where(n => n == 1))|}", /* lang=c#-test */ "Assert.Contains(list, n => n == 1)")] [InlineData( /* lang=c#-test */ "{|xUnit2030:Assert.NotEmpty(list.Where(IsEven))|}", /* lang=c#-test */ "Assert.Contains(list, IsEven)")] public async Task FixerReplacesAssertNotEmptyWithAssertContains( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixer.Key_UseContains); } [Fact] public async Task FixAll_ReplacesAssertEmptyWithDoesNotContain() { var before = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var list = new[] { -1, 0, 1, 2 }; {|xUnit2029:Assert.Empty(list.Where(f => f > 0))|}; {|xUnit2029:Assert.Empty(list.Where(n => n == 1))|}; } } """; var after = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var list = new[] { -1, 0, 1, 2 }; Assert.DoesNotContain(list, f => f > 0); Assert.DoesNotContain(list, n => n == 1); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixer.Key_UseDoesNotContain); } [Fact] public async Task FixAll_ReplacesAssertNotEmptyWithContains() { var before = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var list = new[] { -1, 0, 1, 2 }; {|xUnit2030:Assert.NotEmpty(list.Where(f => f > 0))|}; {|xUnit2030:Assert.NotEmpty(list.Where(n => n == 1))|}; } } """; var after = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var list = new[] { -1, 0, 1, 2 }; Assert.Contains(list, f => f > 0); Assert.Contains(list, n => n == 1); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEmptyOrNotEmptyShouldNotBeUsedForContainsChecksFixer.Key_UseContains); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixerTests { [Fact] public async Task FixAll_ReplacesAllAsserts() { var before = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var collection = new[] { 1, 2, 3 }; [|Assert.True(collection.Any(x => x == 2))|]; [|Assert.False(collection.Any(x => x == 2))|]; } } """; var after = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var collection = new[] { 1, 2, 3 }; Assert.Contains(collection, x => x == 2); Assert.DoesNotContain(collection, x => x == 2); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEqualGenericShouldNotBeUsedForStringValueFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEqualGenericShouldNotBeUsedForStringValueFixerTests { [Fact] public async Task FixAll_RemovesGenericFromAllAsserts() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { string result1 = "foo"; string result2 = "bar"; [|Assert.Equal("foo", result1)|]; [|Assert.StrictEqual("bar", result2)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { string result1 = "foo"; string result2 = "bar"; Assert.Equal("foo", result1); Assert.Equal("bar", result2); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEqualGenericShouldNotBeUsedForStringValueFixer.Key_UseStringAssertEqual); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEqualLiteralValueShouldBeFirstFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEqualLiteralValueShouldBeFirstFixerTests { const string Template = /* lang=c#-test */ """ using System.Collections.Generic; public class TestClass {{ [Xunit.Fact] public void TestMethod() {{ var i = 0; [|Xunit.{0}|]; }} }} """; [Theory] [InlineData( /* lang=c#-test */ "Assert.Equal(i, 0)", /* lang=c#-test */ "Assert.Equal(0, i)")] [InlineData( /* lang=c#-test */ "Assert.Equal(actual: 0, expected: i)", /* lang=c#-test */ "Assert.Equal(actual: i, expected: 0)")] [InlineData( /* lang=c#-test */ "Assert.Equal(expected: i, actual: 0)", /* lang=c#-test */ "Assert.Equal(expected: 0, actual: i)")] [InlineData( /* lang=c#-test */ "Assert.Equal(comparer: default(IEqualityComparer), actual: 0, expected: i)", /* lang=c#-test */ "Assert.Equal(comparer: default(IEqualityComparer), actual: i, expected: 0)")] [InlineData( /* lang=c#-test */ "Assert.Equal(comparer: (x, y) => true, actual: 0, expected: i)", /* lang=c#-test */ "Assert.Equal(comparer: (x, y) => true, actual: i, expected: 0)")] [InlineData( /* lang=c#-test */ "Assert.Equal(expected: i, 0)", /* lang=c#-test */ "Assert.Equal(expected: 0, i)", LanguageVersion.CSharp7_2)] public async Task SwapArguments( string beforeAssert, string afterAssert, LanguageVersion? languageVersion = null) { var before = string.Format(Template, beforeAssert); var after = string.Format(Template, afterAssert); if (languageVersion.HasValue) await Verify.VerifyCodeFix(languageVersion.Value, before, after, AssertEqualLiteralValueShouldBeFirstFixer.Key_SwapArguments); else await Verify.VerifyCodeFix(before, after, AssertEqualLiteralValueShouldBeFirstFixer.Key_SwapArguments); } [Fact] public async Task FixAll_SwapsAllArguments() { var before = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { var i = 0; var j = 1; [|Xunit.Assert.Equal(i, 0)|]; [|Xunit.Assert.Equal(j, 1)|]; } } """; var after = /* lang=c#-test */ """ public class TestClass { [Xunit.Fact] public void TestMethod() { var i = 0; var j = 1; Xunit.Assert.Equal(0, i); Xunit.Assert.Equal(1, j); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEqualLiteralValueShouldBeFirstFixer.Key_SwapArguments); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEqualPrecisionShouldBeInRangeFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEqualPrecisionShouldBeInRangeFixerTests { [Fact] public async Task FixAll_ChangesPrecisionToValidRange() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { Assert.Equal(10.1d, 10.2d, [|-1|]); Assert.Equal(10.1m, 10.2m, [|29|]); } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { Assert.Equal(10.1d, 10.2d, 0); Assert.Equal(10.1m, 10.2m, 28); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEqualPrecisionShouldBeInRangeFixer.Key_UsePrecision); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEqualShouldNotBeUsedForBoolLiteralCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEqualShouldNotBeUsedForBoolLiteralCheckFixerTests { const string template = /* lang=c#-test */ """ using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var actual = true; {0}; }} }} """; [Theory] [InlineData( /* lang=c#-test */ "[|Assert.Equal(false, actual)|]", /* lang=c#-test */ "Assert.False(actual)")] [InlineData( /* lang=c#-test */ "[|Assert.Equal(true, actual)|]", /* lang=c#-test */ "Assert.True(actual)")] [InlineData( /* lang=c#-test */ "[|Assert.StrictEqual(false, actual)|]", /* lang=c#-test */ "Assert.False(actual)")] [InlineData( /* lang=c#-test */ "[|Assert.StrictEqual(true, actual)|]", /* lang=c#-test */ "Assert.True(actual)")] [InlineData( /* lang=c#-test */ "[|Assert.NotEqual(false, actual)|]", /* lang=c#-test */ "Assert.True(actual)")] [InlineData( /* lang=c#-test */ "[|Assert.NotEqual(true, actual)|]", /* lang=c#-test */ "Assert.False(actual)")] [InlineData( /* lang=c#-test */ "[|Assert.NotStrictEqual(false, actual)|]", /* lang=c#-test */ "Assert.True(actual)")] [InlineData( /* lang=c#-test */ "[|Assert.NotStrictEqual(true, actual)|]", /* lang=c#-test */ "Assert.False(actual)")] public async Task ConvertsToBooleanAssert( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer.Key_UseAlternateAssert); } [Fact] public async Task FixAll_ConvertsAllToBooleanAsserts() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var actual = true; [|Assert.Equal(false, actual)|]; [|Assert.Equal(true, actual)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var actual = true; Assert.False(actual); Assert.True(actual); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEqualShouldNotBeUsedForCollectionSizeCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEqualShouldNotBeUsedForCollectionSizeCheckFixerTests { const string template = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var data = new[] {{ 1, 2, 3 }}; {0}; }} }} """; [Theory] [InlineData( /* lang=c#-test */ "[|Assert.Equal(1, data.Count())|]", /* lang=c#-test */ "Assert.Single(data)")] [InlineData( /* lang=c#-test */ "[|Assert.Equal(0, data.Count())|]", /* lang=c#-test */ "Assert.Empty(data)")] [InlineData( /* lang=c#-test */ "[|Assert.NotEqual(0, data.Count())|]", /* lang=c#-test */ "Assert.NotEmpty(data)")] public async Task ReplacesCollectionCountWithAppropriateAssert( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer.Key_UseAlternateAssert); } [Fact] public async Task FixAll_ReplacesAllCollectionSizeChecks() { var before = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = new[] { 1, 2, 3 }; [|Assert.Equal(1, data.Count())|]; [|Assert.Equal(0, data.Count())|]; } } """; var after = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = new[] { 1, 2, 3 }; Assert.Single(data); Assert.Empty(data); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEqualShouldNotBeUsedForNullCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEqualShouldNotBeUsedForNullCheckFixerTests { const string template = /* lang=c#-test */ """ using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ int? data = 1; {0}; }} }} """; [Theory] [InlineData( /* lang=c#-test */ "[|Assert.Equal(null, data)|]", /* lang=c#-test */ "Assert.Null(data)")] [InlineData( /* lang=c#-test */ "[|Assert.StrictEqual(null, data)|]", /* lang=c#-test */ "Assert.Null(data)")] [InlineData( /* lang=c#-test */ "[|Assert.NotEqual(null, data)|]", /* lang=c#-test */ "Assert.NotNull(data)")] [InlineData( /* lang=c#-test */ "[|Assert.NotStrictEqual(null, data)|]", /* lang=c#-test */ "Assert.NotNull(data)")] public async Task ConvertsToAppropriateNullAssert( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertEqualShouldNotBeUsedForNullCheckFixer.Key_UseAlternateAssert); } [Fact] public async Task FixAll_ReplacesAllNullChecks() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { int? data = 1; [|Assert.Equal(null, data)|]; [|Assert.NotEqual(null, data)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { int? data = 1; Assert.Null(data); Assert.NotNull(data); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEqualShouldNotBeUsedForNullCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertEqualsShouldNotBeUsedFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertEqualsShouldNotBeUsedFixerTests { [Fact] public async Task FixAll_ReplacesAllEqualsAndReferenceEqualsCalls() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = 1; {|CS0619:[|Assert.Equals(1, data)|]|}; {|CS0619:[|Assert.ReferenceEquals(1, data)|]|}; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = 1; Assert.Equal(1, data); Assert.Same(1, data); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertEqualsShouldNotBeUsedFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertIsTypeShouldNotBeUsedForAbstractTypeFixerTests.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; using Verify_v2_Pre2_9_3 = CSharpVerifier; using Verify_v3_Pre0_6_0 = CSharpVerifier; public class AssertIsTypeShouldNotBeUsedForAbstractTypeFixerTests { [Fact] public async Task Conversions_WithoutExactMatch() { var before = /* lang=c#-test */ """ using System; using Xunit; public abstract class TestClass { [Fact] public void TestMethod() { var data = new object(); [|Assert.IsType(data)|]; [|Assert.IsType(data)|]; [|Assert.IsNotType(data)|]; [|Assert.IsNotType(data)|]; } } """; var after = /* lang=c#-test */ """ using System; using Xunit; public abstract class TestClass { [Fact] public void TestMethod() { var data = new object(); Assert.IsAssignableFrom(data); Assert.IsAssignableFrom(data); Assert.IsNotAssignableFrom(data); Assert.IsNotAssignableFrom(data); } } """; await Verify_v2_Pre2_9_3.VerifyCodeFix(before, after, AssertIsTypeShouldNotBeUsedForAbstractTypeFixer.Key_UseAlternateAssert); await Verify_v3_Pre0_6_0.VerifyCodeFix(before, after, AssertIsTypeShouldNotBeUsedForAbstractTypeFixer.Key_UseAlternateAssert); } [Fact] public async Task Conversions_WithExactMatch() { var before = /* lang=c#-test */ """ using System; using Xunit; public abstract class TestClass { [Fact] public void TestMethod() { var data = new object(); [|Assert.IsType(data)|]; [|Assert.IsType(data, true)|]; [|Assert.IsType(data, exactMatch: true)|]; [|Assert.IsType(data)|]; [|Assert.IsType(data, true)|]; [|Assert.IsType(data, exactMatch: true)|]; [|Assert.IsNotType(data)|]; [|Assert.IsNotType(data, true)|]; [|Assert.IsNotType(data, exactMatch: true)|]; [|Assert.IsNotType(data)|]; [|Assert.IsNotType(data, true)|]; [|Assert.IsNotType(data, exactMatch: true)|]; } } """; var after = /* lang=c#-test */ """ using System; using Xunit; public abstract class TestClass { [Fact] public void TestMethod() { var data = new object(); Assert.IsType(data, exactMatch: false); Assert.IsType(data, exactMatch: false); Assert.IsType(data, exactMatch: false); Assert.IsType(data, exactMatch: false); Assert.IsType(data, exactMatch: false); Assert.IsType(data, exactMatch: false); Assert.IsNotType(data, exactMatch: false); Assert.IsNotType(data, exactMatch: false); Assert.IsNotType(data, exactMatch: false); Assert.IsNotType(data, exactMatch: false); Assert.IsNotType(data, exactMatch: false); Assert.IsNotType(data, exactMatch: false); } } """; await Verify.VerifyCodeFix(before, after, AssertIsTypeShouldNotBeUsedForAbstractTypeFixer.Key_UseAlternateAssert); } [Fact] public async Task FixAll_ReplacesAllAbstractTypeChecks() { var before = /* lang=c#-test */ """ using System; using Xunit; public abstract class TestClass { [Fact] public void TestMethod() { var data = new object(); [|Assert.IsType(data)|]; [|Assert.IsType(data, true)|]; [|Assert.IsNotType(data)|]; [|Assert.IsNotType(data, exactMatch: true)|]; } } """; var after = /* lang=c#-test */ """ using System; using Xunit; public abstract class TestClass { [Fact] public void TestMethod() { var data = new object(); Assert.IsType(data, exactMatch: false); Assert.IsType(data, exactMatch: false); Assert.IsNotType(data, exactMatch: false); Assert.IsNotType(data, exactMatch: false); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertIsTypeShouldNotBeUsedForAbstractTypeFixer.Key_UseAlternateAssert); } internal class Analyzer_v2_Pre2_9_3 : AssertIsTypeShouldNotBeUsedForAbstractType { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2(compilation, new Version(2, 9, 2)); } internal class Analyzer_v3_Pre0_6_0 : AssertIsTypeShouldNotBeUsedForAbstractType { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV3(compilation, new Version(0, 5, 999)); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertNullShouldNotBeCalledOnValueTypesFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertNullShouldNotBeCalledOnValueTypesFixerTests { [Fact] public async Task ForValueTypeNullAssert_RemovesAssertion() { const string before = /* lang=c#-test */ """ using Xunit; public class Tests { [Fact] public void TestMethod() { int i = 1; [|Assert.NotNull(i)|]; } } """; const string after = /* lang=c#-test */ """ using Xunit; public class Tests { [Fact] public void TestMethod() { int i = 1; } } """; await Verify.VerifyCodeFix(before, after, AssertNullShouldNotBeCalledOnValueTypesFixer.Key_RemoveAssert); } // https://github.com/xunit/xunit/issues/1753 [Fact] public async Task ForAssertionWithTrivia_RemovesAssertionAndLeavesLeadingTriviaInPlace() { const string before = /* lang=c#-test */ """ using System; using Xunit; namespace XUnitTestProject1 { public class UnitTest1 { [Fact] public void Test1() { int i = 1; // I am a comment which gets deleted by the quick fix // Assert [|Assert.NotNull(i)|]; Assert.Null(null); } } } """; const string after = /* lang=c#-test */ """ using System; using Xunit; namespace XUnitTestProject1 { public class UnitTest1 { [Fact] public void Test1() { int i = 1; // I am a comment which gets deleted by the quick fix // Assert Assert.Null(null); } } } """; await Verify.VerifyCodeFix(before, after, AssertNullShouldNotBeCalledOnValueTypesFixer.Key_RemoveAssert); } [Fact] public async Task FixAll_RemovesAllValueTypeNullAssertions() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { int i = 1; bool b = true; [|Assert.NotNull(i)|]; [|Assert.NotNull(b)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { int i = 1; bool b = true; } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertNullShouldNotBeCalledOnValueTypesFixer.Key_RemoveAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertRegexMatchShouldNotUseBoolLiteralCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertRegexMatchShouldNotUseBoolLiteralCheckFixerTests { [Fact] public async Task FixAll_ReplacesAllBooleanRegexChecks() { var before = /* lang=c#-test */ """ using System.Text.RegularExpressions; using Xunit; public class TestClass { [Fact] public void TestMethod() { var result = "foo bar baz"; [|Assert.True(Regex.IsMatch(result, "foo (.*?) baz"))|]; [|Assert.False(Regex.IsMatch(result, "foo (.*?) baz"))|]; } } """; var after = /* lang=c#-test */ """ using System.Text.RegularExpressions; using Xunit; public class TestClass { [Fact] public void TestMethod() { var result = "foo bar baz"; Assert.Matches("foo (.*?) baz", result); Assert.DoesNotMatch("foo (.*?) baz", result); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertRegexMatchShouldNotUseBoolLiteralCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertSameShouldNotBeCalledOnValueTypesFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertSameShouldNotBeCalledOnValueTypesFixerTests { [Fact] public async Task FixAll_ReplacesAllSameCallsWithEqual() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = 1; [|Assert.Same(1, data)|]; [|Assert.NotSame(1, data)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = 1; Assert.Equal(1, data); Assert.NotEqual(1, data); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertSameShouldNotBeCalledOnValueTypesFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertSingleShouldBeUsedForSingleParameterFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertSingleShouldBeUsedForSingleParameterFixerTests { [Fact] public async ValueTask CannotFixInsideLambda() { var source = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public static void TestMethod() { Record.Exception( () => [|Assert.Collection(default(IEnumerable), item => Assert.True(false))|] ); } } """; await Verify.VerifyCodeFix(LanguageVersion.CSharp8, source, source, AssertSingleShouldBeUsedForSingleParameterFixer.Key_UseSingleMethod); } [Fact] public async ValueTask EnumerableAcceptanceTest() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task WithInlineLambda() { [|Assert.Collection(default(IEnumerable), item => Assert.NotNull(item))|]; await [|Assert.CollectionAsync(default(IEnumerable>), async item => Assert.Equal(42, await item))|]; } [Fact] public async Task WithOneStatementLambda() { [|Assert.Collection(default(IEnumerable), item => { Assert.NotNull(item); })|]; await [|Assert.CollectionAsync(default(IEnumerable>), async item => { Assert.Equal(42, await item); })|]; } [Fact] public async Task WithTwoStatementLambda() { [|Assert.Collection(default(IEnumerable), item => { Assert.NotNull(item); Assert.NotNull(item); })|]; await [|Assert.CollectionAsync(default(IEnumerable>), async item => { Assert.Equal(42, await item); Assert.Equal(42, await item); })|]; } [Fact] public async Task WithMultiLineLambda() { [|Assert.Collection(default(IEnumerable), item => { if (item != null) Assert.NotNull(item); })|]; await [|Assert.CollectionAsync(default(IEnumerable>), async item => { if (item != null) Assert.Equal(42, await item); })|]; } [Fact] public async Task WithNameCollision() { var item = 42; [|Assert.Collection(default(IEnumerable), item => Assert.NotNull(item))|]; await [|Assert.CollectionAsync(default(IEnumerable>), async item => Assert.Equal(2112, await item))|]; } [Fact] public async Task WithInspector() { [|Assert.Collection(default(IEnumerable), ElementInspector)|]; await [|Assert.CollectionAsync(default(IEnumerable>), AsyncElementInspector)|]; } void ElementInspector(object obj) { } Task AsyncElementInspector(Task obj) => Task.CompletedTask; } """; var after = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task WithInlineLambda() { var item = Assert.Single(default(IEnumerable)); Assert.NotNull(item); var item_2 = Assert.Single(default(IEnumerable>)); Assert.Equal(42, await item_2); } [Fact] public async Task WithOneStatementLambda() { var item = Assert.Single(default(IEnumerable)); Assert.NotNull(item); var item_2 = Assert.Single(default(IEnumerable>)); Assert.Equal(42, await item_2); } [Fact] public async Task WithTwoStatementLambda() { var item = Assert.Single(default(IEnumerable)); Assert.NotNull(item); Assert.NotNull(item); var item_2 = Assert.Single(default(IEnumerable>)); Assert.Equal(42, await item_2); Assert.Equal(42, await item_2); } [Fact] public async Task WithMultiLineLambda() { var item = Assert.Single(default(IEnumerable)); if (item != null) Assert.NotNull(item); var item_2 = Assert.Single(default(IEnumerable>)); if (item_2 != null) Assert.Equal(42, await item_2); } [Fact] public async Task WithNameCollision() { var item = 42; var item_2 = Assert.Single(default(IEnumerable)); Assert.NotNull(item_2); var item_3 = Assert.Single(default(IEnumerable>)); Assert.Equal(2112, await item_3); } [Fact] public async Task WithInspector() { var item = Assert.Single(default(IEnumerable)); ElementInspector(item); var item_2 = Assert.Single(default(IEnumerable>)); await AsyncElementInspector(item_2); } void ElementInspector(object obj) { } Task AsyncElementInspector(Task obj) => Task.CompletedTask; } """; await Verify.VerifyCodeFix(LanguageVersion.CSharp8, before, after, AssertSingleShouldBeUsedForSingleParameterFixer.Key_UseSingleMethod); } #if NETCOREAPP3_0_OR_GREATER [Fact] public async ValueTask AsyncEnumerableAcceptanceTest() { var before = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task WithInlineLambda() { [|Assert.Collection(default(IAsyncEnumerable), item => Assert.NotNull(item))|]; await [|Assert.CollectionAsync(default(IAsyncEnumerable>), async item => Assert.Equal(42, await item))|]; } [Fact] public async Task WithOneStatementLambda() { [|Assert.Collection(default(IAsyncEnumerable), item => { Assert.NotNull(item); })|]; await [|Assert.CollectionAsync(default(IAsyncEnumerable>), async item => { Assert.Equal(42, await item); })|]; } [Fact] public async Task WithTwoStatementLambda() { [|Assert.Collection(default(IAsyncEnumerable), item => { Assert.NotNull(item); Assert.NotNull(item); })|]; await [|Assert.CollectionAsync(default(IAsyncEnumerable>), async item => { Assert.Equal(42, await item); Assert.Equal(42, await item); })|]; } [Fact] public async Task WithMultiLineLambda() { [|Assert.Collection(default(IAsyncEnumerable), item => { if (item != null) Assert.NotNull(item); })|]; await [|Assert.CollectionAsync(default(IAsyncEnumerable>), async item => { if (item != null) Assert.Equal(42, await item); })|]; } [Fact] public async Task WithNameCollision() { var item = 42; [|Assert.Collection(default(IAsyncEnumerable), item => Assert.NotNull(item))|]; await [|Assert.CollectionAsync(default(IAsyncEnumerable>), async item => Assert.Equal(42, await item))|]; } [Fact] public async Task WithInspector() { [|Assert.Collection(default(IAsyncEnumerable), ElementInspector)|]; await [|Assert.CollectionAsync(default(IAsyncEnumerable>), AsyncElementInspector)|]; } void ElementInspector(object obj) { } Task AsyncElementInspector(Task obj) => Task.CompletedTask; } """; var after = /* lang=c#-test */ """ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public async Task WithInlineLambda() { var item = Assert.Single(default(IAsyncEnumerable)); Assert.NotNull(item); var item_2 = Assert.Single(default(IAsyncEnumerable>)); Assert.Equal(42, await item_2); } [Fact] public async Task WithOneStatementLambda() { var item = Assert.Single(default(IAsyncEnumerable)); Assert.NotNull(item); var item_2 = Assert.Single(default(IAsyncEnumerable>)); Assert.Equal(42, await item_2); } [Fact] public async Task WithTwoStatementLambda() { var item = Assert.Single(default(IAsyncEnumerable)); Assert.NotNull(item); Assert.NotNull(item); var item_2 = Assert.Single(default(IAsyncEnumerable>)); Assert.Equal(42, await item_2); Assert.Equal(42, await item_2); } [Fact] public async Task WithMultiLineLambda() { var item = Assert.Single(default(IAsyncEnumerable)); if (item != null) Assert.NotNull(item); var item_2 = Assert.Single(default(IAsyncEnumerable>)); if (item_2 != null) Assert.Equal(42, await item_2); } [Fact] public async Task WithNameCollision() { var item = 42; var item_2 = Assert.Single(default(IAsyncEnumerable)); Assert.NotNull(item_2); var item_3 = Assert.Single(default(IAsyncEnumerable>)); Assert.Equal(42, await item_3); } [Fact] public async Task WithInspector() { var item = Assert.Single(default(IAsyncEnumerable)); ElementInspector(item); var item_2 = Assert.Single(default(IAsyncEnumerable>)); await AsyncElementInspector(item_2); } void ElementInspector(object obj) { } Task AsyncElementInspector(Task obj) => Task.CompletedTask; } """; await Verify.VerifyCodeFix(LanguageVersion.CSharp8, before, after, AssertSingleShouldBeUsedForSingleParameterFixer.Key_UseSingleMethod); } #endif // NETCOREAPP3_0_OR_GREATER } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertSingleShouldUseTwoArgumentCallFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertSingleShouldUseTwoArgumentCallFixerTests { const string template = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var list = new[] {{ -1, 0, 1, 2 }}; {0}; }} public bool IsEven(int num) => num % 2 == 0; }} """; [Theory] [InlineData( /* lang=c#-test */ "[|Assert.Single(list.Where(f => f > 0))|]", /* lang=c#-test */ "Assert.Single(list, f => f > 0)")] [InlineData( /* lang=c#-test */ "[|Assert.Single(list.Where(n => n == 1))|]", /* lang=c#-test */ "Assert.Single(list, n => n == 1)")] [InlineData( /* lang=c#-test */ "[|Assert.Single(list.Where(IsEven))|]", /* lang=c#-test */ "Assert.Single(list, IsEven)")] public async Task FixerReplacesAssertSingleOneArgumentToTwoArgumentCall( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertSingleShouldUseTwoArgumentCallFixer.Key_UseTwoArguments); } [Fact] public async Task FixAll_ReplacesAllSingleOneArgumentCalls() { var before = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var list = new[] { -1, 0, 1, 2 }; [|Assert.Single(list.Where(f => f > 0))|]; [|Assert.Single(list.Where(n => n == 1))|]; } } """; var after = /* lang=c#-test */ """ using System.Linq; using Xunit; public class TestClass { [Fact] public void TestMethod() { var list = new[] { -1, 0, 1, 2 }; Assert.Single(list, f => f > 0); Assert.Single(list, n => n == 1); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertSingleShouldUseTwoArgumentCallFixer.Key_UseTwoArguments); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertStringEqualityCheckShouldNotUseBoolCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertStringEqualityCheckShouldNotUseBoolCheckFixerTests { const string template = /* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var data = "foo bar baz"; {0}; }} }} """; [Theory] // Instance Equals (true) [InlineData( /* lang=c#-test */ @"[|Assert.True(""foo bar baz"".Equals(data))|]", /* lang=c#-test */ @"Assert.Equal(""foo bar baz"", data)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(""foo bar baz"".Equals(data, StringComparison.Ordinal))|]", /* lang=c#-test */ @"Assert.Equal(""foo bar baz"", data)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(""foo bar baz"".Equals(data, StringComparison.OrdinalIgnoreCase))|]", /* lang=c#-test */ @"Assert.Equal(""foo bar baz"", data, ignoreCase: true)")] // Static Equals (true) [InlineData( /* lang=c#-test */ @"[|Assert.True(string.Equals(""foo bar baz"", data))|]", /* lang=c#-test */ @"Assert.Equal(""foo bar baz"", data)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(string.Equals(""foo bar baz"", data, StringComparison.Ordinal))|]", /* lang=c#-test */ @"Assert.Equal(""foo bar baz"", data)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(string.Equals(""foo bar baz"", data, StringComparison.OrdinalIgnoreCase))|]", /* lang=c#-test */ @"Assert.Equal(""foo bar baz"", data, ignoreCase: true)")] // Instance Equals (false) [InlineData( /* lang=c#-test */ @"[|Assert.False(""foo bar baz"".Equals(data))|]", /* lang=c#-test */ @"Assert.NotEqual(""foo bar baz"", data)")] // Static Equals (false) [InlineData( /* lang=c#-test */ @"[|Assert.False(string.Equals(""foo bar baz"", data))|]", /* lang=c#-test */ @"Assert.NotEqual(""foo bar baz"", data)")] public async Task ConvertsBooleanAssertToEqualityAssert( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertStringEqualityCheckShouldNotUseBoolCheckFixer.Key_UseAlternateAssert); } [Fact] public async Task FixAll_ReplacesAllBooleanStringEqualityChecks() { var before = /* lang=c#-test */ """ using System; using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = "foo bar baz"; [|Assert.True("foo bar baz".Equals(data))|]; [|Assert.False("foo bar baz".Equals(data))|]; } } """; var after = /* lang=c#-test */ """ using System; using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = "foo bar baz"; Assert.Equal("foo bar baz", data); Assert.NotEqual("foo bar baz", data); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertStringEqualityCheckShouldNotUseBoolCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertSubstringCheckShouldNotUseBoolCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertSubstringCheckShouldNotUseBoolCheckFixerTests { const string template = /* lang=c#-test */ """ using System; using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var data = "foo bar baz"; {0}; }} }} """; [Theory] [InlineData( /* lang=c#-test */ @"[|Assert.True(data.Contains(""foo""))|]", /* lang=c#-test */ @"Assert.Contains(""foo"", data)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(data.StartsWith(""foo""))|]", /* lang=c#-test */ @"Assert.StartsWith(""foo"", data)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(data.StartsWith(""foo"", StringComparison.Ordinal))|]", /* lang=c#-test */ @"Assert.StartsWith(""foo"", data, StringComparison.Ordinal)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(data.EndsWith(""foo""))|]", /* lang=c#-test */ @"Assert.EndsWith(""foo"", data)")] [InlineData( /* lang=c#-test */ @"[|Assert.True(data.EndsWith(""foo"", StringComparison.OrdinalIgnoreCase))|]", /* lang=c#-test */ @"Assert.EndsWith(""foo"", data, StringComparison.OrdinalIgnoreCase)")] [InlineData( /* lang=c#-test */ @"[|Assert.False(data.Contains(""foo""))|]", /* lang=c#-test */ @"Assert.DoesNotContain(""foo"", data)")] public async Task ConvertsBooleanAssertToStringSpecificAssert( string beforeAssert, string afterAssert) { var before = string.Format(template, beforeAssert); var after = string.Format(template, afterAssert); await Verify.VerifyCodeFix(before, after, AssertSubstringCheckShouldNotUseBoolCheckFixer.Key_UseAlternateAssert); } [Fact] public async Task FixAll_ReplacesAllBooleanSubstringChecks() { var before = /* lang=c#-test */ """ using System; using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = "foo bar baz"; [|Assert.True(data.Contains("foo"))|]; [|Assert.False(data.Contains("foo"))|]; } } """; var after = /* lang=c#-test */ """ using System; using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = "foo bar baz"; Assert.Contains("foo", data); Assert.DoesNotContain("foo", data); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssertSubstringCheckShouldNotUseBoolCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixerTests { public static readonly TheoryData Assertions = GenerateAssertions(); const string template = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass {{ Task ThrowingMethod() {{ throw new NotImplementedException(); }} [Fact]{0} }} """; static TheoryData GenerateAssertions() { var templates = new (string, string)[] { ( /* lang=c#-test */ "Assert.Throws(typeof(Exception), {0})", /* lang=c#-test */ "Assert.ThrowsAsync(typeof(Exception), {0})" ), ( /* lang=c#-test */ "Assert.Throws({0})", /* lang=c#-test */ "Assert.ThrowsAsync({0})" ), ( /* lang=c#-test */ "Assert.Throws(\"parameter\", {0})", /* lang=c#-test */ "Assert.ThrowsAsync(\"parameter\", {0})" ), ( /* lang=c#-test */ "Assert.ThrowsAny({0})", /* lang=c#-test */ "Assert.ThrowsAnyAsync({0})" ), }; var lambdas = new[] { /* lang=c#-test */ "(Func)ThrowingMethod", /* lang=c#-test */ "() => Task.Delay(0)", /* lang=c#-test */ "(Func)(async () => await Task.Delay(0))", /* lang=c#-test */ "(Func)(async () => await Task.Delay(0).ConfigureAwait(false))", }; var assertions = new TheoryData(); foreach ((var assertionTemplate, var replacementTemplate) in templates) foreach (var lambda in lambdas) { var assertion = string.Format(assertionTemplate, lambda); var replacement = string.Format(replacementTemplate, lambda); assertions.Add(assertion, replacement); } return assertions; } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInMethod_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ {{|CS0619:[|{0}|]|}}; }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ await {0}; }} """, replacement); await VerifyCodeFix(beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInInvokedAnonymousFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Func function = () => {{ {{|CS0619:[|{0}|]|}}; return 0; }}; int number = function(); function(); }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ Func> function = async () => {{ await {0}; return 0; }}; int number = {{|CS0029:function()|}}; function(); }} """, replacement); await VerifyCodeFix(beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInInvokedAnonymousFunctionWithAssignment_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Func function = () => 0; function = () => {{ {{|CS0619:[|{0}|]|}}; return 0; }}; int number = function(); function(); }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ Func> function = () => {{|CS0029:{{|CS1662:0|}}|}}; function = async () => {{ await {0}; return 0; }}; int number = {{|CS0029:function()|}}; function(); }} """, replacement); await VerifyCodeFix(beforeMethod, afterMethod); } #if ROSLYN_LATEST // C# 10 is required for anonymous lambda types [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInInvokedAnonymousFunctionWithVar_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ var function = () => {{ {{|CS0619:[|{0}|]|}}; return 0; }}; int number = function(); function(); }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ var function = async () => {{ await {0}; return 0; }}; int number = {{|CS0029:function()|}}; function(); }} """, replacement); await VerifyCodeFix(LanguageVersion.CSharp10, beforeMethod, afterMethod); } #endif [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInUninvokedAnonymousFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Func function = () => {{ {{|CS0619:[|{0}|]|}}; return 0; }}; }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Func> function = async () => {{ await {0}; return 0; }}; }} """, replacement); await VerifyCodeFix(beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInInvokedNestedAnonymousFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Action outerFunction = (number) => {{ Func innerFunction = delegate () {{ {{|CS0619:[|{0}|]|}}; return string.Empty; }}; var message = innerFunction().ToLower(); innerFunction(); }}; outerFunction(0); }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ Func outerFunction = async (number) => {{ Func> innerFunction = async delegate () {{ await {0}; return string.Empty; }}; var message = innerFunction().{{|CS7036:ToLower|}}(); innerFunction(); }}; outerFunction(0); }} """, replacement); await VerifyCodeFix(beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInExplicitlyInvokedNestedAnonymousFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Action outerFunction = (number) => {{ Func innerFunction = delegate () {{ {{|CS0619:[|{0}|]|}}; return string.Empty; }}; var message = innerFunction.Invoke().ToLower(); }}; outerFunction.Invoke(0); }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ Func outerFunction = async (number) => {{ Func> innerFunction = async delegate () {{ await {0}; return string.Empty; }}; var message = innerFunction.Invoke().{{|CS7036:ToLower|}}(); }}; outerFunction.Invoke(0); }} """, replacement); await VerifyCodeFix(beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInConditionallyInvokedNestedAnonymousFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Action outerFunction = (number) => {{ Func innerFunction = delegate () {{ {{|CS0619:[|{0}|]|}}; return string.Empty; }}; var message = innerFunction?.Invoke().ToLower(); }}; outerFunction?.Invoke(0); }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ Func outerFunction = async (number) => {{ Func> innerFunction = async delegate () {{ await {0}; return string.Empty; }}; var message = innerFunction?.Invoke().{{|CS7036:ToLower|}}(); }}; outerFunction?.Invoke(0); }} """, replacement); await VerifyCodeFix(beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInUninvokedNestedAnonymousFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Action outerFunction = (number) => {{ Func innerFunction = () => {{ {{|CS0619:[|{0}|]|}}; return string.Empty; }}; }}; }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ Action outerFunction = (number) => {{ Func> innerFunction = async () => {{ await {0}; return string.Empty; }}; }}; }} """, replacement); await VerifyCodeFix(beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInInvokedLocalFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ int number = Function(); Function(); int Function() {{ {{|CS0619:[|{0}|]|}}; return 0; }} }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ int number = {{|CS0029:Function()|}}; Function(); async Task Function() {{ await {0}; return 0; }} }} """, replacement); await VerifyCodeFix(LanguageVersion.CSharp7, beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInUninvokedLocalFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ int Function() {{ {{|CS0619:[|{0}|]|}}; return 0; }} }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ async Task Function() {{ await {0}; return 0; }} }} """, replacement); await VerifyCodeFix(LanguageVersion.CSharp7, beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInInvokedNestedLocalFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ int number = OuterFunction(); OuterFunction(); int OuterFunction() {{ var message = InnerFunction().ToLower(); InnerFunction(); return 0; string InnerFunction() {{ {{|CS0619:[|{0}|]|}}; return string.Empty; }} }} }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ int number = {{|CS0029:OuterFunction()|}}; OuterFunction(); async Task OuterFunction() {{ var message = InnerFunction().{{|CS7036:ToLower|}}(); InnerFunction(); return 0; async Task InnerFunction() {{ await {0}; return string.Empty; }} }} }} """, replacement); await VerifyCodeFix(LanguageVersion.CSharp7, beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInUninvokedNestedLocalFunction_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ int OuterFunction() {{ return 0; string InnerFunction() {{ {{|CS0619:[|{0}|]|}}; return string.Empty; }} }} }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ int OuterFunction() {{ return 0; async Task InnerFunction() {{ await {0}; return string.Empty; }} }} }} """, replacement); await VerifyCodeFix(LanguageVersion.CSharp7, beforeMethod, afterMethod); } [Theory] [MemberData(nameof(Assertions))] public async Task GivenAssertionInMixedNestedFunctions_ReplacesWithAsyncAssertion( string assertion, string replacement) { var beforeMethod = string.Format(/* lang=c#-test */ """ public void TestMethod() {{ int OuterLocalFunction() {{ Func outerAnonymousFunction = () => {{ string InnerLocalFunction() {{ Action innerAnonymousFunction = () => {{ {{|CS0619:[|{0}|]|}}; }}; innerAnonymousFunction(); return string.Empty; }} string message = InnerLocalFunction(); InnerLocalFunction(); return false; }}; bool condition = outerAnonymousFunction(); outerAnonymousFunction(); return 0; }} int number = OuterLocalFunction(); OuterLocalFunction(); }} """, assertion); var afterMethod = string.Format(/* lang=c#-test */ """ public async Task TestMethod() {{ async Task OuterLocalFunction() {{ Func> outerAnonymousFunction = async () => {{ async Task InnerLocalFunction() {{ Func innerAnonymousFunction = async () => {{ await {0}; }}; innerAnonymousFunction(); return string.Empty; }} string message = {{|CS0029:InnerLocalFunction()|}}; InnerLocalFunction(); return false; }}; bool condition = {{|CS0029:outerAnonymousFunction()|}}; outerAnonymousFunction(); return 0; }} int number = {{|CS0029:OuterLocalFunction()|}}; OuterLocalFunction(); }} """, replacement); await VerifyCodeFix(LanguageVersion.CSharp7, beforeMethod, afterMethod); } static async Task VerifyCodeFix( string beforeMethod, string afterMethod) { var before = string.Format(template, beforeMethod); var after = string.Format(template, afterMethod); await Verify.VerifyCodeFix(before, after, AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer.Key_UseAlternateAssert); } static async Task VerifyCodeFix( LanguageVersion languageVersion, string beforeMethod, string afterMethod) { var before = string.Format(template, beforeMethod); var after = string.Format(template, afterMethod); await Verify.VerifyCodeFix(languageVersion, before, after, AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AssignableFromAssertionIsConfusinglyNamedFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AssignableFromAssertionIsConfusinglyNamedFixerTests { [Fact] public async Task FixAll_ReplacesAllAssignableFromAssertions() { var before = /* lang=c#-test */ """ using System; using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = "Hello world"; [|Assert.IsAssignableFrom(typeof(object), data)|]; [|Assert.IsAssignableFrom(data)|]; } } """; var after = /* lang=c#-test */ """ using System; using Xunit; public class TestClass { [Fact] public void TestMethod() { var data = "Hello world"; Assert.IsType(typeof(object), data, exactMatch: false); Assert.IsType(data, exactMatch: false); } } """; await Verify.VerifyCodeFixFixAll(before, after, AssignableFromAssertionIsConfusinglyNamedFixer.Key_UseIsType); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/AsyncAssertsShouldBeAwaitedFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class AsyncAssertsShouldBeAwaitedFixerTests { const string codeTemplate = /* lang=c#-test */ """ using System; using System.ComponentModel; using System.Threading.Tasks; using Xunit; public class TestClass : INotifyPropertyChanged {{ public int Property {{ get; set; }} public event PropertyChangedEventHandler? PropertyChanged; public event EventHandler? SimpleEvent; public event EventHandler? SimpleIntEvent; [Fact] public void TestMethod() {{ {0} }} }} public static class MyTaskExtensions {{ public static void ConsumeTask(this Task t) {{ }} }} """; public static TheoryData AsyncAssertions = [ "Assert.PropertyChangedAsync(this, nameof(Property), async () => throw new DivideByZeroException())", "Assert.RaisesAnyAsync(eh => SimpleEvent += eh, eh => SimpleEvent -= eh, async () => throw new DivideByZeroException())", "Assert.RaisesAnyAsync(eh => SimpleIntEvent += eh, eh => SimpleIntEvent -= eh, async () => throw new DivideByZeroException())", "Assert.RaisesAsync(eh => SimpleIntEvent += eh, eh => SimpleIntEvent -= eh, async () => throw new DivideByZeroException())", "Assert.ThrowsAnyAsync(async () => throw new DivideByZeroException())", "Assert.ThrowsAsync(typeof(DivideByZeroException), async () => throw new DivideByZeroException())", "Assert.ThrowsAsync(async () => throw new DivideByZeroException())", "Assert.ThrowsAsync(\"argName\", async () => throw new DivideByZeroException())", ]; [Theory] [MemberData(nameof(AsyncAssertions))] public async Task AddsAsyncAndAwait(string assertion) { var before = string.Format(codeTemplate, $"[|{assertion}|];"); var after = string.Format(codeTemplate, $"await {assertion};").Replace("public void TestMethod", "public async Task TestMethod"); await Verify.VerifyCodeFix(LanguageVersion.CSharp8, before, after, AsyncAssertsShouldBeAwaitedFixer.Key_AddAwait); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/BooleanAssertsShouldNotBeNegatedFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class BooleanAssertsShouldNotBeNegatedFixerTests { [Fact] public async Task AcceptanceTest() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { bool condition = true; // Not negated Assert.True(false); Assert.False(false); Assert.True(condition); Assert.False(condition); // Negated [|Assert.True(!false)|]; [|Assert.False(!false)|]; [|Assert.True(!condition)|]; [|Assert.False(!condition)|]; // Not negated, with message Assert.True(false, "test message"); Assert.False(false, "test message"); Assert.True(condition, "test message"); Assert.False(condition, "test message"); // Negated, with message [|Assert.True(!false, "test message")|]; [|Assert.False(!false, "test message")|]; [|Assert.True(!condition, "test message")|]; [|Assert.False(!condition, "test message")|]; // Not negated, with named parameter message Assert.True(false, userMessage: "test message"); Assert.False(false, userMessage: "test message"); Assert.True(condition, userMessage: "test message"); Assert.False(condition, userMessage: "test message"); // Negated, with named parameter message [|Assert.True(!false, userMessage: "test message")|]; [|Assert.False(!false, userMessage: "test message")|]; [|Assert.True(!condition, userMessage: "test message")|]; [|Assert.False(!condition, userMessage: "test message")|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { bool condition = true; // Not negated Assert.True(false); Assert.False(false); Assert.True(condition); Assert.False(condition); // Negated Assert.False(false); Assert.True(false); Assert.False(condition); Assert.True(condition); // Not negated, with message Assert.True(false, "test message"); Assert.False(false, "test message"); Assert.True(condition, "test message"); Assert.False(condition, "test message"); // Negated, with message Assert.False(false, "test message"); Assert.True(false, "test message"); Assert.False(condition, "test message"); Assert.True(condition, "test message"); // Not negated, with named parameter message Assert.True(false, userMessage: "test message"); Assert.False(false, userMessage: "test message"); Assert.True(condition, userMessage: "test message"); Assert.False(condition, userMessage: "test message"); // Negated, with named parameter message Assert.False(false, userMessage: "test message"); Assert.True(false, userMessage: "test message"); Assert.False(condition, userMessage: "test message"); Assert.True(condition, userMessage: "test message"); } } """; await Verify.VerifyCodeFix(before, after, BooleanAssertsShouldNotBeNegatedFixer.Key_UseSuggestedAssert); } [Fact] public async Task FixAll_ReplacesAllNegatedBooleanAsserts() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { bool condition = true; [|Assert.True(!condition)|]; [|Assert.False(!condition)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { bool condition = true; Assert.False(condition); Assert.True(condition); } } """; await Verify.VerifyCodeFixFixAll(before, after, BooleanAssertsShouldNotBeNegatedFixer.Key_UseSuggestedAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixerTests { const string template = /* lang=c#-test */ """ using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ bool condition = true; {0}; }} }} """; public static TheoryData AssertExpressionReplacement = new() { /* lang=c#-test */ { "True", "condition == true", "True" }, /* lang=c#-test */ { "True", "condition != true", "False" }, /* lang=c#-test */ { "True", "true == condition", "True" }, /* lang=c#-test */ { "True", "true != condition", "False" }, /* lang=c#-test */ { "True", "condition == false", "False" }, /* lang=c#-test */ { "True", "condition != false", "True" }, /* lang=c#-test */ { "True", "false == condition", "False" }, /* lang=c#-test */ { "True", "false != condition", "True" }, /* lang=c#-test */ { "False", "condition == true", "False" }, /* lang=c#-test */ { "False", "condition != true", "True" }, /* lang=c#-test */ { "False", "true == condition", "False" }, /* lang=c#-test */ { "False", "true != condition", "True" }, /* lang=c#-test */ { "False", "condition == false", "True" }, /* lang=c#-test */ { "False", "condition != false", "False" }, /* lang=c#-test */ { "False", "false == condition", "True" }, /* lang=c#-test */ { "False", "false != condition", "False" }, }; [Theory] [MemberData(nameof(AssertExpressionReplacement))] public async Task SimplifiesBooleanAssert( string assertion, string expression, string replacement) { var before = string.Format(template, $"{{|xUnit2025:Assert.{assertion}({expression})|}}"); var after = string.Format(template, $"Assert.{replacement}(condition)"); await Verify.VerifyCodeFix(before, after, BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixer.Key_UseSuggestedAssert); } [Theory] [MemberData(nameof(AssertExpressionReplacement))] public async Task SimplifiesBooleanAssertWithMessage( string assertion, string expression, string replacement) { var before = string.Format(template, $"{{|xUnit2025:Assert.{assertion}({expression}, \"message\")|}}"); var after = string.Format(template, $"Assert.{replacement}(condition, \"message\")"); await Verify.VerifyCodeFix(before, after, BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixer.Key_UseSuggestedAssert); } [Fact] public async Task FixAll_SimplifiesAllBooleanAsserts() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { bool condition = true; {|xUnit2025:Assert.True(condition == true)|}; {|xUnit2025:Assert.True(condition == false)|}; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { bool condition = true; Assert.True(condition); Assert.False(condition); } } """; await Verify.VerifyCodeFixFixAll(before, after, BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckBooleanFixer.Key_UseSuggestedAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixerTests { const string template = /* lang=c#-test */ """ using Xunit; public enum MyEnum {{ None, Bacon, Veggie }} public class TestClass {{ [Fact] public void TestMethod() {{ {0} value = {1}; {2}; }} }} """; public static MatrixTheoryData MethodOperatorValue = new( [Constants.Asserts.True, Constants.Asserts.False], ["==", "!="], ["\"bacon\"", "'5'", "5", "5l", "5.0d", "5.0f", "5.0m", "MyEnum.Bacon"] ); [Theory] [MemberData(nameof(MethodOperatorValue))] public async Task BooleanAssertAgainstLiteralValue_ReplaceWithEquality( string method, string @operator, string value) { var replacement = (method, @operator) switch { (Constants.Asserts.True, "==") or (Constants.Asserts.False, "!=") => Constants.Asserts.Equal, (_, _) => Constants.Asserts.NotEqual, }; // Literal on the right await Verify.VerifyCodeFix( string.Format(template, "var", value, $"{{|xUnit2024:Assert.{method}(value {@operator} {value})|}}"), string.Format(template, "var", value, $"Assert.{replacement}({value}, value)"), BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer.Key_UseSuggestedAssert ); // Literal on the left await Verify.VerifyCodeFix( string.Format(template, "var", value, $"{{|xUnit2024:Assert.{method}({value} {@operator} value)|}}"), string.Format(template, "var", value, $"Assert.{replacement}({value}, value)"), BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer.Key_UseSuggestedAssert ); } public static MatrixTheoryData MethodOperatorType = new( [Constants.Asserts.True, Constants.Asserts.False], ["==", "!="], ["string", "int", "object", "MyEnum"] ); [Theory] [MemberData(nameof(MethodOperatorType))] public async Task BooleanAssertAgainstNull_ReplaceWithNull( string method, string @operator, string type) { var replacement = (method, @operator) switch { (Constants.Asserts.True, "==") or (Constants.Asserts.False, "!=") => Constants.Asserts.Null, (_, _) => Constants.Asserts.NotNull, }; // Null on the right await Verify.VerifyCodeFix( LanguageVersion.CSharp8, string.Format(template, type + "?", "null", $"{{|xUnit2024:Assert.{method}(value {@operator} null)|}}"), string.Format(template, type + "?", "null", $"Assert.{replacement}(value)"), BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer.Key_UseSuggestedAssert ); // Null on the left await Verify.VerifyCodeFix( LanguageVersion.CSharp8, string.Format(template, type + "?", "null", $"{{|xUnit2024:Assert.{method}(null {@operator} value)|}}"), string.Format(template, type + "?", "null", $"Assert.{replacement}(value)"), BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer.Key_UseSuggestedAssert ); } [Fact] public async Task FixAll_ReplacesAllNonBooleanEqualityChecks() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var value = 5; {|xUnit2024:Assert.True(value == 5)|}; {|xUnit2024:Assert.True(value != 5)|}; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var value = 5; Assert.Equal(5, value); Assert.NotEqual(5, value); } } """; await Verify.VerifyCodeFixFixAll(before, after, BooleanAssertsShouldNotBeUsedForSimpleEqualityCheckNonBooleanFixer.Key_UseSuggestedAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/UseAssertFailInsteadOfBooleanAssertFixerTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class UseAssertFailInsteadOfBooleanAssertFixerTests { [Fact] public async Task FixAll_ReplacesAllBooleanAssertsWithFail() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { [|Assert.True(false, "message one")|]; [|Assert.False(true, "message two")|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { Assert.Fail("message one"); Assert.Fail("message two"); } } """; await Verify.VerifyCodeFixFixAll(before, after, UseAssertFailInsteadOfBooleanAssertFixer.Key_UseAssertFail); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X2000/UseGenericOverloadFixTests.cs ================================================ using System.Threading.Tasks; using Xunit; using Xunit.Analyzers.Fixes; using Verify_X2007 = CSharpVerifier; using Verify_X2015 = CSharpVerifier; public class UseGenericOverloadFixTests { [Fact] public async Task X2007_SwitchesToGenericIsAssignableFrom() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var result = 123; [|Assert.IsAssignableFrom(typeof(int), result)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var result = 123; Assert.IsAssignableFrom(result); } } """; await Verify_X2007.VerifyCodeFix(before, after, UseGenericOverloadFix.Key_UseAlternateAssert); } [Theory] [InlineData("result")] [InlineData("result, true")] [InlineData("result, false")] public async Task X2007_SwitchesToGenericIsType(string arguments) { var before = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var result = 123; [|Assert.IsType(typeof(int), {0})|]; }} }} """, arguments); var after = string.Format(/* lang=c#-test */ """ using Xunit; public class TestClass {{ [Fact] public void TestMethod() {{ var result = 123; Assert.IsType({0}); }} }} """, arguments); await Verify_X2007.VerifyCodeFix(before, after, UseGenericOverloadFix.Key_UseAlternateAssert); } [Fact] public async Task X2015_SwitchesToGenericThrows() { var before = /* lang=c#-test */ """ using System; using Xunit; public class TestClass { [Fact] public void TestMethod() { Action func = () => { }; [|Assert.Throws(typeof(DivideByZeroException), func)|]; } } """; var after = /* lang=c#-test */ """ using System; using Xunit; public class TestClass { [Fact] public void TestMethod() { Action func = () => { }; Assert.Throws(func); } } """; await Verify_X2015.VerifyCodeFix(before, after, UseGenericOverloadFix.Key_UseAlternateAssert); } [Fact] public async Task FixAll_SwitchesAllToGenericOverloads() { var before = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var result = 123; [|Assert.IsType(typeof(int), result)|]; [|Assert.IsAssignableFrom(typeof(int), result)|]; } } """; var after = /* lang=c#-test */ """ using Xunit; public class TestClass { [Fact] public void TestMethod() { var result = 123; Assert.IsType(result); Assert.IsAssignableFrom(result); } } """; await Verify_X2007.VerifyCodeFixFixAll(before, after, UseGenericOverloadFix.Key_UseAlternateAssert); } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X3000/CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; using Xunit.Analyzers.Fixes; using Verify_WithAbstractions = CSharpVerifier; using Verify_WithExecution = CSharpVerifier; using Verify_WithRunnerUtility = CSharpVerifier; public class CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixerTests { public class WithAbstractions : CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixerTests { const string Template = /* lang=c#-test */ "using Xunit.Abstractions; public class {0}: {1} {{ }}"; [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithAbstractions.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithAbstractions))] public async Task DoesNotAttemptToFix(string @interface) { var source = string.Format(Template, /* lang=c#-test */ "[|MyClass|]", @interface); await Verify_WithAbstractions.VerifyCodeFixV2( source, source, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } internal class Analyzer : CrossAppDomainClassesMustBeLongLivedMarshalByRefObject { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2Abstractions(compilation); } } public class WithExecution { const string Template_WithoutUsing = /* lang=c#-test */ """ using Xunit.Abstractions; public class Foo {{ }} public class {0}: {1} {{ }} """; const string Template_WithUsing = /* lang=c#-test */ """ using Xunit; using Xunit.Abstractions; public class Foo {{ }} public class {0}: {1} {{ }} """; [Fact] public async Task FixAll_AddsBaseClassToMultipleClasses() { var before = /* lang=c#-test */ """ using Xunit; using Xunit.Abstractions; public class [|MyClass1|]: IMessageSink { public bool OnMessage(IMessageSinkMessage message) => true; } public class [|MyClass2|]: IMessageSink { public bool OnMessage(IMessageSinkMessage message) => true; } """; var after = /* lang=c#-test */ """ using Xunit; using Xunit.Abstractions; public class MyClass1: LongLivedMarshalByRefObject, IMessageSink { public bool OnMessage(IMessageSinkMessage message) => true; } public class MyClass2: LongLivedMarshalByRefObject, IMessageSink { public bool OnMessage(IMessageSinkMessage message) => true; } """; await Verify_WithExecution.VerifyCodeFixV2FixAll(before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType); } [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithExecution.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithExecution))] public async Task WithNoBaseClass_WithoutUsing_AddsBaseClass(string @interface) { var before = string.Format(Template_WithoutUsing, /* lang=c#-test */ "[|MyClass|]", @interface); var after = string.Format(Template_WithoutUsing, "MyClass", $"Xunit.LongLivedMarshalByRefObject, {@interface}"); await Verify_WithExecution.VerifyCodeFixV2( before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithExecution.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithExecution))] public async Task WithNoBaseClass_WithUsing_AddsBaseClass(string @interface) { var before = string.Format(Template_WithUsing, /* lang=c#-test */ "[|MyClass|]", @interface); var after = string.Format(Template_WithUsing, "MyClass", $"LongLivedMarshalByRefObject, {@interface}"); await Verify_WithExecution.VerifyCodeFixV2( before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithExecution.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithExecution))] public async Task WithBadBaseClass_WithoutUsing_ReplacesBaseClass(string @interface) { var before = string.Format(Template_WithoutUsing, /* lang=c#-test */ "[|MyClass|]", $"Foo, {@interface}"); var after = string.Format(Template_WithoutUsing, "MyClass", $"Xunit.LongLivedMarshalByRefObject, {@interface}"); await Verify_WithExecution.VerifyCodeFixV2( before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithExecution.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithExecution))] public async Task WithBadBaseClass_WithUsing_ReplacesBaseClass(string @interface) { var before = string.Format(Template_WithUsing, /* lang=c#-test */ "[|MyClass|]", $"Foo, {@interface}"); var after = string.Format(Template_WithUsing, "MyClass", $"LongLivedMarshalByRefObject, {@interface}"); await Verify_WithExecution.VerifyCodeFixV2( before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } internal class Analyzer : CrossAppDomainClassesMustBeLongLivedMarshalByRefObject { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2Execution(compilation); } } public class WithRunnerUtility { const string Template_WithoutUsing = /* lang=c#-test */ """ using Xunit.Abstractions; public class Foo {{ }} public class {0}: {1} {{ }} """; const string Template_WithUsing = /* lang=c#-test */ """ using Xunit.Abstractions; using Xunit.Sdk; public class Foo {{ }} public class {0}: {1} {{ }} """; [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithRunnerUtility.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithRunnerUtility))] public async Task WithNoBaseClass_WithoutUsing_AddsBaseClass(string @interface) { var before = string.Format(Template_WithoutUsing, /* lang=c#-test */ "[|MyClass|]", @interface); var after = string.Format(Template_WithoutUsing, "MyClass", $"Xunit.Sdk.LongLivedMarshalByRefObject, {@interface}"); await Verify_WithRunnerUtility.VerifyCodeFixV2RunnerUtility( before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithRunnerUtility.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithRunnerUtility))] public async Task WithNoBaseClass_WithUsing_AddsBaseClass(string @interface) { var before = string.Format(Template_WithUsing, /* lang=c#-test */ "[|MyClass|]", @interface); var after = string.Format(Template_WithUsing, "MyClass", $"LongLivedMarshalByRefObject, {@interface}"); await Verify_WithRunnerUtility.VerifyCodeFixV2RunnerUtility( before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithRunnerUtility.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithRunnerUtility))] public async Task WithBadBaseClass_WithoutUsing_ReplacesBaseClass(string @interface) { var before = string.Format(Template_WithoutUsing, /* lang=c#-test */ "[|MyClass|]", $"Foo, {@interface}"); var after = string.Format(Template_WithoutUsing, "MyClass", $"Xunit.Sdk.LongLivedMarshalByRefObject, {@interface}"); await Verify_WithRunnerUtility.VerifyCodeFixV2RunnerUtility( before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } [Theory] [MemberData(nameof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithRunnerUtility.Interfaces), MemberType = typeof(CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectTests.WithRunnerUtility))] public async Task WithBadBaseClass_WithUsing_ReplacesBaseClass(string @interface) { var before = string.Format(Template_WithUsing, /* lang=c#-test */ "[|MyClass|]", $"Foo, {@interface}"); var after = string.Format(Template_WithUsing, "MyClass", $"LongLivedMarshalByRefObject, {@interface}"); await Verify_WithRunnerUtility.VerifyCodeFixV2RunnerUtility( before, after, CrossAppDomainClassesMustBeLongLivedMarshalByRefObjectFixer.Key_SetBaseType ); } internal class Analyzer : CrossAppDomainClassesMustBeLongLivedMarshalByRefObject { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2RunnerUtility(compilation); } } } ================================================ FILE: src/xunit.analyzers.tests/Fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixerTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Analyzers.Fixes; using Verify = CSharpVerifier; public class SerializableClassMustHaveParameterlessConstructorFixerTests { public class JsonTypeID { [Fact] public async Task WithPublicParameteredConstructor_AddsNewConstructor() { var before = /* lang=c#-test */ """ using Xunit.Sdk; [JsonTypeID("1")] public class [|MyJsonObject|] { public MyJsonObject(int _) { } } """; var after = /* lang=c#-test */ """ using Xunit.Sdk; [JsonTypeID("1")] public class MyJsonObject { [System.Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] public MyJsonObject() { } public MyJsonObject(int _) { } } """; await Verify.VerifyCodeFixV3(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task WithNonPublicParameterlessConstructor_ChangesVisibility() { var before = /* lang=c#-test */ """ using Xunit.Sdk; [JsonTypeID("1")] public class [|MyJsonObject|] { protected MyJsonObject() { } } """; var after = /* lang=c#-test */ """ using Xunit.Sdk; [JsonTypeID("1")] public class MyJsonObject { [System.Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] public MyJsonObject() { } } """; await Verify.VerifyCodeFixV3(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } } public class RunnerReporter { [Fact] public async Task WithPublicParameteredConstructor_AddsNewConstructor() { var before = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit.Runner.Common; using Xunit.Sdk; public class [|MyRunnerReporter|] : IRunnerReporter { public MyRunnerReporter(int _) { } public bool CanBeEnvironmentallyEnabled => false; public string Description => string.Empty; public bool ForceNoLogo => false; public bool IsEnvironmentallyEnabled => false; public string? RunnerSwitch => "unused"; public ValueTask CreateMessageHandler( IRunnerLogger logger, IMessageSink? diagnosticMessageSink) => throw new NotImplementedException(); } """; var after = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit.Runner.Common; using Xunit.Sdk; public class MyRunnerReporter : IRunnerReporter { public MyRunnerReporter() { } public MyRunnerReporter(int _) { } public bool CanBeEnvironmentallyEnabled => false; public string Description => string.Empty; public bool ForceNoLogo => false; public bool IsEnvironmentallyEnabled => false; public string? RunnerSwitch => "unused"; public ValueTask CreateMessageHandler( IRunnerLogger logger, IMessageSink? diagnosticMessageSink) => throw new NotImplementedException(); } """; await Verify.VerifyCodeFixV3(LanguageVersion.CSharp8, before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task WithNonPublicParameterlessConstructor_ChangesVisibility() { var before = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit.Runner.Common; using Xunit.Sdk; public class [|MyRunnerReporter|] : IRunnerReporter { protected MyRunnerReporter() { } public bool CanBeEnvironmentallyEnabled => false; public string Description => string.Empty; public bool ForceNoLogo => false; public bool IsEnvironmentallyEnabled => false; public string? RunnerSwitch => "unused"; public ValueTask CreateMessageHandler( IRunnerLogger logger, IMessageSink? diagnosticMessageSink) => throw new NotImplementedException(); } """; var after = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit.Runner.Common; using Xunit.Sdk; public class MyRunnerReporter : IRunnerReporter { public MyRunnerReporter() { } public bool CanBeEnvironmentallyEnabled => false; public string Description => string.Empty; public bool ForceNoLogo => false; public bool IsEnvironmentallyEnabled => false; public string? RunnerSwitch => "unused"; public ValueTask CreateMessageHandler( IRunnerLogger logger, IMessageSink? diagnosticMessageSink) => throw new NotImplementedException(); } """; await Verify.VerifyCodeFixV3(LanguageVersion.CSharp8, before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } } public class XunitSerializable { [Fact] public async Task FixAll_AddsConstructorsToMultipleClasses() { var before = /* lang=c#-test */ """ using Xunit.Sdk; public class [|MyTestCase1|]: IXunitSerializable { public MyTestCase1(int x) { } void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { } void IXunitSerializable.Serialize(IXunitSerializationInfo _) { } } public class [|MyTestCase2|]: IXunitSerializable { public MyTestCase2(string s) { } void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { } void IXunitSerializable.Serialize(IXunitSerializationInfo _) { } } """; var after = /* lang=c#-test */ """ using Xunit.Sdk; public class MyTestCase1: IXunitSerializable { [System.Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] public MyTestCase1() { } public MyTestCase1(int x) { } void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { } void IXunitSerializable.Serialize(IXunitSerializationInfo _) { } } public class MyTestCase2: IXunitSerializable { [System.Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] public MyTestCase2() { } public MyTestCase2(string s) { } void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { } void IXunitSerializable.Serialize(IXunitSerializationInfo _) { } } """; await Verify.VerifyCodeFixV3FixAll(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task WithPublicParameteredConstructor_AddsNewConstructor() { var beforeTemplate = /* lang=c#-test */ """ public class [|MyTestCase|]: {0}.IXunitSerializable {{ public MyTestCase(int x) {{ }} void {0}.IXunitSerializable.Deserialize({0}.IXunitSerializationInfo _) {{ }} void {0}.IXunitSerializable.Serialize({0}.IXunitSerializationInfo _) {{ }} }} """; var afterTemplate = /* lang=c#-test */ """ public class MyTestCase: {0}.IXunitSerializable {{ [System.Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] public MyTestCase() {{ }} public MyTestCase(int x) {{ }} void {0}.IXunitSerializable.Deserialize({0}.IXunitSerializationInfo _) {{ }} void {0}.IXunitSerializable.Serialize({0}.IXunitSerializationInfo _) {{ }} }} """; var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions"); var v2After = string.Format(afterTemplate, "Xunit.Abstractions"); await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); var v3Before = string.Format(beforeTemplate, "Xunit.Sdk"); var v3After = string.Format(afterTemplate, "Xunit.Sdk"); await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task WithNonPublicParameterlessConstructor_ChangesVisibility_WithoutUsing() { var beforeTemplate = /* lang=c#-test */ """ using {0}; public class [|MyTestCase|]: IXunitSerializable {{ protected MyTestCase() {{ throw new System.DivideByZeroException(); }} void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} }} """; var afterTemplate = /* lang=c#-test */ """ using {0}; public class MyTestCase: IXunitSerializable {{ [System.Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] public MyTestCase() {{ throw new System.DivideByZeroException(); }} void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} }} """; var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions"); var v2After = string.Format(afterTemplate, "Xunit.Abstractions"); await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); var v3Before = string.Format(beforeTemplate, "Xunit.Sdk"); var v3After = string.Format(afterTemplate, "Xunit.Sdk"); await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task WithNonPublicParameterlessConstructor_ChangesVisibility_WithUsing() { var beforeTemplate = /* lang=c#-test */ """ using System; using {0}; public class [|MyTestCase|]: IXunitSerializable {{ protected MyTestCase() {{ throw new DivideByZeroException(); }} void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} }} """; var afterTemplate = /* lang=c#-test */ """ using System; using {0}; public class MyTestCase: IXunitSerializable {{ [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] public MyTestCase() {{ throw new DivideByZeroException(); }} void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} }} """; var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions"); var v2After = string.Format(afterTemplate, "Xunit.Abstractions"); await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); var v3Before = string.Format(beforeTemplate, "Xunit.Sdk"); var v3After = string.Format(afterTemplate, "Xunit.Sdk"); await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task PreservesExistingObsoleteAttribute() { var beforeTemplate = /* lang=c#-test */ """ using {0}; using obo = System.ObsoleteAttribute; public class [|MyTestCase|]: IXunitSerializable {{ [obo("This is my custom obsolete message")] protected MyTestCase() {{ throw new System.DivideByZeroException(); }} void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} }} """; var afterTemplate = /* lang=c#-test */ """ using {0}; using obo = System.ObsoleteAttribute; public class MyTestCase: IXunitSerializable {{ [obo("This is my custom obsolete message")] public MyTestCase() {{ throw new System.DivideByZeroException(); }} void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} }} """; var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions"); var v2After = string.Format(afterTemplate, "Xunit.Abstractions"); await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); var v3Before = string.Format(beforeTemplate, "Xunit.Sdk"); var v3After = string.Format(afterTemplate, "Xunit.Sdk"); await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } } public class XunitSerializer { [Fact] public async Task WithPublicParameteredConstructor_AddsNewConstructor() { var before = /* lang=c#-test */ """ using System; using Xunit.Sdk; public class [|MySerializer|] : IXunitSerializer { public MySerializer(int _) { } public object Deserialize(Type type, string serializedValue) => null!; public bool IsSerializable(Type type, object? value, out string? failureReason) { failureReason = null; return true; } public string Serialize(object value) => string.Empty; } """; var after = /* lang=c#-test */ """ using System; using Xunit.Sdk; public class MySerializer : IXunitSerializer { public MySerializer() { } public MySerializer(int _) { } public object Deserialize(Type type, string serializedValue) => null!; public bool IsSerializable(Type type, object? value, out string? failureReason) { failureReason = null; return true; } public string Serialize(object value) => string.Empty; } """; await Verify.VerifyCodeFixV3(LanguageVersion.CSharp8, before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task WithNonPublicParameterlessConstructor_ChangesVisibility() { var before = /* lang=c#-test */ """ using System; using Xunit.Sdk; public class [|MySerializer|] : IXunitSerializer { protected MySerializer() { } public object Deserialize(Type type, string serializedValue) => null!; public bool IsSerializable(Type type, object? value, out string? failureReason) { failureReason = null; return true; } public string Serialize(object value) => string.Empty; } """; var after = /* lang=c#-test */ """ using System; using Xunit.Sdk; public class MySerializer : IXunitSerializer { public MySerializer() { } public object Deserialize(Type type, string serializedValue) => null!; public bool IsSerializable(Type type, object? value, out string? failureReason) { failureReason = null; return true; } public string Serialize(object value) => string.Empty; } """; await Verify.VerifyCodeFixV3(LanguageVersion.CSharp8, before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } } } ================================================ FILE: src/xunit.analyzers.tests/Suppressors/ConsiderCallingConfigureAwaitSuppressorTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public sealed class ConsiderCallingConfigureAwaitSuppressorTests { [Fact] public async Task NonTestMethod_DoesNotSuppress() { var code = /* lang=c#-test */ """ using System.Threading.Tasks; public class NonTestClass { public async Task NonTestMethod() { await {|CA2007:Task.Delay(1)|}; } } """; await Verify.VerifySuppressor(code, CodeAnalysisNetAnalyzers.CA2007()); } [Theory] [InlineData("Fact")] [InlineData("FactAttribute")] [InlineData("Theory")] [InlineData("TheoryAttribute")] public async Task StandardTestMethod_Suppresses(string attribute) { var code = string.Format(/* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class TestClass {{ [{0}] public async Task TestMethod() {{ await {{|#0:Task.Delay(1)|}}; }} }} """, attribute); var expected = DiagnosticResult.CompilerWarning("CA2007").WithLocation(0).WithIsSuppressed(true); await Verify.VerifySuppressor(code, CodeAnalysisNetAnalyzers.CA2007(), expected); } [Fact] public async Task CustomFactTestMethod_DoesNotSuppress() { var code = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; internal class CustomFactAttribute : FactAttribute { } public class TestClass { [CustomFact] public async Task TestMethod() { await {|CA2007:Task.Delay(1)|}; } } """; await Verify.VerifySuppressor(code, CodeAnalysisNetAnalyzers.CA2007()); } [Fact] public async Task CodeInsideFunctions_DoesNotSuppress() { var code = /* lang=c#-test */ """ using System; using System.Threading.Tasks; using Xunit; public class TestClass { [Fact] public void TestMethod() { async Task InnerMethod1() { await {|CA2007:Task.Delay(1)|}; } async Task InnerMethod2() => await {|CA2007:Task.Delay(1)|}; Func Lambda = async () => await {|CA2007:Task.Delay(1)|}; } } """; await Verify.VerifySuppressor(LanguageVersion.CSharp7, code, CodeAnalysisNetAnalyzers.CA2007()); } } ================================================ FILE: src/xunit.analyzers.tests/Suppressors/MakeTypesInternalSuppressorTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public sealed class MakeTypesInternalSuppressorTests { [Fact] public async Task NonTestClass_DoesNotSuppress() { var code = /* lang=c#-test */ """ public class {|CA1515:NonTestClass|} { public void NonTestMethod() { } } """; await Verify.VerifySuppressor(code, CodeAnalysisNetAnalyzers.CA1515()); } [Theory] [InlineData("Fact")] [InlineData("FactAttribute")] [InlineData("Theory")] [InlineData("TheoryAttribute")] [InlineData("CustomFact")] [InlineData("CustomFactAttribute")] public async Task TestClass_Suppresses(string attribute) { var code = string.Format(/* lang=c#-test */ """ using Xunit; internal class CustomFactAttribute : FactAttribute {{ }} public class {{|#0:TestClass|}} {{ [{0}] public void TestMethod() {{ }} }} """, attribute); var expected = new DiagnosticResult("CA1515", DiagnosticSeverity.Warning).WithLocation(0).WithIsSuppressed(true); await Verify.VerifySuppressor(code, CodeAnalysisNetAnalyzers.CA1515(), expected); } } ================================================ FILE: src/xunit.analyzers.tests/Suppressors/NonNullableFieldInitializationSuppressorTests.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; using Xunit; using Verify = CSharpVerifier; public sealed class NonNullableFieldInitializationSuppressorTests { // ----- v2 (Task-based IAsyncLifetime) ----- [Fact] public async Task NonAsyncLifetimeClass_V2_DoesNotSuppress() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 public class NonTestClass { private string {|CS8618:_field|}; } """; await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code]); } [Fact] public async Task FieldInitializedInInitializeAsync_V2_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string {|#0:_field|}; public Task InitializeAsync() { _field = "hello"; return Task.CompletedTask; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code], expected); } [Fact] public async Task PropertyInitializedInInitializeAsync_V2_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { public string {|#0:Prop|} { get; set; } public Task InitializeAsync() { Prop = "hello"; return Task.CompletedTask; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code], expected); } [Fact] public async Task FieldNotInitializedInInitializeAsync_V2_DoesNotSuppress() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string {|CS8618:_field|}; public Task InitializeAsync() { return Task.CompletedTask; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Test1() { } } """; await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code]); } [Fact] public async Task FieldInitializedWithThis_V2_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string {|#0:_field|}; public Task InitializeAsync() { this._field = "hello"; return Task.CompletedTask; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code], expected); } [Fact] public async Task MultipleFields_OnlyInitializedOnesSuppressed_V2() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string {|#0:_initialized|}; private string {|CS8618:_notInitialized|}; public Task InitializeAsync() { _initialized = "hello"; return Task.CompletedTask; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code], expected); } [Fact] public async Task FieldAssignedWithExplicitConstructor_V2_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string _field; public {|#0:TestClass|}() { } public Task InitializeAsync() { _field = "hello"; return Task.CompletedTask; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code], expected); } [Fact] public async Task FieldNotAssignedWithExplicitConstructor_V2_DoesNotSuppress() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string _field; public {|CS8618:TestClass|}() { } public Task InitializeAsync() { return Task.CompletedTask; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Test1() { } } """; await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code]); } [Fact] public async Task PropertyAssignedWithExplicitConstructor_V2_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { public string MyProp { get; set; } public {|#0:TestClass|}() { } public Task InitializeAsync() { MyProp = "hello"; return Task.CompletedTask; } public Task DisposeAsync() => Task.CompletedTask; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV2(LanguageVersion.CSharp8, [code], expected); } // ----- v3 (ValueTask-based IAsyncLifetime) ----- [Fact] public async Task NonAsyncLifetimeClass_V3_DoesNotSuppress() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 public class NonTestClass { private string {|CS8618:_field|}; } """; await Verify.VerifyCompilerWarningSuppressorV3(LanguageVersion.CSharp8, [code]); } [Fact] public async Task FieldInitializedInInitializeAsync_V3_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string {|#0:_field|}; public ValueTask InitializeAsync() { _field = "hello"; return default; } public ValueTask DisposeAsync() => default; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV3(LanguageVersion.CSharp8, [code], expected); } [Fact] public async Task PropertyInitializedInInitializeAsync_V3_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { public string {|#0:Prop|} { get; set; } public ValueTask InitializeAsync() { Prop = "hello"; return default; } public ValueTask DisposeAsync() => default; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV3(LanguageVersion.CSharp8, [code], expected); } [Fact] public async Task FieldNotInitializedInInitializeAsync_V3_DoesNotSuppress() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string {|CS8618:_field|}; public ValueTask InitializeAsync() => default; public ValueTask DisposeAsync() => default; [Fact] public void Test1() { } } """; await Verify.VerifyCompilerWarningSuppressorV3(LanguageVersion.CSharp8, [code]); } [Fact] public async Task FieldAssignedWithExplicitConstructor_V3_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string _field; public {|#0:TestClass|}() { } public ValueTask InitializeAsync() { _field = "hello"; return default; } public ValueTask DisposeAsync() => default; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV3(LanguageVersion.CSharp8, [code], expected); } [Fact] public async Task FieldNotAssignedWithExplicitConstructor_V3_DoesNotSuppress() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { private string _field; public {|CS8618:TestClass|}() { } public ValueTask InitializeAsync() => default; public ValueTask DisposeAsync() => default; [Fact] public void Test1() { } } """; await Verify.VerifyCompilerWarningSuppressorV3(LanguageVersion.CSharp8, [code]); } [Fact] public async Task PropertyAssignedWithExplicitConstructor_V3_Suppresses() { var code = /* lang=c#-test */ """ #nullable enable #pragma warning disable CS0414, CS0169, CS1591 using System.Threading.Tasks; using Xunit; public class TestClass : IAsyncLifetime { public string MyProp { get; set; } public {|#0:TestClass|}() { } public ValueTask InitializeAsync() { MyProp = "hello"; return default; } public ValueTask DisposeAsync() => default; [Fact] public void Test1() { } } """; var expected = DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true).WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); await Verify.VerifyCompilerWarningSuppressorV3(LanguageVersion.CSharp8, [code], expected); } } ================================================ FILE: src/xunit.analyzers.tests/Suppressors/UseAsyncSuffixForAsyncMethodsSuppressorTests.cs ================================================ #if NETCOREAPP // System.Collections.Immutable 1.6.0 conflicts with 6.0.0 in NetFx using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Xunit; using Xunit.Analyzers; using Verify = CSharpVerifier; public class UseAsyncSuffixForAsyncMethodsSuppressorTests { [Fact] public async Task AcceptanceTest() { var code = /* lang=c#-test */ """ using System.Threading.Tasks; using Xunit; public class NonTestClass { public async Task {|#0:NonTestMethod|}() { } } public class TestClass { [Fact] public async Task {|#1:TestMethod|]() { } } """; var expected = new[] { new DiagnosticResult("VSTHRD200", DiagnosticSeverity.Warning).WithLocation(0), new DiagnosticResult("VSTHRD200", DiagnosticSeverity.Warning).WithLocation(1).WithIsSuppressed(true), }; await Verify.VerifySuppressor(code, VsThreadingAnalyzers.VSTHRD200(), expected); } } #endif ================================================ FILE: src/xunit.analyzers.tests/Utility/3rdPartyAnalyzers/AnalyzerLoaderBase.cs ================================================ using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; #if NETCOREAPP using System.Runtime.Loader; #endif namespace Xunit.Analyzers; public class AnalyzerLoaderBase { static readonly Lazy nuGetPackagesFolder = new(GetNuGetPackagesFolder, isThreadSafe: true); protected static string NuGetPackagesFolder => nuGetPackagesFolder.Value; protected static Type FindType( Lazy assembly, string typeName) => assembly.Value.GetType(typeName) ?? throw new InvalidOperationException($"Could not locate type '{typeName}' from '{assembly.Value.GetName().Name}'"); static string GetNuGetPackagesFolder() { var result = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); if (result is null) { var homeFolder = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Environment.GetEnvironmentVariable("USERPROFILE") : Environment.GetEnvironmentVariable("HOME"); result = Path.Combine(homeFolder ?? throw new InvalidOperationException("Could not determine home folder"), ".nuget", "packages"); } if (!Directory.Exists(result)) throw new InvalidOperationException($"NuGet package cache folder '{result}' does not exist"); return result; } protected static Assembly LoadAssembly(string assemblyPath) => #if NETCOREAPP AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); #else Assembly.LoadFrom(assemblyPath); #endif } ================================================ FILE: src/xunit.analyzers.tests/Utility/3rdPartyAnalyzers/CodeAnalysisNetAnalyzers.cs ================================================ using System; using System.IO; using System.Reflection; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; public class CodeAnalysisNetAnalyzers : AnalyzerLoaderBase { static readonly Lazy assemblyCSharpNetAnalyzers = new(LoadCSharpNetAnalyzers, isThreadSafe: true); static readonly Lazy assemblyNetAnalyzers = new(LoadNetAnalyzers, isThreadSafe: true); static readonly Lazy typeCA1515 = new(() => FindType(assemblyCSharpNetAnalyzers, "Microsoft.CodeQuality.CSharp.Analyzers.Maintainability.CSharpMakeTypesInternal"), isThreadSafe: true); static readonly Lazy typeCA2007 = new(() => FindType(assemblyNetAnalyzers, "Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.DoNotDirectlyAwaitATaskAnalyzer"), isThreadSafe: true); public static DiagnosticAnalyzer CA1515() => Activator.CreateInstance(typeCA1515.Value) as DiagnosticAnalyzer ?? throw new InvalidOperationException($"Could not create instance of '{typeCA1515.Value.FullName}'"); public static DiagnosticAnalyzer CA2007() => Activator.CreateInstance(typeCA2007.Value) as DiagnosticAnalyzer ?? throw new InvalidOperationException($"Could not create instance of '{typeCA2007.Value.FullName}'"); static Assembly LoadCSharpNetAnalyzers() { // Make sure we load the dependencies first var _ = assemblyNetAnalyzers.Value; return LoadAssembly(Path.Combine(NuGetPackagesFolder, "microsoft.codeanalysis.netanalyzers", "10.0.100", "analyzers", "dotnet", "cs", "Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll")); } static Assembly LoadNetAnalyzers() { LoadAssembly(Path.Combine(NuGetPackagesFolder, "microsoft.codeanalysis.workspaces.common", "3.11.0", "lib", "netcoreapp3.1", "Microsoft.CodeAnalysis.Workspaces.dll")); return LoadAssembly(Path.Combine(NuGetPackagesFolder, "microsoft.codeanalysis.netanalyzers", "10.0.100", "analyzers", "dotnet", "Microsoft.CodeAnalysis.NetAnalyzers.dll")); } } ================================================ FILE: src/xunit.analyzers.tests/Utility/3rdPartyAnalyzers/VsThreadingAnalyzers.cs ================================================ #if NETCOREAPP // System.Collections.Immutable 1.6.0 conflicts with 6.0.0 in NetFx using System; using System.IO; using System.Reflection; using Microsoft.CodeAnalysis.Diagnostics; namespace Xunit.Analyzers; public class VsThreadingAnalyzers : AnalyzerLoaderBase { static readonly Lazy assemblyVsThreading = new(LoadVsThreadingAnalyzers, isThreadSafe: true); static readonly Lazy typeVSTHRD200 = new(() => FindType(assemblyVsThreading, "Microsoft.VisualStudio.Threading.Analyzers.VSTHRD200UseAsyncNamingConventionAnalyzer"), isThreadSafe: true); public static DiagnosticAnalyzer VSTHRD200() => Activator.CreateInstance(typeVSTHRD200.Value) as DiagnosticAnalyzer ?? throw new InvalidOperationException($"Could not create instance of '{typeVSTHRD200.Value.FullName}'"); static Assembly LoadVsThreadingAnalyzers() { LoadAssembly(Path.Combine(NuGetPackagesFolder, "system.collections.immutable", "6.0.0", "lib", "net6.0", "System.Collections.Immutable.dll")); LoadAssembly(Path.Combine(NuGetPackagesFolder, "microsoft.codeanalysis.workspaces.common", "3.11.0", "lib", "netcoreapp3.1", "Microsoft.CodeAnalysis.Workspaces.dll")); return LoadAssembly(Path.Combine(NuGetPackagesFolder, "microsoft.visualstudio.threading.analyzers", "17.11.20", "analyzers", "cs", "Microsoft.VisualStudio.Threading.Analyzers.dll")); } } #endif ================================================ FILE: src/xunit.analyzers.tests/Utility/AssertsExtensions/EmptyException.cs ================================================ using System; namespace Xunit.Sdk; internal partial class EmptyException { public static EmptyException ForNamedNonEmptyCollection( string collection, string collectionName) => new( $"Assert.Empty() Failure: Collection '{collectionName}' was not empty" + Environment.NewLine + "Collection: " + collection ); } ================================================ FILE: src/xunit.analyzers.tests/Utility/AssertsExtensions/EqualException.cs ================================================ using System; namespace Xunit.Sdk; internal partial class EqualException { public static EqualException ForMismatchedValuesWithMessage( object? expected, object? actual, string? message) { var expectedText = expected as string ?? ArgumentFormatter.Format(expected); var actualText = actual as string ?? ArgumentFormatter.Format(actual); if (string.IsNullOrWhiteSpace(message)) message = "Assert.Equal() Failure: Values differ" + Environment.NewLine + "Expected: " + expectedText.Replace(Environment.NewLine, newLineAndIndent) + Environment.NewLine + "Actual: " + actualText.Replace(Environment.NewLine, newLineAndIndent); return new(message); } } ================================================ FILE: src/xunit.analyzers.tests/Utility/AssertsExtensions/NotEmptyException.cs ================================================ namespace Xunit.Sdk; internal partial class NotEmptyException { public static NotEmptyException ForNamedNonEmptyCollection(string collectionName) => new($"Assert.NotEmpty() Failure: Collection '{collectionName}' was empty"); } ================================================ FILE: src/xunit.analyzers.tests/Utility/CSharpVerifier.Analyzers.RunnerUtility.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; public partial class CSharpVerifier { // ----- Multi-version ----- /// /// Runs code for analysis, against xUnit.net v2 and v3 Runner Utility, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static async Task VerifyAnalyzerRunnerUtility( string source, params DiagnosticResult[] diagnostics) { await VerifyAnalyzerV2RunnerUtility(LanguageVersion.CSharp6, [source], diagnostics); await VerifyAnalyzerV3RunnerUtility(LanguageVersion.CSharp6, [source], diagnostics); } /// /// Runs code for analysis, against xUnit.net v2 and v3 Runner Utility, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static async Task VerifyAnalyzerRunnerUtility( LanguageVersion languageVersion, string source, params DiagnosticResult[] diagnostics) { await VerifyAnalyzerV2RunnerUtility(languageVersion, [source], diagnostics); await VerifyAnalyzerV3RunnerUtility(languageVersion, [source], diagnostics); } /// /// Runs code for analysis, against xUnit.net v2 and v3 Runner Utility, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static async Task VerifyAnalyzerRunnerUtility( string[] sources, params DiagnosticResult[] diagnostics) { await VerifyAnalyzerV2RunnerUtility(LanguageVersion.CSharp6, sources, diagnostics); await VerifyAnalyzerV3RunnerUtility(LanguageVersion.CSharp6, sources, diagnostics); } /// /// Runs code for analysis, against xUnit.net v2 and v3 Runner Utility, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static async Task VerifyAnalyzerRunnerUtility( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { await VerifyAnalyzerV2RunnerUtility(languageVersion, sources, diagnostics); await VerifyAnalyzerV3RunnerUtility(languageVersion, sources, diagnostics); } // ----- v2 ----- /// /// Runs code for analysis, against xUnit.net v2 Runner Utility, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV2RunnerUtility( string source, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV2RunnerUtility(LanguageVersion.CSharp6, [source], diagnostics); /// /// Runs code for analysis, against xUnit.net v2, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV2RunnerUtility( LanguageVersion languageVersion, string source, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV2RunnerUtility(languageVersion, [source], diagnostics); /// /// Runs code for analysis, against xUnit.net v2 Runner Utility, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV2RunnerUtility( string[] sources, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV2RunnerUtility(LanguageVersion.CSharp6, sources, diagnostics); /// /// Runs code for analysis, against xUnit.net v2, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV2RunnerUtility( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { var test = new TestV2RunnerUtility(languageVersion); foreach (var source in sources) test.TestState.Sources.Add(source); test.TestState.ExpectedDiagnostics.AddRange(diagnostics); return test.RunAsync(); } // ----- v3 ----- /// /// Runs code for analysis, against xUnit.net v3 Runner Utility, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV3RunnerUtility( string source, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV3RunnerUtility(LanguageVersion.CSharp6, [source], diagnostics); /// /// Runs code for analysis, against xUnit.net v3 Runner Utility, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV3RunnerUtility( LanguageVersion languageVersion, string source, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV3RunnerUtility(languageVersion, [source], diagnostics); /// /// Runs code for analysis, against xUnit.net v3 Runner Utility, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV3RunnerUtility( string[] sources, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV3RunnerUtility(LanguageVersion.CSharp6, sources, diagnostics); /// /// Runs code for analysis, against xUnit.net v3 Runner Utility, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV3RunnerUtility( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { var test = new TestV3RunnerUtility(languageVersion); foreach (var source in sources) test.TestState.Sources.Add(source); test.TestState.ExpectedDiagnostics.AddRange(diagnostics); return test.RunAsync(); } } ================================================ FILE: src/xunit.analyzers.tests/Utility/CSharpVerifier.Analyzers.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; public partial class CSharpVerifier { // ----- Multi-version ----- /// /// Runs code for analysis, against xUnit.net v2 and v3, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static async Task VerifyAnalyzer( string source, params DiagnosticResult[] diagnostics) { await VerifyAnalyzerV2(LanguageVersion.CSharp6, [source], diagnostics); await VerifyAnalyzerV3(LanguageVersion.CSharp6, [source], diagnostics); } /// /// Runs code for analysis, against xUnit.net v2 and v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static async Task VerifyAnalyzer( LanguageVersion languageVersion, string source, params DiagnosticResult[] diagnostics) { await VerifyAnalyzerV2(languageVersion, [source], diagnostics); await VerifyAnalyzerV3(languageVersion, [source], diagnostics); } /// /// Runs code for analysis, against xUnit.net v2 and v3, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static async Task VerifyAnalyzer( string[] sources, params DiagnosticResult[] diagnostics) { await VerifyAnalyzerV2(LanguageVersion.CSharp6, sources, diagnostics); await VerifyAnalyzerV3(LanguageVersion.CSharp6, sources, diagnostics); } /// /// Runs code for analysis, against xUnit.net v2 and v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static async Task VerifyAnalyzer( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { await VerifyAnalyzerV2(languageVersion, sources, diagnostics); await VerifyAnalyzerV3(languageVersion, sources, diagnostics); } // ----- v2 ----- /// /// Runs code for analysis, against xUnit.net v2, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV2( string source, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV2(LanguageVersion.CSharp6, [source], diagnostics); /// /// Runs code for analysis, against xUnit.net v2, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV2( LanguageVersion languageVersion, string source, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV2(languageVersion, [source], diagnostics); /// /// Runs code for analysis, against xUnit.net v2, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV2( string[] sources, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV2(LanguageVersion.CSharp6, sources, diagnostics); /// /// Runs code for analysis, against xUnit.net v2, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV2( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { var test = new TestV2(languageVersion); foreach (var source in sources) test.TestState.Sources.Add(source); test.TestState.ExpectedDiagnostics.AddRange(diagnostics); return test.RunAsync(); } // ----- v3 ----- /// /// Runs code for analysis, against xUnit.net v3, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV3( string source, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV3(LanguageVersion.CSharp6, [source], diagnostics); /// /// Runs code for analysis, against xUnit.net v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV3( LanguageVersion languageVersion, string source, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV3(languageVersion, [source], diagnostics); /// /// Runs code for analysis, against xUnit.net v3, using C# 6. /// /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV3( string[] sources, params DiagnosticResult[] diagnostics) => VerifyAnalyzerV3(LanguageVersion.CSharp6, sources, diagnostics); /// /// Runs code for analysis, against xUnit.net v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The expected diagnostics (pass none for code that /// should not trigger) public static Task VerifyAnalyzerV3( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { var test = new TestV3(languageVersion); foreach (var source in sources) test.TestState.Sources.Add(source); test.TestState.ExpectedDiagnostics.AddRange(diagnostics); return test.RunAsync(); } } ================================================ FILE: src/xunit.analyzers.tests/Utility/CSharpVerifier.CodeFixes.RunnerUtility.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Testing; public partial class CSharpVerifier { // ----- Multi-version ----- /// /// Verify that a code fix has been applied. Runs against xUnit.net v2 and v3 Runner Utility, using C# 6. /// /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static async Task VerifyCodeFixRunnerUtility( string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) { await VerifyCodeFixV2RunnerUtility(LanguageVersion.CSharp6, before, after, fixerActionKey, diagnostics); await VerifyCodeFixV3RunnerUtility(LanguageVersion.CSharp6, before, after, fixerActionKey, diagnostics); } /// /// Verify that a code fix has been applied. Runs against xUnit.net v2 and v3 Runner Utility, using the /// provided version of C#. /// /// The language version to compile with /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static async Task VerifyCodeFixRunnerUtility( LanguageVersion languageVersion, string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) { await VerifyCodeFixV2RunnerUtility(languageVersion, before, after, fixerActionKey, diagnostics); await VerifyCodeFixV3RunnerUtility(languageVersion, before, after, fixerActionKey, diagnostics); } // ----- v2 ----- /// /// Verify that a code fix has been applied. Runs against xUnit.net v2 Runner Utility, using C# 6. /// /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static Task VerifyCodeFixV2RunnerUtility( string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) => VerifyCodeFixV2RunnerUtility(LanguageVersion.CSharp6, before, after, fixerActionKey, diagnostics); /// /// Verify that a code fix has been applied. Runs against xUnit.net v2 Runner Utility, using the /// provided version of C#. /// /// The language version to compile with /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static Task VerifyCodeFixV2RunnerUtility( LanguageVersion languageVersion, string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) { var newLine = FormattingOptions.NewLine.DefaultValue; var test = new TestV2RunnerUtility(languageVersion) { TestCode = before.Replace("\n", newLine), FixedCode = after.Replace("\n", newLine), CodeActionEquivalenceKey = fixerActionKey, }; test.TestState.ExpectedDiagnostics.AddRange(diagnostics); return test.RunAsync(); } // ----- v3 ----- /// /// Verify that a code fix has been applied. Runs against xUnit.net v3 Runner Utility, using C# 6. /// /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static Task VerifyCodeFixV3RunnerUtility( string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) => VerifyCodeFixV3RunnerUtility(LanguageVersion.CSharp6, before, after, fixerActionKey, diagnostics); /// /// Verify that a code fix has been applied. Runs against xUnit.net v3 Runner Utility, using the /// provided version of C#. /// /// The language version to compile with /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static Task VerifyCodeFixV3RunnerUtility( LanguageVersion languageVersion, string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) { var newLine = FormattingOptions.NewLine.DefaultValue; var test = new TestV3RunnerUtility(languageVersion) { TestCode = before.Replace("\n", newLine), FixedCode = after.Replace("\n", newLine), CodeActionEquivalenceKey = fixerActionKey, }; test.TestState.ExpectedDiagnostics.AddRange(diagnostics); return test.RunAsync(); } } ================================================ FILE: src/xunit.analyzers.tests/Utility/CSharpVerifier.CodeFixes.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Testing; public partial class CSharpVerifier { // ----- Fix All ----- /// /// Verify that "Fix All" correctly applies fixes to all diagnostics in a document. /// Runs against xUnit.net v2 and v3, using C# 6. /// /// The code before the fix (should contain multiple diagnostics) /// The expected code after all fixes are applied /// The key of the fix to run public static async Task VerifyCodeFixFixAll( string before, string after, string fixerActionKey) { await VerifyCodeFixV2FixAll(LanguageVersion.CSharp6, before, after, fixerActionKey); await VerifyCodeFixV3FixAll(LanguageVersion.CSharp6, before, after, fixerActionKey); } /// /// Verify that "Fix All" correctly applies fixes to all diagnostics in a document. /// Runs against xUnit.net v2 and v3, using the provided version of C#. /// /// The language version to compile with /// The code before the fix (should contain multiple diagnostics) /// The expected code after all fixes are applied /// The key of the fix to run public static async Task VerifyCodeFixFixAll( LanguageVersion languageVersion, string before, string after, string fixerActionKey) { await VerifyCodeFixV2FixAll(languageVersion, before, after, fixerActionKey); await VerifyCodeFixV3FixAll(languageVersion, before, after, fixerActionKey); } /// /// Verify that "Fix All" correctly applies fixes to all diagnostics in a document. /// Runs against xUnit.net v2, using C# 6. /// /// The code before the fix (should contain multiple diagnostics) /// The expected code after all fixes are applied /// The key of the fix to run public static Task VerifyCodeFixV2FixAll( string before, string after, string fixerActionKey) => VerifyCodeFixV2FixAll(LanguageVersion.CSharp6, before, after, fixerActionKey); /// /// Verify that "Fix All" correctly applies fixes to all diagnostics in a document. /// Runs against xUnit.net v2, using the provided version of C#. /// /// The language version to compile with /// The code before the fix (should contain multiple diagnostics) /// The expected code after all fixes are applied /// The key of the fix to run public static Task VerifyCodeFixV2FixAll( LanguageVersion languageVersion, string before, string after, string fixerActionKey) { var newLine = FormattingOptions.NewLine.DefaultValue; var test = new TestV2(languageVersion) { TestCode = before.Replace("\n", newLine), CodeActionEquivalenceKey = fixerActionKey, }; test.FixedState.Sources.Add(after.Replace("\n", newLine)); test.BatchFixedState.Sources.Add(after.Replace("\n", newLine)); return test.RunAsync(); } /// /// Verify that "Fix All" correctly applies fixes to all diagnostics in a document. /// Runs against xUnit.net v3, using C# 6. /// /// The code before the fix (should contain multiple diagnostics) /// The expected code after all fixes are applied /// The key of the fix to run public static Task VerifyCodeFixV3FixAll( string before, string after, string fixerActionKey) => VerifyCodeFixV3FixAll(LanguageVersion.CSharp6, before, after, fixerActionKey); /// /// Verify that "Fix All" correctly applies fixes to all diagnostics in a document. /// Runs against xUnit.net v3, using the provided version of C#. /// /// The language version to compile with /// The code before the fix (should contain multiple diagnostics) /// The expected code after all fixes are applied /// The key of the fix to run public static Task VerifyCodeFixV3FixAll( LanguageVersion languageVersion, string before, string after, string fixerActionKey) { var newLine = FormattingOptions.NewLine.DefaultValue; var test = new TestV3(languageVersion) { TestCode = before.Replace("\n", newLine), CodeActionEquivalenceKey = fixerActionKey, }; test.FixedState.Sources.Add(after.Replace("\n", newLine)); test.BatchFixedState.Sources.Add(after.Replace("\n", newLine)); return test.RunAsync(); } // ----- Multi-version ----- /// /// Verify that a code fix has been applied. Runs against xUnit.net v2 and v3, using C# 6. /// /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static async Task VerifyCodeFix( string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) { await VerifyCodeFixV2(LanguageVersion.CSharp6, before, after, fixerActionKey, diagnostics); await VerifyCodeFixV3(LanguageVersion.CSharp6, before, after, fixerActionKey, diagnostics); } /// /// Verify that a code fix has been applied. Runs against xUnit.net v2 and v3, using the /// provided version of C#. /// /// The language version to compile with /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static async Task VerifyCodeFix( LanguageVersion languageVersion, string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) { await VerifyCodeFixV2(languageVersion, before, after, fixerActionKey, diagnostics); await VerifyCodeFixV3(languageVersion, before, after, fixerActionKey, diagnostics); } // ----- v2 ----- /// /// Verify that a code fix has been applied. Runs against xUnit.net v2, using C# 6. /// /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static Task VerifyCodeFixV2( string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) => VerifyCodeFixV2(LanguageVersion.CSharp6, before, after, fixerActionKey, diagnostics); /// /// Verify that a code fix has been applied. Runs against xUnit.net v2, using the /// provided version of C#. /// /// The language version to compile with /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static Task VerifyCodeFixV2( LanguageVersion languageVersion, string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) { var newLine = FormattingOptions.NewLine.DefaultValue; var test = new TestV2(languageVersion) { TestCode = before.Replace("\n", newLine), FixedCode = after.Replace("\n", newLine), CodeActionEquivalenceKey = fixerActionKey, }; test.TestState.ExpectedDiagnostics.AddRange(diagnostics); return test.RunAsync(); } // ----- v3 ----- /// /// Verify that a code fix has been applied. Runs against xUnit.net v3, using C# 6. /// /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static Task VerifyCodeFixV3( string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) => VerifyCodeFixV3(LanguageVersion.CSharp6, before, after, fixerActionKey, diagnostics); /// /// Verify that a code fix has been applied. Runs against xUnit.net v3, using the /// provided version of C#. /// /// The language version to compile with /// The code before the fix /// The expected code after the fix /// The key of the fix to run /// Any expected diagnostics that still exist after the fix public static Task VerifyCodeFixV3( LanguageVersion languageVersion, string before, string after, string fixerActionKey, params DiagnosticResult[] diagnostics) { var newLine = FormattingOptions.NewLine.DefaultValue; var test = new TestV3(languageVersion) { TestCode = before.Replace("\n", newLine), FixedCode = after.Replace("\n", newLine), CodeActionEquivalenceKey = fixerActionKey, }; test.TestState.ExpectedDiagnostics.AddRange(diagnostics); return test.RunAsync(); } } ================================================ FILE: src/xunit.analyzers.tests/Utility/CSharpVerifier.Suppressors.cs ================================================ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; public partial class CSharpVerifier { // ----- Multi-version ----- /// /// Verify that an analyzer was used to suppress another analyzers. Runs against /// xUnit.net v2 and v3, using C# 6. /// /// The code to verify /// The analyzer that is expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static async Task VerifySuppressor( string source, DiagnosticAnalyzer suppressedAnalyzer, params DiagnosticResult[] diagnostics) { await VerifySuppressorV2(LanguageVersion.CSharp6, [source], [suppressedAnalyzer], diagnostics); await VerifySuppressorV3(LanguageVersion.CSharp6, [source], [suppressedAnalyzer], diagnostics); } /// /// Verify that an analyzer was used to suppress another analyzers. Runs against /// xUnit.net v2 and v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The analyzer that is expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static async Task VerifySuppressor( LanguageVersion languageVersion, string source, DiagnosticAnalyzer suppressedAnalyzer, params DiagnosticResult[] diagnostics) { await VerifySuppressorV2(languageVersion, [source], [suppressedAnalyzer], diagnostics); await VerifySuppressorV3(languageVersion, [source], [suppressedAnalyzer], diagnostics); } /// /// Verify that an analyzer was used to suppress one or more other analyzers. Runs against /// xUnit.net v2 and v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The analyzer(s) that are expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static async Task VerifySuppressor( LanguageVersion languageVersion, string[] sources, DiagnosticAnalyzer[] suppressedAnalyzers, params DiagnosticResult[] diagnostics) { await VerifySuppressorV2(languageVersion, sources, suppressedAnalyzers, diagnostics); await VerifySuppressorV3(languageVersion, sources, suppressedAnalyzers, diagnostics); } /// /// Verify that an analyzer was used to suppress a compiler warning. Runs against /// xUnit.net v2 and v3, using the provided version of C#. Sets CompilerDiagnostics /// to Warnings so that compiler warnings (like CS8618) are included in the analysis. /// /// The language version to compile with /// The code to verify /// Any expected diagnostics that still exist after the suppression public static async Task VerifyCompilerWarningSuppressor( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { await VerifyCompilerWarningSuppressorV2(languageVersion, sources, diagnostics); await VerifyCompilerWarningSuppressorV3(languageVersion, sources, diagnostics); } // ----- v2 ----- /// /// Verify that an analyzer was used to suppress another analyzers. Runs against /// xUnit.net v2, using C# 6. /// /// The code to verify /// The analyzer that is expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static Task VerifySuppressorV2( string source, DiagnosticAnalyzer suppressedAnalyzer, params DiagnosticResult[] diagnostics) => VerifySuppressorV2(LanguageVersion.CSharp6, [source], [suppressedAnalyzer], diagnostics); /// /// Verify that an analyzer was used to suppress another analyzers. Runs against /// xUnit.net v2, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The analyzer that is expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static Task VerifySuppressorV2( LanguageVersion languageVersion, string source, DiagnosticAnalyzer suppressedAnalyzer, params DiagnosticResult[] diagnostics) => VerifySuppressorV2(languageVersion, [source], [suppressedAnalyzer], diagnostics); /// /// Verify that an analyzer was used to suppress one or more other analyzers. Runs against /// xUnit.net v2, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The analyzer(s) that are expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static Task VerifySuppressorV2( LanguageVersion languageVersion, string[] sources, DiagnosticAnalyzer[] suppressedAnalyzers, params DiagnosticResult[] diagnostics) { var test = new TestV2(languageVersion); foreach (var suppressedAnalyzer in suppressedAnalyzers) test.AddDiagnosticAnalyzer(suppressedAnalyzer); foreach (var source in sources) test.TestState.Sources.Add(source); test.TestState.ExpectedDiagnostics.AddRange(diagnostics); test.TestState.OutputKind = OutputKind.ConsoleApplication; test.TestState.Sources.Add("internal class Program { public static void Main() { } }"); return test.RunAsync(); } /// /// Verify that an analyzer was used to suppress a compiler warning. Runs against /// xUnit.net v2, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// Any expected diagnostics that still exist after the suppression public static Task VerifyCompilerWarningSuppressorV2( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { var test = new TestV2(languageVersion); foreach (var source in sources) test.TestState.Sources.Add(source); test.CompilerDiagnostics = CompilerDiagnostics.Warnings; test.TestState.ExpectedDiagnostics.AddRange(diagnostics); test.TestState.OutputKind = OutputKind.ConsoleApplication; test.TestState.Sources.Add("internal class Program { public static void Main() { } }"); test.SolutionTransforms.Add((solution, projectId) => { var project = solution.GetProject(projectId)!; var compilationOptions = project.CompilationOptions!; compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( compilationOptions.SpecificDiagnosticOptions.SetItem("CS1701", ReportDiagnostic.Suppress) ); return solution.WithProjectCompilationOptions(projectId, compilationOptions); }); return test.RunAsync(); } // ----- v3 ----- /// /// Verify that an analyzer was used to suppress another analyzers. Runs against /// xUnit.net v3, using C# 6. /// /// The code to verify /// The analyzer that is expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static Task VerifySuppressorV3( string source, DiagnosticAnalyzer suppressedAnalyzer, params DiagnosticResult[] diagnostics) => VerifySuppressorV3(LanguageVersion.CSharp6, [source], [suppressedAnalyzer], diagnostics); /// /// Verify that an analyzer was used to suppress another analyzers. Runs against /// xUnit.net v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The analyzer that is expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static Task VerifySuppressorV3( LanguageVersion languageVersion, string source, DiagnosticAnalyzer suppressedAnalyzer, params DiagnosticResult[] diagnostics) => VerifySuppressorV3(languageVersion, [source], [suppressedAnalyzer], diagnostics); /// /// Verify that an analyzer was used to suppress one or more other analyzers. Runs against /// xUnit.net v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// The analyzer(s) that are expected to be suppressed /// Any expected diagnostics that still exist after the suppression public static Task VerifySuppressorV3( LanguageVersion languageVersion, string[] sources, DiagnosticAnalyzer[] suppressedAnalyzers, params DiagnosticResult[] diagnostics) { var test = new TestV3(languageVersion); foreach (var suppressedAnalyzer in suppressedAnalyzers) test.AddDiagnosticAnalyzer(suppressedAnalyzer); foreach (var source in sources) test.TestState.Sources.Add(source); test.TestState.ExpectedDiagnostics.AddRange(diagnostics); test.TestState.OutputKind = OutputKind.ConsoleApplication; test.TestState.Sources.Add("internal class Program { public static void Main() { } }"); return test.RunAsync(); } /// /// Verify that an analyzer was used to suppress a compiler warning. Runs against /// xUnit.net v3, using the provided version of C#. /// /// The language version to compile with /// The code to verify /// Any expected diagnostics that still exist after the suppression public static Task VerifyCompilerWarningSuppressorV3( LanguageVersion languageVersion, string[] sources, params DiagnosticResult[] diagnostics) { var test = new TestV3(languageVersion); foreach (var source in sources) test.TestState.Sources.Add(source); test.CompilerDiagnostics = CompilerDiagnostics.Warnings; test.TestState.ExpectedDiagnostics.AddRange(diagnostics); test.TestState.OutputKind = OutputKind.ConsoleApplication; test.TestState.Sources.Add("internal class Program { public static void Main() { } }"); return test.RunAsync(); } } ================================================ FILE: src/xunit.analyzers.tests/Utility/CSharpVerifier.cs ================================================ using System.Collections.Generic; using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Testing.Verifiers; using Microsoft.CodeAnalysis.Text; public partial class CSharpVerifier where TAnalyzer : DiagnosticAnalyzer, new() { /// /// Creates a diagnostic result for the diagnostic referenced in . /// public static DiagnosticResult Diagnostic() => CSharpCodeFixVerifier.Diagnostic(); /// /// Creates a diagnostic result for the given diagnostic ID. /// /// The diagnostic ID public static DiagnosticResult Diagnostic(string diagnosticId) => CSharpCodeFixVerifier.Diagnostic(diagnosticId); /// /// Creates a diagnostic result for an expected compiler error. /// /// The compiler error code (e.g., CS0619) public static DiagnosticResult CompilerError(string errorIdentifier) => new(errorIdentifier, DiagnosticSeverity.Error); class TestBase : CSharpCodeFixTest where TVerifier : XunitVerifier, new() { List additionalDiagnosticAnalyzers = new(); protected TestBase( LanguageVersion languageVersion, ReferenceAssemblies referenceAssemblies) { LanguageVersion = languageVersion; ReferenceAssemblies = referenceAssemblies; // Ensure all fixed source matches the inline source (tabs, not spaces) TestState.AnalyzerConfigFiles.Add( ( "/.editorconfig", SourceText.From(""" [*] indent_style = tab """) ) ); // Diagnostics are reported in both normal and generated code TestBehaviors |= TestBehaviors.SkipGeneratedCodeCheck; // Tests that check for messages should run independent of current system culture. CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; } public LanguageVersion LanguageVersion { get; } public void AddDiagnosticAnalyzer(DiagnosticAnalyzer analyzer) => additionalDiagnosticAnalyzers.Add(analyzer); protected override IEnumerable GetCodeFixProviders() { var analyzer = new TAnalyzer(); foreach (var provider in CodeFixProviderDiscovery.GetCodeFixProviders(Language)) if (analyzer.SupportedDiagnostics.Any(diagnostic => provider.FixableDiagnosticIds.Contains(diagnostic.Id))) yield return provider; } protected override IEnumerable GetDiagnosticAnalyzers() { yield return new TAnalyzer(); foreach (var diagnosticAnalyzer in additionalDiagnosticAnalyzers) yield return diagnosticAnalyzer; } protected override ParseOptions CreateParseOptions() => new CSharpParseOptions(LanguageVersion, DocumentationMode.Diagnose); } class TestV2 : TestBase { public TestV2(LanguageVersion languageVersion) : base(languageVersion, CodeAnalyzerHelper.CurrentXunitV2) { } } class TestV2RunnerUtility : TestBase { public TestV2RunnerUtility(LanguageVersion languageVersion) : base(languageVersion, CodeAnalyzerHelper.CurrentXunitV2RunnerUtility) { } } class TestV3 : TestBase { public TestV3(LanguageVersion languageVersion) : base(languageVersion, CodeAnalyzerHelper.CurrentXunitV3) { } } class TestV3RunnerUtility : TestBase { public TestV3RunnerUtility(LanguageVersion languageVersion) : base(languageVersion, CodeAnalyzerHelper.CurrentXunitV3RunnerUtility) { } } } ================================================ FILE: src/xunit.analyzers.tests/Utility/CodeAnalyzerHelper.cs ================================================ using System.Collections.Immutable; using Microsoft.CodeAnalysis.Testing; #if !NET472 using System.IO; #endif static class CodeAnalyzerHelper { public static readonly ReferenceAssemblies CurrentXunitV2; public static readonly ReferenceAssemblies CurrentXunitV2RunnerUtility; public static readonly ReferenceAssemblies CurrentXunitV3; public static readonly ReferenceAssemblies CurrentXunitV3RunnerUtility; // When changing any references here, make sure to update xunit.analyzers.tests.csproj. // We either need a direct reference (like xunit.core) or a package download (like everything else) // in order for this list to work most efficiently. static CodeAnalyzerHelper() { #if NET472 var defaultAssemblies = ReferenceAssemblies.NetFramework.Net472.Default; #else // Can't use ReferenceAssemblies.Net.Net80 because it's too new for Microsoft.CodeAnalysis 3.11.0 var defaultAssemblies = new ReferenceAssemblies( "net8.0", new PackageIdentity("Microsoft.NETCore.App.Ref", "8.0.10"), Path.Combine("ref", "net8.0") ); #endif CurrentXunitV2 = defaultAssemblies.AddPackages( ImmutableArray.Create( new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "6.0.0"), new PackageIdentity("Microsoft.Extensions.Primitives", "6.0.0"), new PackageIdentity("System.Collections.Immutable", "1.6.0"), new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.4"), new PackageIdentity("xunit.abstractions", "2.0.3"), new PackageIdentity("xunit.assert", "2.9.3-pre.4"), new PackageIdentity("xunit.core", "2.9.3-pre.4") ) ); CurrentXunitV2RunnerUtility = defaultAssemblies.AddPackages( ImmutableArray.Create( new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "6.0.0"), new PackageIdentity("Microsoft.Extensions.Primitives", "6.0.0"), new PackageIdentity("System.Collections.Immutable", "1.6.0"), new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.4"), new PackageIdentity("xunit.abstractions", "2.0.3"), new PackageIdentity("xunit.runner.utility", "2.9.3-pre.4") ) ); CurrentXunitV3 = defaultAssemblies.AddPackages( ImmutableArray.Create( new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "6.0.0"), new PackageIdentity("Microsoft.Extensions.Primitives", "6.0.0"), new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.4"), new PackageIdentity("xunit.v3.assert", "3.2.2-pre.18"), new PackageIdentity("xunit.v3.common", "3.2.2-pre.18"), new PackageIdentity("xunit.v3.extensibility.core", "3.2.2-pre.18"), new PackageIdentity("xunit.v3.runner.common", "3.2.2-pre.18") ) ); CurrentXunitV3RunnerUtility = defaultAssemblies.AddPackages( ImmutableArray.Create( new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "6.0.0"), new PackageIdentity("Microsoft.Extensions.Primitives", "6.0.0"), new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.4"), new PackageIdentity("xunit.v3.common", "3.2.2-pre.18"), new PackageIdentity("xunit.v3.runner.utility", "3.2.2-pre.18") ) ); } } ================================================ FILE: src/xunit.analyzers.tests/Utility/CodeFixProviderDiscovery.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.VisualStudio.Composition; using Xunit.Analyzers.Fixes; static class CodeFixProviderDiscovery { static readonly Lazy ExportProviderFactory; static CodeFixProviderDiscovery() { ExportProviderFactory = new Lazy( () => { var discovery = new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true); var parts = Task.Run(() => discovery.CreatePartsAsync(typeof(CodeAnalysisExtensions).Assembly)).GetAwaiter().GetResult(); var catalog = ComposableCatalog.Create(Resolver.DefaultInstance).AddParts(parts); var configuration = CompositionConfiguration.Create(catalog); var runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration); return runtimeComposition.CreateExportProviderFactory(); }, LazyThreadSafetyMode.ExecutionAndPublication ); } public static IEnumerable GetCodeFixProviders(string language) { var exportProvider = ExportProviderFactory.Value.CreateExportProvider(); var exports = exportProvider.GetExports(); return exports.Where(export => export.Metadata.Languages.Contains(language)).Select(export => export.Value); } class LanguageMetadata { public LanguageMetadata(IDictionary data) { if (!data.TryGetValue(nameof(ExportCodeFixProviderAttribute.Languages), out var languages)) languages = new string[0]; Languages = ((string[])languages).ToImmutableArray(); } public ImmutableArray Languages { get; } } } ================================================ FILE: src/xunit.analyzers.tests/Utility/XunitVerifier.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using Xunit; using Xunit.Sdk; namespace Microsoft.CodeAnalysis.Testing.Verifiers; public class XunitVerifier : IVerifier { public XunitVerifier() : this(ImmutableStack.Empty) { } protected XunitVerifier(ImmutableStack context) => Context = context ?? throw new ArgumentNullException(nameof(context)); protected ImmutableStack Context { get; } protected virtual string CreateMessage(string? message) { foreach (var frame in Context) message = "Context: " + frame + Environment.NewLine + message; return message ?? string.Empty; } void IVerifier.Empty( string collectionName, IEnumerable collection) { var tracker = CollectionTracker.Wrap(collection); using var enumerator = tracker.GetEnumerator(); if (enumerator.MoveNext()) throw EmptyException.ForNamedNonEmptyCollection(tracker.FormatStart(), collectionName); } void IVerifier.Equal( T expected, T actual, string? message) { if (EqualityComparer.Default.Equals(expected, actual)) return; if (message is null && Context.IsEmpty) throw EqualException.ForMismatchedValues(ArgumentFormatter.Format(expected), ArgumentFormatter.Format(actual)); else throw EqualException.ForMismatchedValuesWithMessage(expected, actual, CreateMessage(message)); } [DoesNotReturn] void IVerifier.Fail(string? message) => Assert.Fail(message is null ? "" : CreateMessage(message)); void IVerifier.False( [DoesNotReturnIf(true)] bool assert, string? message) { if (message is null && Context.IsEmpty) Assert.False(assert); else Assert.False(assert, CreateMessage(message)); } void IVerifier.LanguageIsSupported(string language) => Assert.False(language != LanguageNames.CSharp && language != LanguageNames.VisualBasic, CreateMessage($"Unsupported Language: '{language}'")); void IVerifier.NotEmpty( string collectionName, IEnumerable collection) { using var enumerator = collection.GetEnumerator(); if (!enumerator.MoveNext()) throw NotEmptyException.ForNamedNonEmptyCollection(collectionName); } IVerifier IVerifier.PushContext(string context) { Assert.IsAssignableFrom(this); return new XunitVerifier(Context.Push(context)); } void IVerifier.SequenceEqual( IEnumerable expected, IEnumerable actual, IEqualityComparer? equalityComparer, string? message) { var comparer = new SequenceEqualEnumerableEqualityComparer(equalityComparer); var areEqual = comparer.Equals(expected, actual); if (!areEqual) throw EqualException.ForMismatchedValuesWithMessage(expected, actual, CreateMessage(message)); } void IVerifier.True( [DoesNotReturnIf(false)] bool assert, string? message) { if (message is null && Context.IsEmpty) Assert.True(assert); else Assert.True(assert, CreateMessage(message)); } sealed class SequenceEqualEnumerableEqualityComparer : IEqualityComparer?> { readonly IEqualityComparer itemEqualityComparer; public SequenceEqualEnumerableEqualityComparer(IEqualityComparer? itemEqualityComparer) => this.itemEqualityComparer = itemEqualityComparer ?? EqualityComparer.Default; public bool Equals(IEnumerable? x, IEnumerable? y) { if (ReferenceEquals(x, y)) return true; if (x is null || y is null) return false; return x.SequenceEqual(y, itemEqualityComparer); } public int GetHashCode(IEnumerable? obj) { if (obj is null) return 0; // From System.Tuple // // The suppression is required due to an invalid contract in IEqualityComparer // https://github.com/dotnet/runtime/issues/30998 return obj .Select(item => itemEqualityComparer.GetHashCode(item!)) .Aggregate( 0, (aggHash, nextHash) => ((aggHash << 5) + aggHash) ^ nextHash ); } } } ================================================ FILE: src/xunit.analyzers.tests/Utility/XunitVerifierV2.cs ================================================ using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.Testing.Verifiers; public class XunitVerifierV2 : XunitVerifier { public XunitVerifierV2() : base(ImmutableStack.Create("Testing against xUnit.net v2")) { } } ================================================ FILE: src/xunit.analyzers.tests/Utility/XunitVerifierV3.cs ================================================ using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.Testing.Verifiers; public class XunitVerifierV3 : XunitVerifier { public XunitVerifierV3() : base(ImmutableStack.Create("Testing against xUnit.net v3")) { } } ================================================ FILE: src/xunit.analyzers.tests/xunit.analyzers.tests.csproj ================================================ xunit.analyzers.tests.$(TargetFramework) Exe xunit.analyzers.tests Xunit.Analyzers net8.0;net472 ================================================ FILE: src/xunit.analyzers.tests/xunit.runner.json ================================================ { "$schema": "https://xUnit.net/schema/current/xunit.runner.schema.json", "diagnosticMessages": true, "parallelizeAssembly": true, "parallelizeTestCollections": true, "shadowCopy": false } ================================================ FILE: tools/SignClient/appsettings.json ================================================ { "SignClient": { "AzureAd": { "AADInstance": "https://login.microsoftonline.com/", "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" }, "Service": { "Url": "https://codesign.dotnetfoundation.org/", "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" } } } ================================================ FILE: tools/builder/.vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [] } ================================================ FILE: tools/builder/.vscode/settings.json ================================================ { "cSpell.words": [ "etailed", "inimal", "msbuild", "nbgv", "netfx", "nuget", "nupkg", "ormal", "uiet" ], "files.exclude": { "**/.git": true, "**/.DS_Store": true, "**/bin": true, "**/obj": true } } ================================================ FILE: tools/builder/.vscode/tasks.json ================================================ { "version": "2.0.0", "tasks": [ { "label": "Build (builder)", "type": "process", "command": "dotnet", "args": [ "build" ], "options": { "cwd": "${workspaceRoot}" }, "group": "build", "presentation": { "focus": true }, "problemMatcher": "$msCompile" }, { "label": "Build (analyzers)", "type": "process", "command": "dotnet", "args": [ "run", "--project", "${workspaceRoot}", "--no-launch-profile", "--", "Build" ], "options": { "cwd": "${workspaceRoot}" }, "group": "build", "presentation": { "focus": true }, "problemMatcher": [] }, { "label": "Unit test (analyzers, .NET Core)", "type": "process", "command": "dotnet", "args": [ "run", "--project", "${workspaceRoot}", "--no-launch-profile", "--", "TestCore" ], "options": { "cwd": "${workspaceRoot}" }, "group": "build", "presentation": { "focus": true }, "problemMatcher": [] }, { "label": "Unit test (analyzers, .NET Framework)", "type": "process", "command": "dotnet", "args": [ "run", "--project", "${workspaceRoot}", "--no-launch-profile", "--", "TestFx" ], "options": { "cwd": "${workspaceRoot}" }, "group": "build", "presentation": { "focus": true }, "problemMatcher": [] } ] } ================================================ FILE: tools/builder/Program.cs ================================================ using System.Threading.Tasks; using McMaster.Extensions.CommandLineUtils; using Xunit.BuildTools.Models; namespace Xunit.BuildTools; public class Program { public static Task Main(string[] args) => CommandLineApplication.ExecuteAsync(args); } ================================================ FILE: tools/builder/build.csproj ================================================ enable Exe net10.0 true ================================================ FILE: tools/builder/models/BuildContext.cs ================================================ using System; using System.Collections.Generic; using System.IO; namespace Xunit.BuildTools.Models; public partial class BuildContext { string? consoleRunner; public string ConsoleRunner => consoleRunner ?? throw new InvalidOperationException($"Tried to retrieve unset {nameof(BuildContext)}.{nameof(ConsoleRunner)}"); public partial IReadOnlyList GetSkippedAnalysisFolders() => Array.Empty(); partial void Initialize() { consoleRunner = Path.Combine(NuGetPackageCachePath, "xunit.v3.runner.console", "3.2.2-pre.18", "tools", "net472", "xunit.v3.runner.console.exe"); if (!File.Exists(consoleRunner)) throw new InvalidOperationException($"Cannot find console runner at '{consoleRunner}'"); } } ================================================ FILE: tools/builder/targets/Build.cs ================================================ using System.IO; using System.Threading.Tasks; using Xunit.BuildTools.Models; namespace Xunit.BuildTools.Targets; public static partial class Build { public static partial async Task PerformBuild(BuildContext context) { context.BuildStep("Compiling binaries"); var buildLog = Path.Combine(context.BuildArtifactsFolder, "build.binlog"); await context.Exec("dotnet", $"msbuild -nologo -maxCpuCount -restore:False -verbosity:{context.Verbosity} -property:Configuration={context.ConfigurationText} -binaryLogger:{buildLog}"); } } ================================================ FILE: tools/builder/targets/Packages.cs ================================================ using System.IO; using System.Linq; using System.Threading.Tasks; using Xunit.BuildTools.Models; namespace Xunit.BuildTools.Targets; public static partial class Packages { public static async Task OnExecute(BuildContext context) { context.BuildStep("Creating NuGet packages"); // Clean up any existing packages to force re-packing var packageFiles = Directory.GetFiles(context.PackageOutputFolder, "*.nupkg"); foreach (var packageFile in packageFiles) File.Delete(packageFile); await context.Exec("dotnet", $"pack --nologo --no-build --configuration {context.ConfigurationText} --output {context.PackageOutputFolder} --verbosity {context.Verbosity} src/xunit.analyzers -p:NuspecFile=xunit.analyzers.nuspec -tl:off"); } } ================================================ FILE: tools/builder/targets/SignAssemblies.cs ================================================ using System; using System.IO; using System.Linq; using System.Threading.Tasks; using Xunit.BuildTools.Models; namespace Xunit.BuildTools.Targets; public static partial class SignAssemblies { public static Task OnExecute(BuildContext context) { // Check early because we don't need to make copies or show the banner for non-signed scenarios if (!context.CanSign) return Task.CompletedTask; context.BuildStep("Signing binaries"); // Note that any changes to .nuspec files means this list needs to be updated, and nuspec files should // always reference the original signed paths, and not dependency copies (i.e., xunit.v3.common.dll) var binaries = new[] { Path.Combine(context.BaseFolder, "src", "xunit.analyzers", "bin", context.ConfigurationText, "netstandard2.0", "xunit.analyzers.dll"), Path.Combine(context.BaseFolder, "src", "xunit.analyzers.fixes", "bin", context.ConfigurationText, "netstandard2.0", "xunit.analyzers.fixes.dll"), }.Select(unsignedPath => { var unsignedFolder = Path.GetDirectoryName(unsignedPath) ?? throw new InvalidOperationException($"Path '{unsignedPath}' did not have a folder"); var signedFolder = Path.Combine(unsignedFolder, "signed"); Directory.CreateDirectory(signedFolder); var signedPath = Path.Combine(signedFolder, Path.GetFileName(unsignedPath)); File.Copy(unsignedPath, signedPath, overwrite: true); return signedPath; }).ToArray(); return context.SignFiles(context.BaseFolder, binaries); } } ================================================ FILE: tools/builder/targets/Test.cs ================================================ using System; using System.IO; using System.Linq; using System.Threading.Tasks; using Xunit.BuildTools.Models; namespace Xunit.BuildTools.Targets; [Target( BuildTarget.Test, BuildTarget.Build )] public class Test { public static Task OnExecute(BuildContext context) { context.BuildStep("Running tests"); var testDLLs = Directory .GetFiles(Path.Join(context.BaseFolder, "src"), "*.tests*.csproj", SearchOption.AllDirectories) .Select(csproj => '"' + Path.Combine(Path.GetDirectoryName(csproj)!, "bin", context.ConfigurationText, "net8.0", Path.GetFileNameWithoutExtension(csproj) + ".net8.0.dll") + '"'); if (context.NeedMono) { context.WriteLineColor(ConsoleColor.Yellow, "Skipping .NET Framework tests on Mono"); Console.WriteLine(); } else testDLLs = testDLLs.Concat( Directory .GetFiles(Path.Join(context.BaseFolder, "src"), "*.tests*.csproj", SearchOption.AllDirectories) .Select(csproj => '"' + Path.Combine(Path.GetDirectoryName(csproj)!, "bin", context.ConfigurationText, "net472", Path.GetFileNameWithoutExtension(csproj) + ".net472.exe") + '"') ); return context.Exec(context.ConsoleRunner, $"{string.Join(" ", testDLLs)} -ctrf {Path.Join(context.TestOutputFolder, "results.ctrf")}"); } } ================================================ FILE: version.json ================================================ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", "version": "1.28.0-pre.{height}", "nugetPackageVersion": { "semVer": 2 }, "publicReleaseRefSpec": [ "^refs/heads/.*$" ] } ================================================ FILE: xunit.analyzers.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33122.133 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3CAA9D63-0806-4CB1-8C32-AA8045E1F441}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig src\Directory.Build.props = src\Directory.Build.props src\Directory.Build.targets = src\Directory.Build.targets README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xunit.analyzers", "src\xunit.analyzers\xunit.analyzers.csproj", "{897D042D-2C87-412F-805E-2905A1A38C83}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xunit.analyzers.fixes", "src\xunit.analyzers.fixes\xunit.analyzers.fixes.csproj", "{B8612BA7-3EC9-4D08-82D6-ABDBF73E6BF6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xunit.analyzers.tests", "src\xunit.analyzers.tests\xunit.analyzers.tests.csproj", "{58C228AC-60BB-4928-AE3A-282CDD2EA912}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "compat", "compat", "{C4169EC8-5C1D-416C-82B2-B9084C8F9D77}" ProjectSection(SolutionItems) = preProject src\compat\Directory.Build.props = src\compat\Directory.Build.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xunit.analyzers.latest", "src\compat\xunit.analyzers.latest\xunit.analyzers.latest.csproj", "{ABFA42B0-3347-43C8-8D6B-13511DECED5F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xunit.analyzers.latest.fixes", "src\compat\xunit.analyzers.latest.fixes\xunit.analyzers.latest.fixes.csproj", "{B3D3E63A-6E6E-4673-8C6A-25030BF24378}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xunit.analyzers.latest.tests", "src\compat\xunit.analyzers.latest.tests\xunit.analyzers.latest.tests.csproj", "{86C1AC30-64A1-47AD-83F1-D86E30D4DE20}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {897D042D-2C87-412F-805E-2905A1A38C83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {897D042D-2C87-412F-805E-2905A1A38C83}.Debug|Any CPU.Build.0 = Debug|Any CPU {897D042D-2C87-412F-805E-2905A1A38C83}.Release|Any CPU.ActiveCfg = Release|Any CPU {897D042D-2C87-412F-805E-2905A1A38C83}.Release|Any CPU.Build.0 = Release|Any CPU {B8612BA7-3EC9-4D08-82D6-ABDBF73E6BF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8612BA7-3EC9-4D08-82D6-ABDBF73E6BF6}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8612BA7-3EC9-4D08-82D6-ABDBF73E6BF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {B8612BA7-3EC9-4D08-82D6-ABDBF73E6BF6}.Release|Any CPU.Build.0 = Release|Any CPU {58C228AC-60BB-4928-AE3A-282CDD2EA912}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58C228AC-60BB-4928-AE3A-282CDD2EA912}.Debug|Any CPU.Build.0 = Debug|Any CPU {58C228AC-60BB-4928-AE3A-282CDD2EA912}.Release|Any CPU.ActiveCfg = Release|Any CPU {58C228AC-60BB-4928-AE3A-282CDD2EA912}.Release|Any CPU.Build.0 = Release|Any CPU {ABFA42B0-3347-43C8-8D6B-13511DECED5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ABFA42B0-3347-43C8-8D6B-13511DECED5F}.Debug|Any CPU.Build.0 = Debug|Any CPU {ABFA42B0-3347-43C8-8D6B-13511DECED5F}.Release|Any CPU.ActiveCfg = Release|Any CPU {ABFA42B0-3347-43C8-8D6B-13511DECED5F}.Release|Any CPU.Build.0 = Release|Any CPU {B3D3E63A-6E6E-4673-8C6A-25030BF24378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B3D3E63A-6E6E-4673-8C6A-25030BF24378}.Debug|Any CPU.Build.0 = Debug|Any CPU {B3D3E63A-6E6E-4673-8C6A-25030BF24378}.Release|Any CPU.ActiveCfg = Release|Any CPU {B3D3E63A-6E6E-4673-8C6A-25030BF24378}.Release|Any CPU.Build.0 = Release|Any CPU {86C1AC30-64A1-47AD-83F1-D86E30D4DE20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {86C1AC30-64A1-47AD-83F1-D86E30D4DE20}.Debug|Any CPU.Build.0 = Debug|Any CPU {86C1AC30-64A1-47AD-83F1-D86E30D4DE20}.Release|Any CPU.ActiveCfg = Release|Any CPU {86C1AC30-64A1-47AD-83F1-D86E30D4DE20}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {897D042D-2C87-412F-805E-2905A1A38C83} = {3CAA9D63-0806-4CB1-8C32-AA8045E1F441} {B8612BA7-3EC9-4D08-82D6-ABDBF73E6BF6} = {3CAA9D63-0806-4CB1-8C32-AA8045E1F441} {58C228AC-60BB-4928-AE3A-282CDD2EA912} = {3CAA9D63-0806-4CB1-8C32-AA8045E1F441} {C4169EC8-5C1D-416C-82B2-B9084C8F9D77} = {3CAA9D63-0806-4CB1-8C32-AA8045E1F441} {ABFA42B0-3347-43C8-8D6B-13511DECED5F} = {C4169EC8-5C1D-416C-82B2-B9084C8F9D77} {B3D3E63A-6E6E-4673-8C6A-25030BF24378} = {C4169EC8-5C1D-416C-82B2-B9084C8F9D77} {86C1AC30-64A1-47AD-83F1-D86E30D4DE20} = {C4169EC8-5C1D-416C-82B2-B9084C8F9D77} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {601643AE-7617-4470-841B-56F1E8356D87} EndGlobalSection EndGlobal