diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index cdf4f1397e..7f555c6d88 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -1 +1,5 @@ ; Please do not edit this file manually, it should only be updated through code fix application. +### New Rules +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +CA2250 | Usage | Info | ProvideCorrectArgumentsToFormattingMethodsAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca2250) \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index d53e63aa40..4d3727db65 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1,17 +1,17 @@  - @@ -342,8 +342,8 @@ The format argument that is passed to System.String.Format does not contain a format item that corresponds to each object argument, or vice versa. - - Provide correct arguments to formatting methods + + Incorrect number of arguments passed to this formatting method call Test for NaN correctly @@ -1482,4 +1482,7 @@ Replace with 'string.Contains' + + Some string format items are missing from this formatting method call + \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/ProvideCorrectArgumentsToFormattingMethods.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/ProvideCorrectArgumentsToFormattingMethods.cs index 49388ce992..fab4252186 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/ProvideCorrectArgumentsToFormattingMethods.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/ProvideCorrectArgumentsToFormattingMethods.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text.RegularExpressions; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; @@ -18,38 +20,123 @@ namespace Microsoft.NetCore.Analyzers.Runtime [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class ProvideCorrectArgumentsToFormattingMethodsAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA2241"; + internal const string DefaultRuleId = "CA2241"; + internal const string NotEnoughArgsRuleId = "CA2250"; private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentsToFormattingMethodsTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentsToFormattingMethodsMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableMessageArgs = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentsToFormattingMethodsMessageArgs), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentsToFormattingMethodsDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - internal static DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, - s_localizableTitle, - s_localizableMessage, - DiagnosticCategory.Usage, - RuleLevel.BuildWarningCandidate, - description: s_localizableDescription, - isPortedFxCopRule: true, - isDataflowRule: false); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + // e.g. Console.WriteLine("{0} {1}", 42); + internal static DiagnosticDescriptor NotEnoughArgsRule = DiagnosticDescriptorHelper.Create( + NotEnoughArgsRuleId, + s_localizableTitle, + s_localizableMessageArgs, + DiagnosticCategory.Usage, + RuleLevel.BuildWarningCandidate, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + // e.g. Console.WriteLine("{1}", 42); + internal static DiagnosticDescriptor NotEnoughArgsMissingFormatIndexRule = DiagnosticDescriptorHelper.Create( + NotEnoughArgsRuleId, + s_localizableTitle, + s_localizableMessageArgs, + DiagnosticCategory.Usage, + RuleLevel.BuildWarningCandidate, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + // e.g. Console.WriteLine("{0}", 1, 2) + internal static DiagnosticDescriptor TooManyArgsRule = DiagnosticDescriptorHelper.Create( + DefaultRuleId, + s_localizableTitle, + s_localizableMessageArgs, + DiagnosticCategory.Usage, + RuleLevel.IdeSuggestion, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + // e.g. Console.WriteLine("{1}", 1, 2, 3) + internal static DiagnosticDescriptor TooManyArgsMissingFormatIndexRule = DiagnosticDescriptorHelper.Create( + DefaultRuleId, + s_localizableTitle, + s_localizableMessageArgs, + DiagnosticCategory.Usage, + RuleLevel.IdeSuggestion, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + // e.g. Console.WriteLine("{1}", 1, 2) + internal static DiagnosticDescriptor EnoughArgsMissingFormatIndexRule = DiagnosticDescriptorHelper.Create( + DefaultRuleId, + s_localizableTitle, + s_localizableMessageArgs, + DiagnosticCategory.Usage, + RuleLevel.IdeSuggestion, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + //// e.g. Console.WriteLine("{0} {1}") -> resolves to the overload expecting a string, not a format string + //internal static DiagnosticDescriptor FormatItemInNonStringFormatMethodRule = DiagnosticDescriptorHelper.Create( + // RuleId, + // s_localizableTitle, + // s_localizableMessageArgs, + // DiagnosticCategory.Usage, + // RuleLevel.IdeSuggestion, + // description: s_localizableDescription, + // isPortedFxCopRule: true, + // isDataflowRule: false); + + //// e.g. Console.WriteLine("{1}") + //internal static DiagnosticDescriptor FormatItemInNonStringFormatMethodMissingFormatItemRule = DiagnosticDescriptorHelper.Create( + // RuleId, + // s_localizableTitle, + // s_localizableMessageArgs, + // DiagnosticCategory.Usage, + // RuleLevel.IdeSuggestion, + // description: s_localizableDescription, + // isPortedFxCopRule: true, + // isDataflowRule: false); + + /// + /// This regex is used to remove escaped brackets from the format string before looking for valid {} pairs. + /// + private static readonly Regex s_removeEscapedBracketsRegex = new("{{", RegexOptions.Compiled); + + /// + /// This regex is used to extract the text between the brackets and save the contents in a MatchCollection. + /// + private static readonly Regex s_extractPlaceholdersRegex = new("{(.*?)}", RegexOptions.Compiled); + + public override ImmutableArray SupportedDiagnostics + => ImmutableArray.Create( + NotEnoughArgsRule, + NotEnoughArgsMissingFormatIndexRule, + EnoughArgsMissingFormatIndexRule, + TooManyArgsRule, + TooManyArgsMissingFormatIndexRule); public override void Initialize(AnalysisContext analysisContext) { analysisContext.EnableConcurrentExecution(); analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - analysisContext.RegisterCompilationStartAction(compilationContext => + analysisContext.RegisterCompilationStartAction(context => { - var formatInfo = new StringFormatInfo(compilationContext.Compilation); + var formatInfo = new StringFormatInfo(context.Compilation); - compilationContext.RegisterOperationAction(operationContext => + context.RegisterOperationAction(context => { - var invocation = (IInvocationOperation)operationContext.Operation; + var invocation = (IInvocationOperation)context.Operation; - StringFormatInfo.Info? info = formatInfo.TryGet(invocation.TargetMethod, operationContext); + StringFormatInfo.Info? info = formatInfo.TryGet(invocation.TargetMethod, context); if (info == null || invocation.Arguments.Length <= info.FormatStringIndex) { // not a target method @@ -57,34 +144,36 @@ public override void Initialize(AnalysisContext analysisContext) } IArgumentOperation formatStringArgument = invocation.Arguments[info.FormatStringIndex]; - if (!object.Equals(formatStringArgument?.Value?.Type, formatInfo.String) || + if (!Equals(formatStringArgument?.Value?.Type, formatInfo.String) || !(formatStringArgument?.Value?.ConstantValue.Value is string)) { // wrong argument return; } + // __arglist is not supported here (see https://github.com/dotnet/roslyn/issues/7346) + // but we want to skip it only in C# since VB doesn't support __arglist + if (info.ExpectedStringFormatArgumentCount >= 0 && + invocation.TargetMethod.IsVararg && + invocation.Language == LanguageNames.CSharp) + { + return; + } + var stringFormat = (string)formatStringArgument.Value.ConstantValue.Value; - int expectedStringFormatArgumentCount = GetFormattingArguments(stringFormat); + var stringFormatIndexes = GetStringFormatItemIndexes(stringFormat); - // explicit parameter case + // Target method resolves to an overload with a non variable (e.g. params) argument count + // so we can easily analyze the arguments. if (info.ExpectedStringFormatArgumentCount >= 0) { - // __arglist is not supported here - if (invocation.TargetMethod.IsVararg) - { - // can't deal with this for now. - return; - } - - if (info.ExpectedStringFormatArgumentCount != expectedStringFormatArgumentCount) - { - operationContext.ReportDiagnostic(operationContext.Operation.Syntax.CreateDiagnostic(Rule)); - } - + ReportOnArgumentsMismatch(stringFormat, info.ExpectedStringFormatArgumentCount, context, invocation); return; } + // Target method resolves to an overload with a variable argument count so we need + // to try to understand the number of arguments passed. + // ensure argument is an array IArgumentOperation paramsArgument = invocation.Arguments[info.FormatStringIndex + 1]; if (paramsArgument.ArgumentKind is not ArgumentKind.ParamArray and not ArgumentKind.Explicit) @@ -95,7 +184,7 @@ public override void Initialize(AnalysisContext analysisContext) if (paramsArgument.Value is not IArrayCreationOperation arrayCreation || arrayCreation.GetElementType() is not ITypeSymbol elementType || - !object.Equals(elementType, formatInfo.Object) || + !Equals(elementType, formatInfo.Object) || arrayCreation.DimensionSizes.Length != 1) { // wrong format @@ -112,206 +201,102 @@ public override void Initialize(AnalysisContext analysisContext) // REVIEW: "ElementValues" is a bit confusing where I need to double dot those to get number of elements int actualArgumentCount = intializer.ElementValues.Length; - if (actualArgumentCount != expectedStringFormatArgumentCount) - { - operationContext.ReportDiagnostic(operationContext.Operation.Syntax.CreateDiagnostic(Rule)); - } + ReportOnArgumentsMismatch(stringFormat, actualArgumentCount, context, invocation); }, OperationKind.Invocation); }); } - private static int GetFormattingArguments(string format) + private static HashSet GetStringFormatItemIndexes(string stringFormat) { - // code is from mscorlib - // https://github.com/dotnet/coreclr/blob/bc146608854d1db9cdbcc0b08029a87754e12b49/src/mscorlib/src/System/Text/StringBuilder.cs#L1312 + // removing escaped left brackets and replacing with space characters so they won't + // impede the extraction of placeholders, yet the locations of the placeholders are + // the same as in the original string. + var formatStringWithEscapedBracketsChangedToSpaces = s_removeEscapedBracketsRegex.Replace(stringFormat, " "); - // return count of this format - {index[,alignment][:formatString]} - var pos = 0; - int len = format.Length; - var uniqueNumbers = new HashSet(); + var matches = s_extractPlaceholdersRegex.Matches(formatStringWithEscapedBracketsChangedToSpaces); + var indexes = new HashSet(); - // main loop - while (true) + foreach (Match match in matches) { - // loop to find starting "{" - char ch; - while (pos < len) + var formatItemIndex = ExtractFormatItemIndex(match.Groups[1].Value); + if (formatItemIndex.HasValue) { - ch = format[pos]; - - pos++; - if (ch == '}') - { - if (pos < len && format[pos] == '}') // Treat as escape character for }} - { - pos++; - } - else - { - return -1; - } - } - - if (ch == '{') - { - if (pos < len && format[pos] == '{') // Treat as escape character for {{ - { - pos++; - } - else - { - pos--; - break; - } - } + indexes.Add(formatItemIndex.Value); } + } - // finished with "{" - if (pos == len) - { - break; - } + return indexes; + } - pos++; + private static int? ExtractFormatItemIndex(string textInsideBrackets) + { + var formatItemText = textInsideBrackets.IndexOf(",", StringComparison.OrdinalIgnoreCase) > 0 + ? textInsideBrackets.Split(',')[0] + : textInsideBrackets.Split(':')[0]; - if (pos == len || (ch = format[pos]) < '0' || ch > '9') - { - // finished with "{x" - return -1; - } + // placeholders cannot begin with whitespace + if (formatItemText.Length > 0 && char.IsWhiteSpace(formatItemText, 0)) + { + return null; + } - // searching for index - var index = 0; - do - { - index = index * 10 + ch - '0'; + if (!int.TryParse(formatItemText, out var formatItemIndex) || + formatItemIndex < 0) + { + return null; + } - pos++; - if (pos == len) - { - // wrong index format - return -1; - } + return formatItemIndex; + } - ch = format[pos]; - } while (ch >= '0' && ch <= '9' && index < 1000000); + private static void ReportOnArgumentsMismatch(string stringFormat, int actualArgumentCount, + OperationAnalysisContext context, IInvocationOperation invocation) + { + var stringFormatIndexes = GetStringFormatItemIndexes(stringFormat); - // eat up whitespace - while (pos < len && (ch = format[pos]) == ' ') + var expectedArgumentCount = stringFormatIndexes.Count > 0 + ? stringFormatIndexes.Max() + 1 + : 0; + + if (actualArgumentCount > expectedArgumentCount) + { + if (HasAnyMissingFormatIndex(stringFormatIndexes, expectedArgumentCount)) { - pos++; + context.ReportDiagnostic(invocation.CreateDiagnostic(TooManyArgsMissingFormatIndexRule)); } - - // searching for alignment - var width = 0; - if (ch == ',') + else { - pos++; - - // eat up whitespace - while (pos < len && format[pos] == ' ') - { - pos++; - } - - if (pos == len) - { - // wrong format, reached end without "}" - return -1; - } - - ch = format[pos]; - if (ch == '-') - { - pos++; - - if (pos == len) - { - // wrong format. reached end without "}" - return -1; - } - - ch = format[pos]; - } - - if (ch is < '0' or > '9') - { - // wrong format after "-" - return -1; - } - - do - { - width = width * 10 + ch - '0'; - pos++; - - if (pos == len) - { - // wrong width format - return -1; - } - - ch = format[pos]; - } while (ch >= '0' && ch <= '9' && width < 1000000); + context.ReportDiagnostic(invocation.CreateDiagnostic(TooManyArgsRule)); } - // eat up whitespace - while (pos < len && (ch = format[pos]) == ' ') + return; + } + else if (actualArgumentCount < expectedArgumentCount) + { + if (HasAnyMissingFormatIndex(stringFormatIndexes, expectedArgumentCount)) { - pos++; + context.ReportDiagnostic(invocation.CreateDiagnostic(NotEnoughArgsMissingFormatIndexRule)); } - - // searching for embedded format string - if (ch == ':') + else { - pos++; - - while (true) - { - if (pos == len) - { - // reached end without "}" - return -1; - } - - ch = format[pos]; - pos++; - - if (ch == '{') - { - if (pos < len && format[pos] == '{') // Treat as escape character for {{ - pos++; - else - return -1; - } - else if (ch == '}') - { - if (pos < len && format[pos] == '}') // Treat as escape character for }} - { - pos++; - } - else - { - pos--; - break; - } - } - } + context.ReportDiagnostic(invocation.CreateDiagnostic(NotEnoughArgsRule)); } - if (ch != '}') + return; + } + else + { + if (HasAnyMissingFormatIndex(stringFormatIndexes, expectedArgumentCount)) { - // "}" is expected - return -1; + context.ReportDiagnostic(invocation.CreateDiagnostic(EnoughArgsMissingFormatIndexRule)); } - pos++; - - uniqueNumbers.Add(index); - - } // end of main loop + return; + } - return uniqueNumbers.Count; + static bool HasAnyMissingFormatIndex(HashSet stringFormatIndexes, int expectedArgumentCount) + => stringFormatIndexes.Count > 0 && + Enumerable.Range(0, expectedArgumentCount - 1).Except(stringFormatIndexes).Any(); } private class StringFormatInfo @@ -347,23 +332,28 @@ public StringFormatInfo(Compilation compilation) return info; } - // Check if this the underlying method is user configured string formatting method. - var additionalStringFormatMethodsOption = context.Options.GetAdditionalStringFormattingMethodsOption(Rule, context.Operation.Syntax.SyntaxTree, context.Compilation, context.CancellationToken); - if (additionalStringFormatMethodsOption.Contains(method.OriginalDefinition) && - TryGetFormatInfo(method, out info)) + foreach (var descriptor in new[] { NotEnoughArgsRule, TooManyArgsRule }) { - return info; - } + // Check if this the underlying method is user configured string formatting method. + var additionalStringFormatMethodsOption = context.Options.GetAdditionalStringFormattingMethodsOption(descriptor, + context.Operation.Syntax.SyntaxTree, context.Compilation, context.CancellationToken); + if (additionalStringFormatMethodsOption.Contains(method.OriginalDefinition) && + TryGetFormatInfo(method, out info)) + { + return info; + } - // Check if the user configured automatic determination of formatting methods. - // If so, check if the method called has a 'string format' parameter followed by an params array. - var determineAdditionalStringFormattingMethodsAutomatically = context.Options.GetBoolOptionValue(EditorConfigOptionNames.TryDetermineAdditionalStringFormattingMethodsAutomatically, - Rule, context.Operation.Syntax.SyntaxTree, context.Compilation, defaultValue: false, context.CancellationToken); - if (determineAdditionalStringFormattingMethodsAutomatically && - TryGetFormatInfo(method, out info) && - info.ExpectedStringFormatArgumentCount == -1) - { - return info; + // Check if the user configured automatic determination of formatting methods. + // If so, check if the method called has a 'string format' parameter followed by an params array. + var determineAdditionalStringFormattingMethodsAutomatically = context.Options.GetBoolOptionValue( + EditorConfigOptionNames.TryDetermineAdditionalStringFormattingMethodsAutomatically, descriptor, + context.Operation.Syntax.SyntaxTree, context.Compilation, defaultValue: false, context.CancellationToken); + if (determineAdditionalStringFormattingMethodsAutomatically && + TryGetFormatInfo(method, out info) && + info.ExpectedStringFormatArgumentCount == -1) + { + return info; + } } return null; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf index 7a20a34670..22a328fcd5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1667,9 +1667,14 @@ Argument formátu, který se předává do System.String.Format, neobsahuje položku formátování, která odpovídá jednotlivým argumentům objektů, nebo naopak. - - Provide correct arguments to formatting methods - Poskytněte metodám formátování správné argumenty + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf index 1f87411727..49db8420f3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1667,9 +1667,14 @@ Das an "System.String.Format" übergebene Formatargument enthält kein Formatelement, das den einzelnen Objektargumenten entspricht bzw. umgekehrt. - - Provide correct arguments to formatting methods - Geeignete Argumente für Formatierungsmethoden angeben + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf index b2eb9e9c1e..e27574f26b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1667,9 +1667,14 @@ El argumento de cadena format pasado a System.String.Format no contiene un elemento de formato que corresponda a cada argumento de objeto o viceversa. - - Provide correct arguments to formatting methods - Proporcionar argumentos correctos para los métodos de formato + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf index 49c9c08d95..ead7959bd4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1667,9 +1667,14 @@ L'argument de mise en forme passé à System.String.Format ne contient aucun élément de mise en forme pour chaque argument d'objet correspondant, ou vice versa. - - Provide correct arguments to formatting methods - Indiquer le nombre correct d'arguments dans les méthodes de mise en forme + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf index 9de345095d..658db14059 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1667,9 +1667,14 @@ L'argomento format passato a System.String.Format non contiene un elemento di formato corrispondente a ogni argomento dell'oggetto o viceversa. - - Provide correct arguments to formatting methods - Fornire gli argomenti corretti ai metodi di formattazione + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf index 69621d6385..cfd9e8a805 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1667,9 +1667,14 @@ System.String.Format に渡される書式引数には、各オブジェクト引数に対応する書式項目が含まれていないか、各書式項目に対応するオブジェクト引数が含まれていません。 - - Provide correct arguments to formatting methods - 書式設定メソッドに正しい引数を指定します + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf index b8a532fec1..430b92b5ad 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1667,9 +1667,14 @@ System.String.Format으로 전달된 format 인수에 각 개체 인수에 해당하는 format 항목이 포함되지 않으며 그 반대의 경우도 마찬가지입니다. - - Provide correct arguments to formatting methods - 서식 지정 메서드에 올바른 인수를 제공하세요. + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf index 7de06d566f..a6b13e4181 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1667,9 +1667,14 @@ Argument formatu przekazywany do metody System.String.Format nie zawiera elementu formatu odpowiadającego każdemu argumentowi obiektu lub odwrotnie. - - Provide correct arguments to formatting methods - Określ poprawne argumenty dla metod formatujących + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf index 519a58d4cf..26afbce484 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf @@ -1667,9 +1667,14 @@ O argumento de formato passado para System.String.Format não contém um item de formato correspondente a cada argumento de objeto ou vice-versa. - - Provide correct arguments to formatting methods - Fornecer os argumentos corretos para métodos de formatação + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf index b519700bb4..c993dbe2f0 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1667,9 +1667,14 @@ Передаваемый в System.String.Format аргумент формата не содержит элемент формата, соответствующий каждому из аргументов объекта, или наоборот. - - Provide correct arguments to formatting methods - Задайте правильные аргументы для методов форматирования + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf index 897668ed0b..1e47fd5264 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1667,9 +1667,14 @@ System.String.Format’a geçirilen biçim bağımsız değişkeni, her nesne bağımsız değişkenine karşılık gelen bir biçim öğesi içermiyor ve tersi için de aynısı geçerli. - - Provide correct arguments to formatting methods - Biçimlendirme yöntemlerine doğru bağımsız değişkenleri sağlayın + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf index 2bfc4eeecc..baf997726b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf @@ -1667,9 +1667,14 @@ 传递到 System.String.Format 的 format 参数不包含与各对象参数相对应的格式项,反之亦然。 - - Provide correct arguments to formatting methods - 为格式化方法提供正确的参数 + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf index 662d9960e3..7b7850a4a8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf @@ -1667,9 +1667,14 @@ 傳遞給 System.String.Format 的格式化引數,並未包含與每個物件引數相對應的格式項目,反之亦然。 - - Provide correct arguments to formatting methods - 為格式化方法提供正確的引數 + + Incorrect number of arguments passed to this formatting method call + Incorrect number of arguments passed to this formatting method call + + + + Some string format items are missing from this formatting method call + Some string format items are missing from this formatting method call diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/ProvideCorrectArgumentsToFormattingMethodsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/ProvideCorrectArgumentsToFormattingMethodsTests.cs index 293decb6df..86319c10db 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/ProvideCorrectArgumentsToFormattingMethodsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/ProvideCorrectArgumentsToFormattingMethodsTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using Test.Utilities; @@ -15,10 +16,10 @@ namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests { public class ProvideCorrectArgumentsToFormattingMethodsTests { - #region Diagnostic Tests - - [Fact] - public async Task CA2241CSharpString() + [Theory] + [InlineData("Console.Write")] + [InlineData("Console.WriteLine")] + public async Task CA2241_TooManyArgs_ConsoleWriteMethods_Diagnostic(string invocation) { await VerifyCS.VerifyAnalyzerAsync(@" using System; @@ -27,34 +28,51 @@ public class C { void Method() { - var a = String.Format("""", 1); - var b = String.Format(""{0}"", 1, 2); - var c = String.Format(""{0} {1}"", 1, 2, 3); - var d = String.Format(""{0} {1} {2}"", 1, 2, 3, 4); - var e = string.Format(""{0} {0}"", 1, 2); - - IFormatProvider p = null; - var f = String.Format(p, """", 1); - var g = String.Format(p, ""{0}"", 1, 2); - var h = String.Format(p, ""{0} {1}"", 1, 2, 3); - var i = String.Format(p, ""{0} {1} {2}"", 1, 2, 3, 4); + {|#0:" + invocation + @"("""", 1)|}; + {|#1:" + invocation + @"(""{0}"", 1, 2)|}; + {|#2:" + invocation + @"(""{0} {1}"", 1, 2, 3)|}; + {|#3:" + invocation + @"(""{0} {1} {2}"", 1, 2, 3, 4)|}; + {|#4:" + invocation + @"(""{0} {1} {2}"", 1, 2, 3, 4, 5, 6, 7, 8)|}; + {|#5:" + invocation + @"(""{2} {0} {1}"", 1, 2, 3, 4)|}; + {|#6:" + invocation + @"(""{0} {0}"", 1, 2)|}; } } ", - GetCSharpResultAt(8, 17), - GetCSharpResultAt(9, 17), - GetCSharpResultAt(10, 17), - GetCSharpResultAt(11, 17), - GetCSharpResultAt(12, 17), - - GetCSharpResultAt(15, 17), - GetCSharpResultAt(16, 17), - GetCSharpResultAt(17, 17), - GetCSharpResultAt(18, 17)); + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(4), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(5), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(6)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class C + Sub Method() + {|#0:" + invocation + @"("""", 1)|} + {|#1:" + invocation + @"(""{0}"", 1, 2)|} + {|#2:" + invocation + @"(""{0} {1}"", 1, 2, 3)|} + {|#3:" + invocation + @"(""{0} {1} {2}"", 1, 2, 3, 4)|} + {|#4:" + invocation + @"(""{0} {1} {2}"", 1, 2, 3, 4, 5, 6, 7, 8)|} + {|#5:" + invocation + @"(""{2} {0} {1}"", 1, 2, 3, 4)|} + {|#6:" + invocation + @"(""{0} {0}"", 1, 2)|} + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(4), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(5), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(6)); } - [Fact] - public async Task CA2241CSharpConsoleWrite() + [Theory, WorkItem(1254, "https://github.com/dotnet/roslyn-analyzers/issues/1254")] + [InlineData("Console.Write")] + [InlineData("Console.WriteLine")] + public async Task CA2241_TooManyArgsMissingFormatIndex_ConsoleWriteMethods_Diagnostic(string invocation) { await VerifyCS.VerifyAnalyzerAsync(@" using System; @@ -63,82 +81,181 @@ public class C { void Method() { - Console.Write("""", 1); - Console.Write(""{0}"", 1, 2); - Console.Write(""{0} {1}"", 1, 2, 3); - Console.Write(""{0} {1} {2}"", 1, 2, 3, 4); - Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4, 5); + {|#0:" + invocation + @"(""{1}"", 1, 2, 3)|}; + {|#1:" + invocation + @"(""{2}"", 1, 2, 3, 4)|}; + {|#2:" + invocation + @"(""{0} {2}"", 1, 2, 3, 4)|}; + {|#3:" + invocation + @"(""{0} {2}"", 1, 2, 3, 4, 5, 6, 7, 8)|}; + {|#4:" + invocation + @"(""{0} {2} {3} {5}"", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)|}; + {|#5:" + invocation + @"(""{5} {2} {3} {1}"", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)|}; } } ", - GetCSharpResultAt(8, 9), - GetCSharpResultAt(9, 9), - GetCSharpResultAt(10, 9), - GetCSharpResultAt(11, 9), - GetCSharpResultAt(12, 9)); + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(4), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(5)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class C + Sub Method() + {|#0:" + invocation + @"(""{1}"", 1, 2, 3)|} + {|#1:" + invocation + @"(""{2}"", 1, 2, 3, 4)|} + {|#2:" + invocation + @"(""{0} {2}"", 1, 2, 3, 4)|} + {|#3:" + invocation + @"(""{0} {2}"", 1, 2, 3, 4, 5, 6, 7, 8)|} + {|#4:" + invocation + @"(""{0} {2} {3} {5}"", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)|} + {|#5:" + invocation + @"(""{5} {2} {3} {1}"", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)|} + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(4), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(5)); } [Fact] - public async Task CA2241CSharpConsoleWriteLine() + public async Task CA2241_TooManyArgs_StringFormatMethods_Diagnostic() { await VerifyCS.VerifyAnalyzerAsync(@" using System; public class C { - void Method() + void Method(IFormatProvider formatProvider) { - Console.WriteLine("""", 1); - Console.WriteLine(""{0}"", 1, 2); - Console.WriteLine(""{0} {1}"", 1, 2, 3); - Console.WriteLine(""{0} {1} {2}"", 1, 2, 3, 4); - Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4, 5); + // Too many args + var a = {|#0:String.Format("""", 1)|}; + var b = {|#1:String.Format(""{0}"", 1, 2)|}; + var c = {|#2:String.Format(""{0} {1}"", 1, 2, 3)|}; + var d = {|#3:String.Format(""{0} {1} {2}"", 1, 2, 3, 4)|}; + var e = {|#4:String.Format(""{0} {1} {2}"", 1, 2, 3, 4, 5, 6, 7, 8)|}; + var f = {|#5:string.Format(""{0} {0}"", 1, 2)|}; + + // Too many args with format provider + var g = {|#6:String.Format(formatProvider, """", 1)|}; + var h = {|#7:String.Format(formatProvider, ""{0}"", 1, 2)|}; + var i = {|#8:String.Format(formatProvider, ""{0} {1}"", 1, 2, 3)|}; + var j = {|#9:String.Format(formatProvider, ""{0} {1} {2}"", 1, 2, 3, 4)|}; + var k = {|#10:String.Format(formatProvider, ""{0} {0}"", 1, 2)|}; } } ", - GetCSharpResultAt(8, 9), - GetCSharpResultAt(9, 9), - GetCSharpResultAt(10, 9), - GetCSharpResultAt(11, 9), - GetCSharpResultAt(12, 9)); + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(4), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(5), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(6), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(7), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(8), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(9), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(10)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class C + Sub Method(formatProvider As IFormatProvider) + ' Too many args + Dim a = {|#0:String.Format("""", 1)|} + Dim b = {|#1:String.Format(""{0}"", 1, 2)|} + Dim c = {|#2:String.Format(""{0} {1}"", 1, 2, 3)|} + Dim d = {|#3:String.Format(""{0} {1} {2}"", 1, 2, 3, 4)|} + Dim e = {|#4:String.Format(""{0} {1} {2}"", 1, 2, 3, 4, 5, 6, 7, 8)|} + Dim f = {|#5:string.Format(""{0} {0}"", 1, 2)|} + + ' Too many args with format provider + Dim g = {|#6:String.Format(formatProvider, """", 1)|} + Dim h = {|#7:String.Format(formatProvider, ""{0}"", 1, 2)|} + Dim i = {|#8:String.Format(formatProvider, ""{0} {1}"", 1, 2, 3)|} + Dim j = {|#9:String.Format(formatProvider, ""{0} {1} {2}"", 1, 2, 3, 4)|} + Dim k = {|#10:String.Format(formatProvider, ""{0} {0}"", 1, 2)|} + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(4), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(5), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(6), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(7), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(8), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(9), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(10)); } - [Fact] - public async Task CA2241CSharpPassing() + [Fact, WorkItem(1254, "https://github.com/dotnet/roslyn-analyzers/issues/1254")] + public async Task CA2241_TooManyArgsMissingFormatIndex_StringFormatMethods_Diagnostic() { await VerifyCS.VerifyAnalyzerAsync(@" using System; public class C { - void Method() + void Method(IFormatProvider formatProvider) { - var a = String.Format(""{0}"", 1); - var b = String.Format(""{0} {1}"", 1, 2); - var c = String.Format(""{0} {1} {2}"", 1, 2, 3); - var d = String.Format(""{0} {1} {2} {3}"", 1, 2, 3, 4); - var e = String.Format(""{0} {1} {2} {0}"", 1, 2, 3); + // Too many args + missing format index + var a = {|#0:string.Format(""{1}"", 1, 2, 3)|}; + var b = {|#1:string.Format(""{2}"", 1, 2, 3, 4)|}; + var c = {|#2:string.Format(""{0} {2}"", 1, 2, 3, 4)|}; + var d = {|#3:string.Format(""{0} {2}"", 1, 2, 3, 4, 5, 6, 7, 8)|}; + var e = {|#4:string.Format(""{0} {2} {3} {5}"", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)|}; - Console.Write(""{0}"", 1); - Console.Write(""{0} {1}"", 1, 2); - Console.Write(""{0} {1} {2}"", 1, 2, 3); - Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4); - Console.Write(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5); - Console.Write(""{0} {1} {2} {3} {0}"", 1, 2, 3, 4); - - Console.WriteLine(""{0}"", 1); - Console.WriteLine(""{0} {1}"", 1, 2); - Console.WriteLine(""{0} {1} {2}"", 1, 2, 3); - Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4); - Console.WriteLine(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5); - Console.WriteLine(""{0} {1} {2} {3} {0}"", 1, 2, 3, 4); + // Too many args with format provider + missing format index + var f = {|#5:string.Format(formatProvider, ""{1}"", 1, 2, 3)|}; + var g = {|#6:string.Format(formatProvider, ""{2}"", 1, 2, 3, 4)|}; + var h = {|#7:string.Format(formatProvider, ""{0} {2}"", 1, 2, 3, 4)|}; } } -"); +", + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(4), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(5), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(6), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(7)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class C + Sub Method(formatProvider As IFormatProvider) + ' Too many args + missing format index + Dim a = {|#0:string.Format(""{1}"", 1, 2, 3)|} + Dim b = {|#1:string.Format(""{2}"", 1, 2, 3, 4)|} + Dim c = {|#2:string.Format(""{0} {2}"", 1, 2, 3, 4)|} + Dim d = {|#3:string.Format(""{0} {2}"", 1, 2, 3, 4, 5, 6, 7, 8)|} + Dim e = {|#4:string.Format(""{0} {2} {3} {5}"", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)|} + + ' Too many args with format provider + missing format index + Dim f = {|#5:string.Format(formatProvider, ""{1}"", 1, 2, 3)|} + Dim g = {|#6:string.Format(formatProvider, ""{2}"", 1, 2, 3, 4)|} + Dim h = {|#7:string.Format(formatProvider, ""{0} {2}"", 1, 2, 3, 4)|} + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(4), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(5), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(6), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(7)); } - [Fact] - public async Task CA2241CSharpExplicitObjectArraySupported() + [Theory] + [InlineData("Console.Write")] + [InlineData("Console.WriteLine")] + public async Task CA2250_NotEnoughArgs_ConsoleWriteMethods_Diagnostic(string invocation) { await VerifyCS.VerifyAnalyzerAsync(@" using System; @@ -147,134 +264,361 @@ public class C { void Method() { - var s = String.Format(""{0} {1} {2} {3}"", new object[] {1, 2}); - Console.Write(""{0} {1} {2} {3}"", new object[] {1, 2, 3, 4, 5}); - Console.WriteLine(""{0} {1} {2} {3}"", new object[] {1, 2, 3, 4, 5}); + {|#0:" + invocation + @"(""{0} {1}"", 1)|}; + {|#1:" + invocation + @"(""{0} {1} {2}"", 1, 2)|}; + {|#2:" + invocation + @"(""{0} {1} {2}"", 1)|}; + {|#3:" + invocation + @"(""{2} {0} {1}"", 1)|}; + {|#4:" + invocation + @"(""{1} {0} {1}"", 1)|}; } } ", - GetCSharpResultAt(8, 17), - GetCSharpResultAt(9, 9), - GetCSharpResultAt(10, 9)); + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(4)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class C + Sub Method() + {|#0:" + invocation + @"(""{0} {1}"", 1)|} + {|#1:" + invocation + @"(""{0} {1} {2}"", 1, 2)|} + {|#2:" + invocation + @"(""{0} {1} {2}"", 1)|} + {|#3:" + invocation + @"(""{2} {0} {1}"", 1)|} + {|#4:" + invocation + @"(""{1} {0} {1}"", 1)|} + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(4)); } - [Fact] - public async Task CA2241CSharpVarArgsNotSupported() + [Theory, WorkItem(1254, "https://github.com/dotnet/roslyn-analyzers/issues/1254")] + [InlineData("Console.Write")] + [InlineData("Console.WriteLine")] + public async Task CA2250_NotEnoughArgsMissingFormatIndexRule_ConsoleWriteMethods_Diagnostic(string invocation) { - // currently not supported due to "https://github.com/dotnet/roslyn/issues/7346" - await new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net472.Default, - TestCode = @" + await VerifyCS.VerifyAnalyzerAsync(@" using System; public class C { void Method() { - Console.Write(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, __arglist(5)); - Console.WriteLine(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, __arglist(5)); + {|#0:" + invocation + @"(""{1}"", 1)|}; + {|#1:" + invocation + @"(""{1} {2}"", 1, 2)|}; + {|#2:" + invocation + @"(""{2} {0}"", 1, 2)|}; + {|#3:" + invocation + @"(""{4} {1} {2}"", 1, 2, 3)|}; } } ", - }.RunAsync(); - } + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(3)); - [Fact] - public async Task CA2241VBString() - { await VerifyVB.VerifyAnalyzerAsync(@" Imports System Public Class C Sub Method() - Dim a = String.Format("""", 1) - Dim b = String.Format(""{0}"", 1, 2) - Dim c = String.Format(""{0} {1}"", 1, 2, 3) - Dim d = String.Format(""{0} {1} {2}"", 1, 2, 3, 4) - - Dim p as IFormatProvider = Nothing - Dim e = String.Format(p, """", 1) - Dim f = String.Format(p, ""{0}"", 1, 2) - Dim g = String.Format(p, ""{0} {1}"", 1, 2, 3) - Dim h = String.Format(p, ""{0} {1} {2}"", 1, 2, 3, 4) + {|#0:" + invocation + @"(""{1}"", 1)|} + {|#1:" + invocation + @"(""{1} {2}"", 1, 2)|} + {|#2:" + invocation + @"(""{2} {0}"", 1, 2)|} + {|#3:" + invocation + @"(""{4} {1} {2}"", 1, 2, 3)|} End Sub -End Class -", - GetBasicResultAt(6, 17), - GetBasicResultAt(7, 17), - GetBasicResultAt(8, 17), - GetBasicResultAt(9, 17), - - GetBasicResultAt(12, 17), - GetBasicResultAt(13, 17), - GetBasicResultAt(14, 17), - GetBasicResultAt(15, 17)); +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(3)); } [Fact] - public async Task CA2241VBConsoleWrite() + public async Task CA2250_NotEnoughArgs_StringFormatMethods_Diagnostic() { - // this works in VB - // Dim s = Console.WriteLine(""{0} {1} {2}"", 1, 2, 3, 4) - // since VB bind it to __arglist version where we skip analysis - // due to a bug - https://github.com/dotnet/roslyn/issues/7346 - // we might skip it only in C# since VB doesn't support __arglist + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class C +{ + void Method(IFormatProvider formatProvider) + { + // Not enough args + var a = {|#0:String.Format(""{0}"")|}; + var b = {|#1:String.Format(""{0} {1}"", 1)|}; + var c = {|#2:String.Format(""{0} {1} {2}"", 1, 2)|}; + var d = {|#3:String.Format(""{0} {1} {2}"", 1)|}; + var e = {|#4:String.Format(""{2} {0} {1}"", 1)|}; + var f = {|#5:String.Format(""{1} {0} {1}"", 1)|}; + + // Not enough args with format provider + var g = {|#6:String.Format(formatProvider, ""{0}"")|}; + var h = {|#7:String.Format(formatProvider, ""{0} {1}"", 1)|}; + var i = {|#8:String.Format(formatProvider, ""{0} {1} {2}"", 1, 2)|}; + var j = {|#9:String.Format(formatProvider, ""{0} {1} {2}"", 1)|}; + var k = {|#10:String.Format(formatProvider, ""{2} {0} {1}"", 1)|}; + var l = {|#11:String.Format(formatProvider, ""{1} {0} {1}"", 1)|}; + } +} +", + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(4), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(5), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(6), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(7), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(8), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(9), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(10), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(11)); + await VerifyVB.VerifyAnalyzerAsync(@" Imports System Public Class C - Sub Method() - Console.Write("""", 1) - Console.Write(""{0}"", 1, 2) - Console.Write(""{0} {1}"", 1, 2, 3) - Console.Write(""{0} {1} {2}"", 1, 2, 3, 4) - Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4, 5) + Sub Method(formatProvider As IFormatProvider) + ' Not enough args + Dim a = {|#0:String.Format(""{0}"")|} + Dim b = {|#1:String.Format(""{0} {1}"", 1)|} + Dim c = {|#2:String.Format(""{0} {1} {2}"", 1, 2)|} + Dim d = {|#3:String.Format(""{0} {1} {2}"", 1)|} + Dim e = {|#4:String.Format(""{2} {0} {1}"", 1)|} + Dim f = {|#5:String.Format(""{1} {0} {1}"", 1)|} + + ' Not enough args with format provider + Dim g = {|#6:String.Format(formatProvider, ""{0}"")|} + Dim h = {|#7:String.Format(formatProvider, ""{0} {1}"", 1)|} + Dim i = {|#8:String.Format(formatProvider, ""{0} {1} {2}"", 1, 2)|} + Dim j = {|#9:String.Format(formatProvider, ""{0} {1} {2}"", 1)|} + Dim k = {|#10:String.Format(formatProvider, ""{2} {0} {1}"", 1)|} + Dim l = {|#11:String.Format(formatProvider, ""{1} {0} {1}"", 1)|} End Sub -End Class +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(4), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(5), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(6), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(7), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(8), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(9), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(10), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(11)); + } + + [Fact, WorkItem(1254, "https://github.com/dotnet/roslyn-analyzers/issues/1254")] + public async Task CA2250_NotEnoughArgsMissingFormatIndex_StringFormatMethods_Diagnostic() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class C +{ + void Method(IFormatProvider formatProvider) + { + // Not enough args + missing format index + var a = {|#0:String.Format(""{1}"")|}; + var b = {|#1:String.Format(""{1}"", 1)|}; + var c = {|#2:String.Format(""{2}"", 1, 2)|}; + var d = {|#3:String.Format(""{2} {0}"", 1)|}; + + // Not enough args with format provider + missing format index + var e = {|#4:String.Format(formatProvider, ""{1}"")|}; + var f = {|#5:String.Format(formatProvider, ""{1}"", 1)|}; + var g = {|#6:String.Format(formatProvider, ""{2}"", 1, 2)|}; + var h = {|#7:String.Format(formatProvider, ""{2} {0}"", 1)|}; + } +} ", - GetBasicResultAt(6, 9), - GetBasicResultAt(7, 9), - GetBasicResultAt(8, 9), -#if NETCOREAPP - GetBasicResultAt(9, 9), -#endif - GetBasicResultAt(10, 9)); + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(4), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(5), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(6), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(7)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class C + Sub Method(formatProvider As IFormatProvider) + ' Not enough args + missing format index + Dim a = {|#0:String.Format(""{1}"")|} + Dim b = {|#1:String.Format(""{1}"", 1)|} + Dim c = {|#2:String.Format(""{2}"", 1, 2)|} + Dim d = {|#3:String.Format(""{2} {0}"", 1)|} + + ' Not enough args with format provider + missing format index + Dim e = {|#4:String.Format(formatProvider, ""{1}"")|} + Dim f = {|#5:String.Format(formatProvider, ""{1}"", 1)|} + Dim g = {|#6:String.Format(formatProvider, ""{2}"", 1, 2)|} + Dim h = {|#7:String.Format(formatProvider, ""{2} {0}"", 1)|} + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(4), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(5), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(6), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(7)); } - [Fact] - public async Task CA2241VBConsoleWriteLine() + [Theory, WorkItem(1254, "https://github.com/dotnet/roslyn-analyzers/issues/1254")] + [InlineData("Console.Write")] + [InlineData("Console.WriteLine")] + public async Task CA2241_EnoughArgsMissingFormatIndex_ConsoleWriteMethods_Diagnostic(string invocation) { - // this works in VB - // Dim s = Console.WriteLine(""{0} {1} {2}"", 1, 2, 3, 4) - // since VB bind it to __arglist version where we skip analysis - // due to a bug - https://github.com/dotnet/roslyn/issues/7346 - // we might skip it only in C# since VB doesn't support __arglist + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class C +{ + void Method() + { + {|#0:" + invocation + @"(""{1}"", 1, 2)|}; + {|#1:" + invocation + @"(""{1} {2}"", 1, 2, 3)|}; + {|#2:" + invocation + @"(""{2} {0}"", 1, 2, 3)|}; + {|#3:" + invocation + @"(""{4} {1} {2}"", 1, 2, 3, 4, 5)|}; + {|#4:" + invocation + @"(""{0} {2} {0} {2}"", 1, 2, 3)|}; + } +} +", + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(4)); + await VerifyVB.VerifyAnalyzerAsync(@" Imports System Public Class C Sub Method() - Console.WriteLine("""", 1) - Console.WriteLine(""{0}"", 1, 2) - Console.WriteLine(""{0} {1}"", 1, 2, 3) - Console.WriteLine(""{0} {1} {2}"", 1, 2, 3, 4) - Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4, 5) + {|#0:" + invocation + @"(""{1}"", 1, 2)|} + {|#1:" + invocation + @"(""{1} {2}"", 1, 2, 3)|} + {|#2:" + invocation + @"(""{2} {0}"", 1, 2, 3)|} + {|#3:" + invocation + @"(""{4} {1} {2}"", 1, 2, 3, 4, 5)|} + {|#4:" + invocation + @"(""{0} {2} {0} {2}"", 1, 2, 3)|} End Sub -End Class +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(4)); + } + + [Fact, WorkItem(1254, "https://github.com/dotnet/roslyn-analyzers/issues/1254")] + public async Task CA2241_EnoughArgsMissingFormatIndex_StringFormatMethods_Diagnostic() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class C +{ + void Method(IFormatProvider formatProvider) + { + var a = {|#0:string.Format(""{1}"", 1, 2)|}; + var b = {|#1:string.Format(""{1} {2}"", 1, 2, 3)|}; + var c = {|#2:string.Format(""{2} {0}"", 1, 2, 3)|}; + var d = {|#3:string.Format(""{4} {1} {2}"", 1, 2, 3, 4, 5)|}; + var e = {|#4:string.Format(""{0} {2} {0} {2}"", 1, 2, 3)|}; + + var f = {|#5:string.Format(formatProvider, ""{1}"", 1, 2)|}; + var g = {|#6:string.Format(formatProvider, ""{1} {2}"", 1, 2, 3)|}; + var h = {|#7:string.Format(formatProvider, ""{2} {0}"", 1, 2, 3)|}; + var i = {|#8:string.Format(formatProvider, ""{4} {1} {2}"", 1, 2, 3, 4, 5)|}; + var j = {|#9:string.Format(formatProvider, ""{0} {2} {0} {2}"", 1, 2, 3)|}; + } +} ", - GetBasicResultAt(6, 9), - GetBasicResultAt(7, 9), - GetBasicResultAt(8, 9), -#if NETCOREAPP - GetBasicResultAt(9, 9), -#endif - GetBasicResultAt(10, 9)); + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(4), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(5), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(6), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(7), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(8), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(9)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class C + Sub Method(formatProvider As IFormatProvider) + Dim a = {|#0:string.Format(""{1}"", 1, 2)|} + Dim b = {|#1:string.Format(""{1} {2}"", 1, 2, 3)|} + Dim c = {|#2:string.Format(""{2} {0}"", 1, 2, 3)|} + Dim d = {|#3:string.Format(""{4} {1} {2}"", 1, 2, 3, 4, 5)|} + Dim e = {|#4:string.Format(""{0} {2} {0} {2}"", 1, 2, 3)|} + + Dim f = {|#5:string.Format(formatProvider, ""{1}"", 1, 2)|} + Dim g = {|#6:string.Format(formatProvider, ""{1} {2}"", 1, 2, 3)|} + Dim h = {|#7:string.Format(formatProvider, ""{2} {0}"", 1, 2, 3)|} + Dim i = {|#8:string.Format(formatProvider, ""{4} {1} {2}"", 1, 2, 3, 4, 5)|} + Dim j = {|#9:string.Format(formatProvider, ""{0} {2} {0} {2}"", 1, 2, 3)|} + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(4), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(5), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(6), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(7), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(8), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(9)); } [Fact] - public async Task CA2241VBPassing() + public async Task CA2241_ValidInvocation_NoDiagnostic() { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class C +{ + void Method() + { + var a = String.Format(""{0}"", 1); + var b = String.Format(""{0} {1}"", 1, 2); + var c = String.Format(""{0} {1} {2}"", 1, 2, 3); + var d = String.Format(""{0} {1} {2} {3}"", 1, 2, 3, 4); + var e = String.Format(""{0} {1} {2} {0}"", 1, 2, 3); + var f = String.Format(""abc""); + + Console.Write(""{0}"", 1); + Console.Write(""{0} {1}"", 1, 2); + Console.Write(""{0} {1} {2}"", 1, 2, 3); + Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4); + Console.Write(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5); + Console.Write(""{0} {1} {2} {3} {0}"", 1, 2, 3, 4); + + Console.WriteLine(""{0}"", 1); + Console.WriteLine(""{0} {1}"", 1, 2); + Console.WriteLine(""{0} {1} {2}"", 1, 2, 3); + Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4); + Console.WriteLine(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5); + Console.WriteLine(""{0} {1} {2} {3} {0}"", 1, 2, 3, 4); + } +}"); + await VerifyVB.VerifyAnalyzerAsync(@" Imports System @@ -284,65 +628,200 @@ Sub Method() Dim b = String.Format(""{0} {1}"", 1, 2) Dim c = String.Format(""{0} {1} {2}"", 1, 2, 3) Dim d = String.Format(""{0} {1} {2} {3}"", 1, 2, 3, 4) + Dim e = String.Format(""{0} {1} {2} {0}"", 1, 2, 3) + Dim f = String.Format(""abc"") Console.Write(""{0}"", 1) Console.Write(""{0} {1}"", 1, 2) Console.Write(""{0} {1} {2}"", 1, 2, 3) Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4) - Console.Write(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5) + Console.Write(""{0} {1} {2} {0}"", 1, 2, 3) Console.WriteLine(""{0}"", 1) Console.WriteLine(""{0} {1}"", 1, 2) Console.WriteLine(""{0} {1} {2}"", 1, 2, 3) Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4) - Console.WriteLine(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5) + Console.WriteLine(""{0} {1} {2} {0}"", 1, 2, 3) End Sub -End Class -"); +End Class"); } - [Fact] - public async Task CA2241VBExplicitObjectArraySupported() + [Theory] + [InlineData("Console.Write")] + [InlineData("Console.WriteLine")] + public async Task CA2241_CA2250_ExplicitObjectArray_ConsoleWriteMethods(string invocation) { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class C +{ + void Diag() + { + // Too many args + {|#0:" + invocation + @"(""{0} {1} {2}"", new object[] { 1, 2, 3, 4 })|}; + // Too many args + missing format index + {|#1:" + invocation + @"(""{0} {2}"", new object[] { 1, 2, 3, 4 })|}; + // Not enough args + {|#2:" + invocation + @"(""{0} {1} {2}"", new object[] { 1, 2 })|}; + // Not enough args + missing format index + {|#3:" + invocation + @"(""{0} {2}"", new object[] { 1, 2 })|}; + // Enough args but missing format index + {|#4:" + invocation + @"(""{0} {2}"", new object[] { 1, 2, 3 })|}; + } + + void NoDiag() + { + " + invocation + @"(""{0} {1} {2}"", new object[] { 1, 2, 3 }); + } +} +", + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(4)); + await VerifyVB.VerifyAnalyzerAsync(@" Imports System Public Class C - Sub Method() - Dim s = String.Format(""{0} {1} {2} {3}"", New Object() {1, 2}) - Console.Write(""{0} {1} {2} {3}"", New Object() {1, 2, 3, 4, 5}) - Console.WriteLine(""{0} {1} {2} {3}"", New Object() {1, 2, 3, 4, 5}) + Sub Diag() + ' Too many args + {|#0:" + invocation + @"(""{0} {1} {2}"", New Object() { 1, 2, 3, 4 })|} + ' Too many args + missing format index + {|#1:" + invocation + @"(""{0} {2}"", New Object() { 1, 2, 3, 4 })|} + ' Not enough args + {|#2:" + invocation + @"(""{0} {1} {2}"", New Object() { 1, 2 })|} + ' Not enough args + missing format index + {|#3:" + invocation + @"(""{0} {2}"", New Object() { 1, 2 })|} + ' Enough args but missing format index + {|#4:" + invocation + @"(""{0} {2}"", New Object() { 1, 2, 3 })|} End Sub -End Class -", - GetBasicResultAt(6, 17), - GetBasicResultAt(7, 9), - GetBasicResultAt(8, 9)); + + Sub NoDiag() + " + invocation + @"(""{0} {1} {2}"", New Object() { 1, 2, 3 }) + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(4)); } [Fact] - public async Task CA2241CSharpFormatStringParser() + public async Task CA2241_CA2250_ExplicitObjectArray_StringFormatMethods() { await VerifyCS.VerifyAnalyzerAsync(@" using System; public class C { - void Method() + void Diag() { - var a = String.Format(""{0,-4 :xd}"", 1); - var b = String.Format(""{0 , 5 : d} {1}"", 1, 2); - var c = String.Format(""{0:d} {1} {2}"", 1, 2, 3); - var d = String.Format(""{0, 5} {1} {2} {3}"", 1, 2, 3, 4); - - Console.Write(""{0,1}"", 1); - Console.Write(""{0: x} {1}"", 1, 2); - Console.Write(""{{escape}}{0} {1} {2}"", 1, 2, 3); - Console.Write(""{0: {{escape}} x} {1} {2} {3}"", 1, 2, 3, 4); - Console.Write(""{0 , -10 : {{escape}} y} {1} {2} {3} {4}"", 1, 2, 3, 4, 5); + // Too many args + var a = {|#0:string.Format(""{0} {1} {2}"", new object[] { 1, 2, 3, 4 })|}; + // Too many args + missing format index + var b = {|#1:string.Format(""{0} {2}"", new object[] { 1, 2, 3, 4 })|}; + // Not enough args + var c = {|#2:string.Format(""{0} {1} {2}"", new object[] { 1, 2 })|}; + // Not enough args + missing format index + var d = {|#3:string.Format(""{0} {2}"", new object[] { 1, 2 })|}; + // Enough args but missing format index + var e = {|#4:string.Format(""{0} {2}"", new object[] { 1, 2, 3 })|}; + } + + void NoDiag() + { + var s = String.Format(""{0} {1} {2}"", new object[] { 1, 2, 3 }); } } -"); +", + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(0), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(1), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(2), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(4)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class C + Sub Diag() + ' Too many args + Dim a = {|#0:string.Format(""{0} {1} {2}"", New Object() { 1, 2, 3, 4 })|} + ' Too many args + missing format index + Dim b = {|#1:string.Format(""{0} {2}"", New Object() { 1, 2, 3, 4 })|} + ' Not enough args + Dim c = {|#2:string.Format(""{0} {1} {2}"", New Object() { 1, 2 })|} + ' Not enough args + missing format index + Dim d = {|#3:string.Format(""{0} {2}"", New Object() { 1, 2 })|} + ' Enough args but missing format index + Dim e = {|#4:string.Format(""{0} {2}"", New Object() { 1, 2, 3 })|} + End Sub + + Sub NoDiag() + Dim s = String.Format(""{0} {1} {2}"", New Object() { 1, 2, 3 }) + End Sub +End Class", + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(0), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(1), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(2), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(3), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(4)); + } + + [Fact] + public async Task CA2241_CA2250_CSharp_VarArgsNotSupported() + { + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net472.Default, + TestCode = @" + using System; + + public class C + { + void Method() + { + Console.Write(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, __arglist(5)); + Console.WriteLine(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, __arglist(5)); + + Console.Write(""{0} {1}"", 1, 2, 3, 4, __arglist(5)); + Console.WriteLine(""{0} {1}"", 1, 2, 3, 4, __arglist(5)); + + Console.Write(""{0} {1} {2} {3} {4} {5} {6}"", 1, 2, 3, 4, __arglist(5)); + Console.WriteLine(""{0} {1} {2} {3} {4} {5} {6}"", 1, 2, 3, 4, __arglist(5)); + } + } + ", + }.RunAsync(); + } + + [Fact] + public async Task CA2241_CA2250_FormatStringParser_NoDiagnostic() + { + await VerifyCS.VerifyAnalyzerAsync(@" + using System; + + public class C + { + void Method() + { + var a = String.Format(""{0,-4 :xd}"", 1); + var b = String.Format(""{0 , 5 : d} {1}"", 1, 2); + var c = String.Format(""{0:d} {1} {2}"", 1, 2, 3); + var d = String.Format(""{0, 5} {1} {2} {3}"", 1, 2, 3, 4); + + Console.Write(""{0,1}"", 1); + Console.Write(""{0: x} {1}"", 1, 2); + Console.Write(""{{escape}}{0} {1} {2}"", 1, 2, 3); + Console.Write(""{0: {{escape}} x} {1} {2} {3}"", 1, 2, 3, 4); + Console.Write(""{0 , -10 : {{escape}} y} {1} {2} {3} {4}"", 1, 2, 3, 4, 5); + } + } + "); } [Theory] @@ -353,7 +832,7 @@ void Method() [InlineData(false)] // Configured and enabled [InlineData(true)] - public async Task EditorConfigConfiguration_HeuristicAdditionalStringFormattingMethods(bool? editorConfig) + public async Task CA2241_CA2250_EditorConfigConfiguration_HeuristicAdditionalStringFormattingMethods(bool? editorConfig) { string editorConfigText = editorConfig == null ? string.Empty : "dotnet_code_quality.try_determine_additional_string_formatting_methods_automatically = " + editorConfig.Value; @@ -371,7 +850,18 @@ class Test void M1(string param) { - var a = MyFormat("""", 1); + // Too many args + var s1 = MyFormat(""{0}"", 1, 2); + // Too many args and missing format index + var s2 = MyFormat(""{1}"", 1, 2, 3); + + // Not enough args + var s3 = MyFormat(""{0} {1}"", 1); + // Not enough args and missing format index + var s4 = MyFormat(""{0} {2}"", 1); + + // Enough args and missing format index + var s5 = MyFormat(""{1}"", 1, 2); } }" }, @@ -381,9 +871,14 @@ void M1(string param) if (editorConfig == true) { - csharpTest.ExpectedDiagnostics.Add( - // Test0.cs(8,17): warning CA2241: Provide correct arguments to formatting methods - GetCSharpResultAt(8, 17)); + csharpTest.ExpectedDiagnostics.AddRange(new[] + { + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(9, 18), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(11, 18), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(14, 18), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(16, 18), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(19, 18), + }); } await csharpTest.RunAsync(); @@ -401,7 +896,18 @@ Return format End Function Private Sub M1(ByVal param As String) - Dim a = MyFormat("""", 1) + ' Too many args + Dim s1 = MyFormat(""{0}"", 1, 2) + ' Too many args and missing format index + Dim s2 = MyFormat(""{1}"", 1, 2, 3) + + ' Not enough args + Dim s3 = MyFormat(""{0} {1}"", 1) + ' Not enough args and missing format index + Dim s4 = MyFormat(""{0} {2}"", 1) + + ' Enough args and missing format index + Dim s5 = MyFormat(""{1}"", 1, 2) End Sub End Class" }, @@ -411,9 +917,14 @@ End Class" if (editorConfig == true) { - basicTest.ExpectedDiagnostics.Add( - // Test0.vb(8,17): warning CA2241: Provide correct arguments to formatting methods - GetBasicResultAt(8, 17)); + basicTest.ExpectedDiagnostics.AddRange(new[] + { + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(9, 18), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(11, 18), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(14, 18), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(16, 18), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(19, 18), + }); } await basicTest.RunAsync(); @@ -426,12 +937,15 @@ End Class" // Match by method name [InlineData("dotnet_code_quality.additional_string_formatting_methods = MyFormat")] // Setting only for Rule ID - [InlineData("dotnet_code_quality." + ProvideCorrectArgumentsToFormattingMethodsAnalyzer.RuleId + ".additional_string_formatting_methods = MyFormat")] + [InlineData(@"dotnet_code_quality.CA2241.additional_string_formatting_methods = MyFormat + dotnet_code_quality.CA2250.additional_string_formatting_methods = MyFormat")] + [InlineData(@"dotnet_code_quality.CA2241.additional_string_formatting_methods = MyFormat")] + [InlineData(@"dotnet_code_quality.CA2250.additional_string_formatting_methods = MyFormat")] // Match by documentation ID without "M:" prefix [InlineData("dotnet_code_quality.additional_string_formatting_methods = Test.MyFormat(System.String,System.Object[])~System.String")] // Match by documentation ID with "M:" prefix [InlineData("dotnet_code_quality.additional_string_formatting_methods = M:Test.MyFormat(System.String,System.Object[])~System.String")] - public async Task EditorConfigConfiguration_AdditionalStringFormattingMethods(string editorConfigText) + public async Task CA2241_CA2250_EditorConfigConfiguration_AdditionalStringFormattingMethods(string editorConfigText) { var csharpTest = new VerifyCS.Test { @@ -446,7 +960,18 @@ class Test void M1(string param) { - var a = MyFormat("""", 1); + // Too many args + var s1 = MyFormat(""{0}"", 1, 2); + // Too many args and missing format index + var s2 = MyFormat(""{1}"", 1, 2, 3); + + // Not enough args + var s3 = MyFormat(""{0} {1}"", 1); + // Not enough args and missing format index + var s4 = MyFormat(""{0} {2}"", 1); + + // Enough args and missing format index + var s5 = MyFormat(""{1}"", 1, 2); } }" }, @@ -456,9 +981,15 @@ void M1(string param) if (editorConfigText.Length > 0) { - csharpTest.ExpectedDiagnostics.Add( - // Test0.cs(8,17): warning CA2241: Provide correct arguments to formatting methods - GetCSharpResultAt(8, 17)); + // TODO: Make sure to report only the right diagnostic not both + csharpTest.ExpectedDiagnostics.AddRange(new[] + { + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(9, 18), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(11, 18), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(14, 18), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(16, 18), + VerifyCS.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(19, 18), + }); } await csharpTest.RunAsync(); @@ -476,7 +1007,18 @@ Return format End Function Private Sub M1(ByVal param As String) - Dim a = MyFormat("""", 1) + ' Too many args + Dim s1 = MyFormat(""{0}"", 1, 2) + ' Too many args and missing format index + Dim s2 = MyFormat(""{1}"", 1, 2, 3) + + ' Not enough args + Dim s3 = MyFormat(""{0} {1}"", 1) + ' Not enough args and missing format index + Dim s4 = MyFormat(""{0} {2}"", 1) + + ' Enough args and missing format index + Dim s5 = MyFormat(""{1}"", 1, 2) End Sub End Class" }, @@ -486,22 +1028,18 @@ End Class" if (editorConfigText.Length > 0) { - basicTest.ExpectedDiagnostics.Add( - // Test0.vb(8,17): warning CA2241: Provide correct arguments to formatting methods - GetBasicResultAt(8, 17)); + // TODO: Make sure to report only the right diagnostic not both + basicTest.ExpectedDiagnostics.AddRange(new[] + { + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsRule).WithLocation(9, 18), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.TooManyArgsMissingFormatIndexRule).WithLocation(11, 18), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsRule).WithLocation(14, 18), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.NotEnoughArgsMissingFormatIndexRule).WithLocation(16, 18), + VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.EnoughArgsMissingFormatIndexRule).WithLocation(19, 18), + }); } await basicTest.RunAsync(); } - - #endregion - - private static DiagnosticResult GetCSharpResultAt(int line, int column) - => VerifyCS.Diagnostic() - .WithLocation(line, column); - - private static DiagnosticResult GetBasicResultAt(int line, int column) - => VerifyVB.Diagnostic() - .WithLocation(line, column); } } \ No newline at end of file diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 7baccb8b66..8366c30f30 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -14,7 +14,7 @@ Globalization: CA2101, CA1300-CA1310 Mobility: CA1600-CA1601 Performance: HA, CA1800-CA1838 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5403 -Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2249 +Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2250 Naming: CA1700-CA1726 Interoperability: CA1400-CA1417 Maintainability: CA1500-CA1509