protected XunitDiagnosticAnalyzer(XunitCapabilities capabilities = null) { if (capabilities == null) { capabilitiesFactory = XunitCapabilities.Create; } else { capabilitiesFactory = c => capabilities; } }
// For testing public TheoryMethodCannotHaveDefaultParameter(XunitCapabilities capabilities) : base(capabilities) { }
// For testing public TheoryMethodCannotHaveParamsArray(XunitCapabilities capabilities) : base(capabilities) { }
public override void Initialize(AnalysisContext context) { context.RegisterCompilationStartAction(compilationStartContext => { var compilation = compilationStartContext.Compilation; var xunitCapabilities = XunitCapabilities.Create(compilation); var xunitSupportsParameterArrays = xunitCapabilities.TheorySupportsParameterArrays; var xunitSupportsDefaultParameterValues = xunitCapabilities.TheorySupportsDefaultParameterValues; var theoryType = compilation.GetTypeByMetadataName(Constants.Types.XunitTheoryAttribute); var inlineDataType = compilation.GetTypeByMetadataName(Constants.Types.XunitInlineDataAttribute); if (theoryType == null || inlineDataType == null) { return; } var objectArrayType = compilation.CreateArrayTypeSymbol(compilation.ObjectType); compilationStartContext.RegisterSymbolAction(symbolContext => { var method = (IMethodSymbol)symbolContext.Symbol; var attributes = method.GetAttributes(); if (!attributes.ContainsAttributeType(theoryType)) { return; } foreach (var attribute in attributes) { symbolContext.CancellationToken.ThrowIfCancellationRequested(); if (attribute.AttributeClass != inlineDataType) { continue; } // Check if the semantic model indicates there are no syntax/compilation errors if (attribute.ConstructorArguments.Length != 1 || !objectArrayType.Equals(attribute.ConstructorArguments.FirstOrDefault().Type)) { continue; } var attributeSyntax = (AttributeSyntax)attribute.ApplicationSyntaxReference.GetSyntax(symbolContext.CancellationToken); var arrayStyle = ParameterArrayStyleType.Initializer; var dataParameterExpressions = GetParameterExpressionsFromArrayArgument(attributeSyntax); if (dataParameterExpressions == null) { arrayStyle = ParameterArrayStyleType.Params; dataParameterExpressions = attributeSyntax.ArgumentList?.Arguments.Select(a => a.Expression).ToList() ?? new List <ExpressionSyntax>(); } 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 var values = dataArrayArgument.IsNull ? ImmutableArray.Create(dataArrayArgument) : dataArrayArgument.Values; if (values.Length < method.Parameters.Count(p => RequiresMatchingValue(p, xunitSupportsParameterArrays, xunitSupportsDefaultParameterValues))) { var builder = ImmutableDictionary.CreateBuilder <string, string>(); builder[ParameterArrayStyle] = arrayStyle.ToString(); symbolContext.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]; // unwrap parameter type when the argument is a parameter list var parameterType = xunitSupportsParameterArrays && parameter.IsParams && parameter.Type is IArrayTypeSymbol arrayParam ? arrayParam.ElementType : parameter.Type; if (parameterType == compilation.ObjectType) { // Everything is assignable to object and 'params object[]' so move on if (xunitSupportsParameterArrays && parameter.IsParams) { valueIdx = values.Length; break; } else { paramIdx++; continue; } } var builder = ImmutableDictionary.CreateBuilder <string, string>(); builder[ParameterIndex] = paramIdx.ToString(); builder[ParameterName] = parameter.Name; var properties = builder.ToImmutable(); var value = values[valueIdx]; if (!value.IsNull) { var isConvertible = DetermineIsConvertible(compilation, value.Type, parameterType); if (!isConvertible) { symbolContext.ReportDiagnostic(Diagnostic.Create( Descriptors.X1010_InlineDataMustMatchTheoryParameters_IncompatibleValueType, dataParameterExpressions[valueIdx].GetLocation(), properties, parameter.Name, SymbolDisplay.ToDisplayString(parameterType))); } } if (value.IsNull && parameterType.IsValueType && parameterType.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T) { symbolContext.ReportDiagnostic(Diagnostic.Create( Descriptors.X1012_InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameter, dataParameterExpressions[valueIdx].GetLocation(), properties, parameter.Name, SymbolDisplay.ToDisplayString(parameterType))); } 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 <string, string>(); builder[ParameterIndex] = valueIdx.ToString(); symbolContext.ReportDiagnostic(Diagnostic.Create( Descriptors.X1011_InlineDataMustMatchTheoryParameters_ExtraValue, dataParameterExpressions[valueIdx].GetLocation(), builder.ToImmutable(), values[valueIdx].ToCSharpString())); } } }, SymbolKind.Method); }); }
// For testing public TheoryMethodCannotHaveDefaultParameter(XunitCapabilities capabilities) : this(c => capabilities) { }
// For testing public TheoryMethodCannotHaveParamsArray(XunitCapabilities capabilities) : this(c => capabilities) { }