예제 #1
0
            public string TypeArgsText(SyntaxNodeAnalysisContext context)
            {
                var builder = StringBuilderPool.Borrow();

                for (var i = 0; i < this.Count; i++)
                {
                    if (i > 0)
                    {
                        _ = builder.Append(", ");
                    }

                    _ = builder.Append(this[i].ToString(context));
                }

                if (!this.method.ReturnsVoid)
                {
                    if (builder.Length > 0)
                    {
                        _ = builder.Append(", ");
                    }

                    _ = builder.Append(this.method.ReturnType.ToString(context));
                }

                return(builder.Return());
            }
예제 #2
0
        /// <summary>
        /// Check the solution for compiler errors and warnings, uses:
        /// </summary>
        public static async Task NoCompilerErrorsAsync(Solution solution, IReadOnlyList <string> allowedIds, AllowedDiagnostics allowedDiagnostics)
        {
            var diagnostics = await Analyze.GetDiagnosticsAsync(solution).ConfigureAwait(false);

            var introducedDiagnostics = diagnostics
                                        .SelectMany(x => x)
                                        .Where(x => IsIncluded(x, allowedDiagnostics))
                                        .ToArray();

            if (introducedDiagnostics.Select(x => x.Id)
                .Except(allowedIds ?? Enumerable.Empty <string>())
                .Any())
            {
                var error = StringBuilderPool.Borrow();
                error.AppendLine($"Found error{(introducedDiagnostics.Length > 1 ? "s" : string.Empty)}.");
                foreach (var introducedDiagnostic in introducedDiagnostics)
                {
                    var errorInfo = await introducedDiagnostic.ToStringAsync(solution).ConfigureAwait(false);

                    error.AppendLine($"{errorInfo}");
                }

                throw AssertException.Create(StringBuilderPool.Return(error));
            }
        }
예제 #3
0
        /// <summary>
        /// Returns what System.Type.FullName returns.
        /// </summary>
        /// <param name="type">The <see cref="INamedTypeSymbol"/>.</param>
        /// <returns>What System.Type.FullName returns.</returns>
        public static string FullName(this INamedTypeSymbol type)
        {
            if (type is null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            var builder  = StringBuilderPool.Borrow();
            var previous = default(SymbolDisplayPart);

            foreach (var part in type.ToDisplayParts(Simple))
            {
                switch (part.Kind)
                {
                case SymbolDisplayPartKind.ClassName:
                case SymbolDisplayPartKind.InterfaceName:
                case SymbolDisplayPartKind.StructName:
                case SymbolDisplayPartKind.NamespaceName:
                    if (part.Symbol is { } symbol)
                    {
                        builder.Append(symbol.MetadataName);
                    }
                    else
                    {
                        throw new InvalidOperationException($"Part symbol is null {part}.");
                    }

                    break;
예제 #4
0
        internal static bool TryGetTypesArrayText(ImmutableArray <IParameterSymbol> parameters, SemanticModel semanticModel, int position, out string typesArrayText)
        {
            if (parameters.Length == 0)
            {
                typesArrayText = "Type.EmptyTypes";
                return(true);
            }

            var builder = StringBuilderPool.Borrow().Append("new[] { ");

            for (var i = 0; i < parameters.Length; i++)
            {
                var parameter = parameters[i];
                if (!semanticModel.IsAccessible(position, parameter.Type))
                {
                    _ = builder.Return();
                    typesArrayText = null;
                    return(false);
                }

                if (i != 0)
                {
                    _ = builder.Append(", ");
                }

                _ = builder.Append("typeof(")
                    .Append(parameter.Type.ToString(semanticModel, position))
                    .Append(")");
            }

            typesArrayText = builder.Append(" }").Return();
            return(true);
        }
예제 #5
0
        private static async Task AssertNoCompilerErrorsAsync(CodeFixProvider codeFix, Solution fixedSolution)
        {
            var diagnostics = await Analyze.GetDiagnosticsAsync(fixedSolution).ConfigureAwait(false);

            var introducedDiagnostics = diagnostics
                                        .SelectMany(x => x)
                                        .Where(IsIncluded)
                                        .ToArray();

            if (introducedDiagnostics.Select(x => x.Id)
                .Except(DiagnosticSettings.AllowedErrorIds())
                .Any())
            {
                var errorBuilder = StringBuilderPool.Borrow();
                errorBuilder.AppendLine($"{codeFix} introduced syntax error{(introducedDiagnostics.Length > 1 ? "s" : string.Empty)}.");
                foreach (var introducedDiagnostic in introducedDiagnostics)
                {
                    var errorInfo = await introducedDiagnostic.ToStringAsync(fixedSolution).ConfigureAwait(false);

                    errorBuilder.AppendLine($"{errorInfo}");
                }

                errorBuilder.AppendLine("First source file with error is:");
                var sources = await Task.WhenAll(fixedSolution.Projects.SelectMany(p => p.Documents).Select(d => CodeReader.GetStringFromDocumentAsync(d, Formatter.Annotation, CancellationToken.None)));

                var lineSpan = introducedDiagnostics.First().Location.GetMappedLineSpan();
                var match    = sources.SingleOrDefault(x => CodeReader.FileName(x) == lineSpan.Path);
                errorBuilder.Append(match);
                errorBuilder.AppendLine();
                throw AssertException.Create(errorBuilder.Return());
            }
        }
예제 #6
0
        private static void NoCompilerErrors(IReadOnlyList <ImmutableArray <Diagnostic> > diagnostics, IReadOnlyList <string> allowedIds, AllowedDiagnostics allowedDiagnostics)
        {
            var introducedDiagnostics = diagnostics
                                        .SelectMany(x => x)
                                        .Where(x => IsIncluded(x, allowedDiagnostics))
                                        .Where(x => !IsExcluded(x))
                                        .ToArray();

            if (introducedDiagnostics.Select(x => x.Id)
                .Except(allowedIds ?? Enumerable.Empty <string>())
                .Any())
            {
                var error = StringBuilderPool.Borrow();
                error.AppendLine($"Found error{(introducedDiagnostics.Length > 1 ? "s" : string.Empty)}.");
                foreach (var introducedDiagnostic in introducedDiagnostics)
                {
                    error.AppendLine($"{introducedDiagnostic.ToErrorString()}");
                }

                throw new AssertException(StringBuilderPool.Return(error));
            }

            bool IsExcluded(Diagnostic diagnostic)
            {
                if (allowedIds.Contains(diagnostic.Id))
                {
                    return(true);
                }

                return(diagnostic.Id switch
                {
                    "CS1061" when diagnostic.GetMessage(CultureInfo.InvariantCulture).Contains("does not contain a definition for 'InitializeComponent' and no accessible extension method 'InitializeComponent' accepting a first argument of type") => true,
                    _ => false,
                });
            }
예제 #7
0
        // ReSharper disable once UnusedMethodReturnValue.Local
        private static DocumentEditor AddDefaultFieldWithDocs(DocumentEditor editor, ClassDeclarationSyntax containingType)
        {
            var code = StringBuilderPool.Borrow()
                       .AppendLine(DefaultDocs)
                       .AppendLine(string.Format(DefaultFieldFormat, Modifier(containingType), containingType.Identifier.ValueText))
                       .Return();

            return(editor.AddField(containingType, ParseField(code))
                   .Seal(containingType));
        }
 public void Parallel()
 {
     using var builder1 = StringBuilderPool.Borrow();
     using var builder2 = StringBuilderPool.Borrow();
     builder1.Append("a");
     builder2.Append("b");
     Assert.AreNotSame(GetInner(builder1), GetInner(builder2));
     Assert.AreEqual("a", builder1.ToString());
     Assert.AreEqual("b", builder2.ToString());
 }
예제 #9
0
        // ReSharper disable once UnusedMethodReturnValue.Local
        private static DocumentEditor AddDefaultPropertyWithDocs(DocumentEditor editor, ClassDeclarationSyntax containingType)
        {
            var code = StringBuilderPool.Borrow()
                       .AppendLine(DefaultDocs)
                       .AppendLine(string.Format(CultureInfo.InvariantCulture, DefaultPropertyFormat, Modifier(containingType), containingType.Identifier.ValueText))
                       .Return();

            return(editor.AddProperty(containingType, ParseProperty(code))
                   .Seal(containingType));
        }
        public void BorrowAppendLineReturn()
        {
            var text = StringBuilderPool.Borrow()
                       .AppendLine("a")
                       .AppendLine()
                       .AppendLine("b")
                       .Return();

            Assert.AreEqual("a\r\n\r\nb\r\n", text);
        }
        private static MethodDeclarationSyntax IMultiValueConverterConvertBack(string containingTypeName)
        {
            var code = StringBuilderPool.Borrow()
                       .AppendLine("        object[] System.Windows.Data.IMultiValueConverter.ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)")
                       .AppendLine("        {")
                       .AppendLine($"            throw new System.NotSupportedException($\"{{nameof({containingTypeName})}} can only be used in OneWay bindings\");")
                       .AppendLine("        }")
                       .Return();

            return(ParseMethod(code));
        }
예제 #12
0
        /// <summary>
        /// Assert that <paramref name="diagnostics"/> is empty. Throw an AssertException with details if not.
        /// </summary>
        /// <param name="diagnostics">The diagnostics.</param>
        public static void NoDiagnostics(IReadOnlyList <ImmutableArray <Diagnostic> > diagnostics)
        {
            if (diagnostics.Any(x => x.Any()))
            {
                var builder = StringBuilderPool.Borrow().AppendLine("Expected no diagnostics, found:");
                foreach (var diagnostic in diagnostics.SelectMany(x => x))
                {
                    builder.AppendLine(diagnostic.ToErrorString());
                }

                throw new AssertException(builder.Return());
            }
        }
예제 #13
0
        /// <summary>
        /// Gets the line indicated by <paramref name="position"/> and inserts ↓ before the character.
        /// </summary>
        /// <param name="code">The code to parse.</param>
        /// <param name="position">The error position.</param>
        /// <returns>A string with the line with error indicated.</returns>
        public static string GetLineWithErrorIndicated(string code, LinePosition position)
        {
            var builder   = StringBuilderPool.Borrow();
            var line      = 0;
            var character = 0;

            foreach (var c in code)
            {
                if (c == '\r')
                {
                    continue;
                }

                if (c == '\n')
                {
                    if (!builder.IsEmpty)
                    {
                        return(builder.Return());
                    }

                    line++;
                    character = 0;
                    continue;
                }

                if (line == position.Line)
                {
                    if (character == position.Character)
                    {
                        builder.Append('↓');
                    }

                    builder.Append(c);
                }

                character++;
            }

            if (!builder.IsEmpty)
            {
                if (position.Character == builder.Length)
                {
                    builder.Append('↓');
                }

                return(builder.Return());
            }

            StringBuilderPool.Return(builder);
            return($"Code dod not have position {position}");
        }
예제 #14
0
        internal static string ToDisplayString(this BindingFlags flags, SyntaxNode?location)
        {
            var usingStatic = IsUsingStatic(location);
            var builder     = StringBuilderPool.Borrow();

            //// below is in specific order.
            AppendIf(BindingFlags.Public);
            AppendIf(BindingFlags.NonPublic);
            AppendIf(BindingFlags.Static);
            AppendIf(BindingFlags.Instance);
            AppendIf(BindingFlags.DeclaredOnly);
            AppendIf(BindingFlags.FlattenHierarchy);

            // below is in no specific order
            AppendIf(BindingFlags.CreateInstance);
            AppendIf(BindingFlags.Default);
            AppendIf(BindingFlags.ExactBinding);
            AppendIf(BindingFlags.GetField);
            AppendIf(BindingFlags.GetProperty);
            AppendIf(BindingFlags.IgnoreCase);
            AppendIf(BindingFlags.InvokeMethod);
            AppendIf(BindingFlags.IgnoreReturn);
            AppendIf(BindingFlags.OptionalParamBinding);
            AppendIf(BindingFlags.PutDispProperty);
            AppendIf(BindingFlags.PutRefDispProperty);
            AppendIf(BindingFlags.SetField);
            AppendIf(BindingFlags.SetProperty);
            AppendIf(BindingFlags.SuppressChangeType);
            AppendIf(BindingFlags.DoNotWrapExceptions);

            return(builder.Return());

            void AppendIf(BindingFlags flag)
            {
                if (flags.HasFlagFast(flag))
                {
                    if (builder.Length != 0)
                    {
                        _ = builder.Append(" | ");
                    }

                    if (!usingStatic)
                    {
                        _ = builder.Append("BindingFlags.");
                    }

                    _ = builder.Append(flag.Name());
                }
            }
        }
        private static bool ShouldKebabCase(PathSegment segment, out string kebabCase)
        {
            if (segment.Parameter == null &&
                IsHumpOrSnakeCased(segment.Span))
            {
                var builder = StringBuilderPool.Borrow();
                for (var i = 0; i < segment.Span.Length; i++)
                {
                    var c = segment.Span[i];
                    if (char.IsUpper(c))
                    {
                        if (i > 0)
                        {
                            _ = builder.Append("-");
                        }

                        _ = builder.Append(char.ToLower(c));
                    }
                    else if (c == '_')
                    {
                        _ = builder.Append("-");
                    }
                    else
                    {
                        _ = builder.Append(c);
                    }
                }

                kebabCase = builder.Return();
                return(true);
            }

            kebabCase = null;
            return(false);

            bool IsHumpOrSnakeCased(Span span)
            {
                for (var i = 0; i < span.Length; i++)
                {
                    var c = span[i];
                    if (char.IsUpper(c) ||
                        c == '_')
                    {
                        return(true);
                    }
                }

                return(false);
            }
        }
예제 #16
0
        private static bool IsCorrectDelegateType(MethodTypes methodTypes, IMethodSymbol delegateMethod, SyntaxNodeAnalysisContext context, out string delegateText)
        {
            if (!methodTypes.ReturnType.Equals(delegateMethod.ReturnType))
            {
                delegateText = DelegateText();
                return(false);
            }

            if (methodTypes.Count == delegateMethod.Parameters.Length)
            {
                for (var i = 0; i < delegateMethod.Parameters.Length; i++)
                {
                    if (!methodTypes[i].Equals(delegateMethod.Parameters[i].Type))
                    {
                        delegateText = DelegateText();
                        return(false);
                    }
                }

                delegateText = null;
                return(true);
            }

            delegateText = DelegateText();
            return(false);

            string DelegateText()
            {
                if (methodTypes.ReturnType.SpecialType == SpecialType.System_Void)
                {
                    if (methodTypes.Count == 0)
                    {
                        return("System.Action");
                    }

                    return(StringBuilderPool.Borrow()
                           .Append("System.Action<")
                           .Append(methodTypes.TypeArgsText(context))
                           .Append(">")
                           .Return());
                }

                return(StringBuilderPool.Borrow()
                       .Append("System.Func<")
                       .Append(methodTypes.TypeArgsText(context))
                       .Append(">")
                       .Return());
            }
        }
예제 #17
0
        /// <inheritdoc/>
        protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context)
        {
            var document   = context.Document;
            var syntaxRoot = await document.GetSyntaxRootAsync(context.CancellationToken)
                             .ConfigureAwait(false);

            var semanticModel = await document.GetSemanticModelAsync(context.CancellationToken)
                                .ConfigureAwait(false);

            foreach (var diagnostic in context.Diagnostics)
            {
                if (syntaxRoot.TryFindNodeOrAncestor(diagnostic, out MethodDeclarationSyntax methodDeclaration) &&
                    semanticModel.TryGetSymbol(methodDeclaration, context.CancellationToken, out var method))
                {
                    if (ClrMethod.IsAttachedGet(method, semanticModel, context.CancellationToken, out var fieldOrProperty) &&
                        DependencyProperty.TryGetRegisteredName(fieldOrProperty, semanticModel, context.CancellationToken, out var registeredName))
                    {
                        var parameter = method.Parameters[0];
                        var text      = StringBuilderPool.Borrow()
                                        .AppendLine($"/// <summary>Helper for getting <see cref=\"{fieldOrProperty.Name}\"/> from <paramref name=\"{parameter.Name}\"/>.</summary>")
                                        .AppendLine($"/// <param name=\"{parameter.Name}\"><see cref=\"{parameter.Type.ToMinimalDisplayString(semanticModel, methodDeclaration.SpanStart, SymbolDisplayFormat.MinimallyQualifiedFormat)}\"/> to read <see cref=\"{fieldOrProperty.Name}\"/> from.</param>")
                                        .AppendLine($"/// <returns>{registeredName} property value.</returns>")
                                        .Return();
                        context.RegisterCodeFix(
                            "Add standard documentation.",
                            (editor, _) => editor.ReplaceNode(methodDeclaration, x => x.WithDocumentationText(text)),
                            this.GetType(),
                            diagnostic);
                    }
                    else if (ClrMethod.IsAttachedSet(method, semanticModel, context.CancellationToken, out fieldOrProperty) &&
                             DependencyProperty.TryGetRegisteredName(fieldOrProperty, semanticModel, context.CancellationToken, out registeredName))
                    {
                        var parameter = method.Parameters[0];
                        var text      = StringBuilderPool.Borrow()
                                        .AppendLine($"/// <summary>Helper for setting <see cref=\"{fieldOrProperty.Name}\"/> on <paramref name=\"{parameter.Name}\"/>.</summary>")
                                        .AppendLine($"/// <param name=\"{parameter.Name}\"><see cref=\"{parameter.Type.ToMinimalDisplayString(semanticModel, methodDeclaration.SpanStart, SymbolDisplayFormat.MinimallyQualifiedFormat)}\"/> to set <see cref=\"{fieldOrProperty.Name}\"/> on.</param>")
                                        .AppendLine($"/// <param name=\"{method.Parameters[1].Name}\">{registeredName} property value.</param>")
                                        .Return();
                        context.RegisterCodeFix(
                            "Add standard documentation.",
                            (editor, _) => editor.ReplaceNode(methodDeclaration, x => x.WithDocumentationText(text)),
                            this.GetType(),
                            diagnostic);
                    }
                }
            }
        }
예제 #18
0
        private static CodeAction FindAction(IReadOnlyList <CodeAction> actions, string fixTitle)
        {
            if (fixTitle == null)
            {
                if (actions.TrySingle(out var action))
                {
                    return(action);
                }

                if (actions.Count == 0)
                {
                    throw new AssertException("Expected one code fix, was 0.");
                }

                throw new AssertException($"Expected only one code fix, found {actions.Count}:\r\n" +
                                          $"{string.Join("\r\n", actions.Select(x => x.Title))}\r\n" +
                                          "Use the overload that specifies title.");
            }
            else
            {
                if (actions.TrySingle(x => x.Title == fixTitle, out var action))
                {
                    return(action);
                }

                if (actions.All(x => x.Title != fixTitle))
                {
                    var errorBuilder = StringBuilderPool.Borrow();
                    errorBuilder.AppendLine($"Did not find a code fix with title {fixTitle}.").AppendLine("Found:");
                    foreach (var codeAction in actions)
                    {
                        errorBuilder.AppendLine(codeAction.Title);
                    }

                    throw new AssertException(StringBuilderPool.Return(errorBuilder));
                }

                if (actions.Count(x => x.Title == fixTitle) == 0)
                {
                    throw new AssertException("Expected one code fix, was 0.");
                }

                throw new AssertException($"Expected only one code fix, found {actions.Count}:\r\n" +
                                          $"{string.Join("\r\n", actions.Select(x => x.Title))}\r\n" +
                                          "Use the overload that specifies title.");
            }
        }
예제 #19
0
        private static async Task VerifyNoCompilerErrorsAsync(CodeFixProvider fix, Solution fixedSolution)
        {
            var diagnostics = await Analyze.GetDiagnosticsAsync(fixedSolution).ConfigureAwait(false);

            var introducedDiagnostics = diagnostics
                                        .SelectMany(x => x)
                                        .Where(IsIncluded)
                                        .ToArray();

            if (introducedDiagnostics.Select(x => x.Id)
#pragma warning disable CS0618 // Suppress until removed. Will be replaced with Metadatareferences.FromAttributes()
                .Except(SuppressedDiagnostics)
#pragma warning restore CS0618 // Suppress until removed. Will be replaced with Metadatareferences.FromAttributes()
                .Any())
            {
                var errorBuilder = StringBuilderPool.Borrow();
                errorBuilder.AppendLine($"{fix.GetType().Name} introduced syntax error{(introducedDiagnostics.Length > 1 ? "s" : string.Empty)}.");
                foreach (var introducedDiagnostic in introducedDiagnostics)
                {
                    errorBuilder.AppendLine($"{introducedDiagnostic.ToErrorString()}");
                }

                var sources = await Task.WhenAll(fixedSolution.Projects.SelectMany(p => p.Documents).Select(d => CodeReader.GetStringFromDocumentAsync(d, CancellationToken.None)));

                errorBuilder.AppendLine("First source file with error is:");
                var lineSpan = introducedDiagnostics.First().Location.GetMappedLineSpan();
                if (sources.TrySingle(x => CodeReader.FileName(x) == lineSpan.Path, out var match))
                {
                    errorBuilder.AppendLine(match);
                }
                else if (sources.TryFirst(x => CodeReader.FileName(x) == lineSpan.Path, out _))
                {
                    errorBuilder.AppendLine($"Found more than one document for {lineSpan.Path}.");
                    foreach (string source in sources.Where(x => CodeReader.FileName(x) == lineSpan.Path))
                    {
                        errorBuilder.AppendLine(source);
                    }
                }
                else
                {
                    errorBuilder.AppendLine($"Did not find a single document for {lineSpan.Path}.");
                }

                throw new AssertException(errorBuilder.Return());
            }
        }
예제 #20
0
        public void UseTwice()
        {
            Clear(); // testrunner may run in parallel creating more than one builder.
            StringBuilder inner1;

            using (var builder = StringBuilderPool.Borrow())
            {
                builder.Append("a");
                inner1 = GetInner(builder);
                Assert.AreEqual("a", builder.ToString());
            }

            using (var builder = StringBuilderPool.Borrow())
            {
                builder.Append("bc");
                var inner2 = GetInner(builder);
                Assert.AreSame(inner1, inner2);
                Assert.AreEqual("bc", builder.ToString());
            }
        }
예제 #21
0
        /// <summary>
        /// Verifies that <paramref name="code"/> produces no diagnostics when analyzed with <paramref name="analyzer"/>.
        /// </summary>
        /// <param name="analyzer">The analyzer.</param>
        /// <param name="code">The code to analyze.</param>
        /// <param name="compilationOptions">The <see cref="CSharpCompilationOptions"/> to use.</param>
        /// <param name="metadataReferences">The metadata references to use when compiling.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public static async Task ValidAsync(DiagnosticAnalyzer analyzer, IReadOnlyList <string> code, CSharpCompilationOptions compilationOptions, IReadOnlyList <MetadataReference> metadataReferences)
        {
            var diagnostics = await Analyze.GetDiagnosticsAsync(
                analyzer,
                code,
                compilationOptions,
                metadataReferences)
                              .ConfigureAwait(false);

            if (diagnostics.SelectMany(x => x).Any())
            {
                var builder = StringBuilderPool.Borrow().AppendLine("Expected no diagnostics, found:");
                foreach (var diagnostic in diagnostics.SelectMany(x => x))
                {
                    builder.AppendLine(diagnostic.ToString(code));
                }

                throw AssertException.Create(builder.Return());
            }
        }
예제 #22
0
            private static string Format(IReadOnlyList <IdentifierNameSyntax> flags)
            {
                var builder = StringBuilderPool.Borrow();

                for (var i = 0; i < flags.Count; i++)
                {
                    if (i > 0)
                    {
                        _ = builder.Append(" | ");
                    }

                    ExpressionSyntax flag = flags[i];
                    while (flag.Parent is MemberAccessExpressionSyntax memberAccess)
                    {
                        flag = memberAccess;
                    }

                    _ = builder.Append(flag);
                }

                return(builder.Return());
            }
예제 #23
0
        internal static bool TryGetKey(string text, [NotNullWhen(true)] out string?key)
        {
            if (string.IsNullOrEmpty(text))
            {
                key = null;
                return(false);
            }

            var builder = StringBuilderPool.Borrow()
                          .Append(Regex.Replace(text, "{(?<n>\\d+)}", x => $"__{x.Groups["n"].Value}__"))
                          .Replace(" ", "_")
                          .Replace("\r\n", "_n_")
                          .Replace("\n", "_n_")
                          .Replace(".", "_")
                          .Replace(",", "_")
                          .Replace(":", "_");

            if (char.IsDigit(builder[0]))
            {
                _ = builder.Insert(0, '_');
            }

            const int MaxLength = 100;

            if (builder.Length > MaxLength)
            {
                _ = builder.Remove(MaxLength, builder.Length - MaxLength);
            }

            key = builder.Return();
            if (key == "Resources")
            {
                key += "_";
            }

            return(SyntaxFacts.IsValidIdentifier(key));
        }
        private static void VerifyNoFix(Solution sln, IReadOnlyList <ImmutableArray <Diagnostic> > diagnostics, CodeFixProvider fix)
        {
            var fixableDiagnostics = diagnostics.SelectMany(x => x)
                                     .Where(x => fix.FixableDiagnosticIds.Contains(x.Id))
                                     .ToArray();

            foreach (var fixableDiagnostic in fixableDiagnostics)
            {
                var actions = Fix.GetActions(sln, fix, fixableDiagnostic);
                if (actions.Any())
                {
                    var builder = StringBuilderPool.Borrow()
                                  .AppendLine("Expected code to have no fixable diagnostics.")
                                  .AppendLine("The following actions were registered:");

                    foreach (var action in actions)
                    {
                        builder.AppendLine(action.Title);
                    }

                    throw new AssertException(builder.Return());
                }
            }
        }
예제 #25
0
        private static string CreateStub(DescriptorInfo descriptorInfo)
        {
            var descriptor = descriptorInfo.Descriptor;
            var stub       = Properties.Resources.DiagnosticDocTemplate
                             .AssertReplace("{ID}", descriptor.Id)
                             .AssertReplace("## ADD TITLE HERE", $"## {descriptor.Title.ToString(CultureInfo.InvariantCulture)}")
                             .AssertReplace("{SEVERITY}", descriptor.DefaultSeverity.ToString())
                             .AssertReplace("{ENABLED}", descriptor.IsEnabledByDefault ? "true" : "false")
                             .AssertReplace("{CATEGORY}", descriptor.Category)
                             .AssertReplace("ADD DESCRIPTION HERE", descriptor.Description.ToString(CultureInfo.InvariantCulture))
                             .AssertReplace("{TITLE}", descriptor.Title.ToString(CultureInfo.InvariantCulture));

            if (Analyzers.Count(x => x.SupportedDiagnostics.Any(d => d.Id == descriptor.Id)) == 1)
            {
                return(stub.AssertReplace("{TYPENAME}", descriptorInfo.Analyzer.GetType().Name)
                       .AssertReplace("{URL}", descriptorInfo.CodeFileUri));
            }

            var builder = StringBuilderPool.Borrow();
            var first   = true;

            foreach (var analyzer in Analyzers.Where(x => x.SupportedDiagnostics.Any(d => d.Id == descriptor.Id)))
            {
                _ = builder.AppendLine("  <tr>")
                    .AppendLine($"    <td>{(first ? "Code" : string.Empty)}</td>")
                    .AppendLine($"     <td><a href=\"{DescriptorInfo.GetCodeFileUri(analyzer)}\">{analyzer.GetType().Name}</a></td>")
                    .AppendLine("  </tr>");

                first = false;
            }

            var text = builder.Return();

            return(stub.Replace("  <tr>\r\n    <td>Code</td>\r\n    <td><a href=\"{URL}\">{TYPENAME}</a></td>\r\n  </tr>\r\n", text)
                   .Replace("  <tr>\n    <td>Code</td>\n    <td><a href=\"{URL}\">{TYPENAME}</a></td>\n  </tr>\n", text));
        }
예제 #26
0
            static string GetEnd(string text)
            {
                bool lastLine = false;
                var  builder  = StringBuilderPool.Borrow();

                for (var i = text.Length - 1; i >= 0; i--)
                {
                    switch (text[i])
                    {
                    case '\r':
                        if (lastLine)
                        {
                            return(builder.Return());
                        }

                        builder.Insert(0, "\\r");
                        break;

                    case '\n':
                        if (lastLine)
                        {
                            return(builder.Return());
                        }

                        builder.Insert(0, "\\n");
                        break;

                    default:
                        lastLine = true;
                        builder.Insert(0, text[i]);
                        break;
                    }
                }

                return(builder.Return());
            }
예제 #27
0
        private static CodeAction FindAction(IReadOnlyList <CodeAction> actions, string?fixTitle)
        {
            if (fixTitle is null)
            {
                if (actions.TrySingle(out var action))
                {
                    return(action);
                }

                if (actions.Count == 0)
                {
                    throw new AssertException("Expected one code fix, was 0.");
                }

                throw new AssertException(FoundManyMessage());
            }
            else
            {
                if (actions.TrySingle(x => x.Title == fixTitle, out var action))
                {
                    return(action);
                }

                if (actions.All(x => x.Title != fixTitle))
                {
                    var errorBuilder = StringBuilderPool.Borrow()
                                       .AppendLine($"Did not find a code fix with title {fixTitle}.").AppendLine("Found:");
                    foreach (var codeAction in actions)
                    {
                        errorBuilder.AppendLine(codeAction.Title);
                    }

                    throw new AssertException(StringBuilderPool.Return(errorBuilder));
                }

                if (actions.Any(x => x.Title == fixTitle))
                {
                    throw new AssertException(FoundManyMessage());
                }

                throw new AssertException("Expected one code fix, was 0.");
            }

            string FoundManyMessage()
            {
                if (actions.Select(x => x.Title).Distinct().Count() > 1)
                {
                    var errorBuilder = StringBuilderPool
                                       .Borrow()
                                       .AppendLine($"Expected only one code fix, found {actions.Count}:");
                    foreach (var a in actions.OrderBy(x => x.Title))
                    {
                        errorBuilder.AppendLine("  " + a.Title);
                    }

                    return(errorBuilder.AppendLine("Use the overload that specifies title.")
                           .AppendLine("Or maybe you meant to call RoslynAssert.FixAll?")
                           .Return());
                }
                else
                {
                    var errorBuilder = StringBuilderPool
                                       .Borrow()
                                       .AppendLine($"Expected only one code fix, found {actions.Count}:");
                    foreach (var a in actions.OrderBy(x => x.Title))
                    {
                        errorBuilder.AppendLine("  " + a.Title);
                    }

                    return(errorBuilder.AppendLine("Or maybe you meant to call RoslynAssert.FixAll?")
                           .Return());
                }
            }
        }
예제 #28
0
        /// <summary>
        /// Verify that two strings of code are equal. Agnostic to end of line characters.
        /// </summary>
        /// <param name="expected">The expected code.</param>
        /// <param name="actual">The actual code.</param>
        /// <param name="messageHeader">The first line to add to the exception message on error.</param>
        internal static void AreEqual(string expected, string actual, string messageHeader)
        {
            var expectedPos = 0;
            var actualPos   = 0;
            var line        = 1;

            while (expectedPos < expected.Length && actualPos < actual.Length)
            {
                var ec = expected[expectedPos];
                var ac = actual[actualPos];
                if (ec == '\r' || ac == '\r')
                {
                    if (ec == '\r')
                    {
                        expectedPos++;
                    }

                    if (ac == '\r')
                    {
                        actualPos++;
                    }

                    continue;
                }

                if (ec != ac)
                {
                    if (IsAt(expected, expectedPos, "\\r") ||
                        IsAt(actual, actualPos, "\\r"))
                    {
                        if (IsAt(expected, expectedPos, "\\r"))
                        {
                            expectedPos += 2;
                        }

                        if (IsAt(actual, actualPos, "\\r"))
                        {
                            actualPos += 2;
                        }

                        continue;
                    }

                    var errorBuilder = StringBuilderPool.Borrow();
                    if (messageHeader != null)
                    {
                        errorBuilder.AppendLine(messageHeader);
                    }

                    if (!IsSingleLine(expected) ||
                        !IsSingleLine(actual))
                    {
                        errorBuilder.AppendLine(
                            CodeReader.TryGetFileName(expected, out var fileName)
                                ? $"Mismatch on line {line} of file {fileName}."
                                : $"Mismatch on line {line}.");
                    }

                    var expectedLine = expected.Split('\n')[line - 1].Trim('\r');
                    var actualLine   = actual.Split('\n')[line - 1].Trim('\r');
                    var diffPos      = DiffPos(expectedLine, actualLine);

                    errorBuilder.AppendLine($"Expected: {expectedLine}")
                    .AppendLine($"Actual:   {actualLine}")
                    .AppendLine($"          {new string(' ', diffPos)}^");

                    if (!IsSingleLine(expected) ||
                        !IsSingleLine(actual))
                    {
                        errorBuilder.AppendLine("Expected:")
                        .Append(expected)
                        .AppendLine()
                        .AppendLine("Actual:")
                        .Append(actual)
                        .AppendLine();
                    }

                    throw new AssertException(errorBuilder.Return());
                }

                if (ec == '\n')
                {
                    line++;
                }

                expectedPos++;
                actualPos++;
            }

            while (expectedPos < expected.Length && expected[expectedPos] == '\r')
            {
                expectedPos++;
            }

            while (actualPos < actual.Length && actual[actualPos] == '\r')
            {
                actualPos++;
            }

            if (expectedPos == expected.Length && actualPos == actual.Length)
            {
                return;
            }

            var messageBuilder = StringBuilderPool.Borrow();

            if (messageHeader != null)
            {
                messageBuilder.AppendLine(messageHeader);
            }

            messageBuilder.AppendLine(CodeReader.TryGetFileName(expected, out var name) ? $"Mismatch at end of file {name}." : "Mismatch at end.")
            .Append("Expected: ").AppendLine(GetEnd(expected))
            .Append("Actual:   ").AppendLine(GetEnd(actual))
            .AppendLine($"          {new string(' ', DiffPos(GetEnd(expected), GetEnd(actual)))}^");

            if (!IsSingleLine(expected) ||
                !IsSingleLine(actual))
            {
                messageBuilder.AppendLine("Expected:")
                .Append(expected)
                .AppendLine()
                .AppendLine("Actual:")
                .Append(actual)
                .AppendLine();
            }

            throw new AssertException(messageBuilder.Return());

            bool IsSingleLine(string text)
            {
                bool foundNewLine = false;

                foreach (var c in text)
                {
                    switch (c)
                    {
                    case '\r':
                    case '\n':
                        foundNewLine = true;
                        break;

                    default:
                        if (foundNewLine)
                        {
                            return(false);
                        }

                        break;
                    }
                }

                return(true);
            }

            int DiffPos(string expectedLine, string actualLine)
            {
                var diffPos = Math.Min(expectedLine.Length, actualLine.Length);

                for (var i = 0; i < Math.Min(expectedLine.Length, actualLine.Length); i++)
                {
                    if (expectedLine[i] != actualLine[i])
                    {
                        diffPos = i;
                        break;
                    }
                }

                return(diffPos);
            }

            string GetEnd(string text)
            {
                bool lastLine = false;
                var  builder  = StringBuilderPool.Borrow();

                for (var i = text.Length - 1; i >= 0; i--)
                {
                    switch (text[i])
                    {
                    case '\r':
                        if (lastLine)
                        {
                            return(builder.Return());
                        }

                        builder.Insert(0, "\\r");
                        break;

                    case '\n':
                        if (lastLine)
                        {
                            return(builder.Return());
                        }

                        builder.Insert(0, "\\n");
                        break;

                    default:
                        lastLine = true;
                        builder.Insert(0, text[i]);
                        break;
                    }
                }

                return(builder.Return());
            }
        }
예제 #29
0
        /// <summary>Adjust each row in <paramref name="text"/> to start with <paramref name="leadingWhitespace"/>.</summary>
        /// <param name="text">The text.</param>
        /// <param name="leadingWhitespace">The whitespace to adjust each row to have.</param>
        /// <returns><paramref name="text"/> with each line adjusted to start with <paramref name="leadingWhitespace"/>.</returns>
        public static string WithLeadingWhiteSpace(this string text, string?leadingWhitespace)
        {
            if (text is null)
            {
                throw new ArgumentNullException(nameof(text));
            }

            if (leadingWhitespace is null)
            {
                return(text);
            }

            var eol = text.IndexOf('\n');

            if (eol < 0 ||
                eol == text.Length - 1)
            {
                var substring = FindSubstring(0, text.Length);
                return(leadingWhitespace + text.Substring(substring.Start, substring.Length));
            }

            var builder = StringBuilderPool.Borrow();
            var pos     = 0;

            do
            {
                var substring = FindSubstring(pos, eol + 1);
                _ = builder.Append(leadingWhitespace)
                    .Append(text, substring.Start, substring.Length);
                pos = eol + 1;
            }while ((eol = text.IndexOf('\n', pos)) > 0);

            if (pos < text.Length - 1)
            {
                var substring = FindSubstring(pos, text.Length);
                _ = builder.Append(leadingWhitespace)
                    .Append(text, substring.Start, substring.Length);
            }

            return(builder.Return());

            Substring FindSubstring(int start, int end)
            {
                if (text[start] != ' ')
                {
                    return(new Substring(start, end));
                }

                if (string.IsNullOrEmpty(leadingWhitespace))
                {
                    return(new Substring(text.CountWhile(x => x == ' ', start, end), end));
                }

                var indexOf = text.IndexOf(leadingWhitespace, StringComparison.Ordinal);

                if (indexOf == 0)
                {
                    var offset = start;
                    while (text.StartsWith(offset + 1, leadingWhitespace !))
                    {
                        offset++;
                    }

                    return(new Substring(offset + leadingWhitespace !.Length, end));
                }

                if (indexOf > 0)
                {
                    return(IsWhitespaceTo(text, start, indexOf)
                        ? new Substring(indexOf, end)
                        : new Substring(start, end));
                }

                return(new Substring(text.CountWhile(x => x == ' ', start, end), end));
            }
        }
예제 #30
0
        private static string CreateStub(DescriptorInfo descriptorInfo)
        {
            var descriptor = descriptorInfo.Descriptor;
            var stub       = $@"# {descriptor.Id}
## {descriptor.Title.ToString(CultureInfo.InvariantCulture)}

<!-- start generated table -->
<table>
  <tr>
    <td>CheckId</td>
    <td>{descriptor.Id}</td>
  </tr>
  <tr>
    <td>Severity</td>
    <td>{descriptor.DefaultSeverity.ToString()}</td>
  </tr>
  <tr>
    <td>Enabled</td>
    <td>{(descriptor.IsEnabledByDefault ? "True" : "False")}</td>
  </tr>
  <tr>
    <td>Category</td>
    <td>{descriptor.Category}</td>
  </tr>
  <tr>
    <td>Code</td>
    <td><a href=""<URL>""><TYPENAME></a></td>
  </tr>
</table>
<!-- end generated table -->

## Description

{descriptor.Description.ToString(CultureInfo.InvariantCulture)}

## Motivation

ADD MOTIVATION HERE

## How to fix violations

ADD HOW TO FIX VIOLATIONS HERE

<!-- start generated config severity -->
## Configure severity

### Via ruleset file.

Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx).

### Via #pragma directive.
```C#
#pragma warning disable {descriptor.Id} // {descriptor.Title.ToString(CultureInfo.InvariantCulture)}
Code violating the rule here
#pragma warning restore {descriptor.Id} // {descriptor.Title.ToString(CultureInfo.InvariantCulture)}
```

Or put this at the top of the file to disable all instances.
```C#
#pragma warning disable {descriptor.Id} // {descriptor.Title.ToString(CultureInfo.InvariantCulture)}
```

### Via attribute `[SuppressMessage]`.

```C#
[System.Diagnostics.CodeAnalysis.SuppressMessage(""{descriptor.Category}"", 
    ""{descriptor.Id}:{descriptor.Title.ToString(CultureInfo.InvariantCulture)}"", 
    Justification = ""Reason..."")]
```
<!-- end generated config severity -->";

            if (Analyzers.Count(x => x.SupportedDiagnostics.Any(d => d.Id == descriptor.Id)) == 1)
            {
                return(stub.AssertReplace("<TYPENAME>", descriptorInfo.Analyzer.GetType().Name)
                       .AssertReplace("<URL>", descriptorInfo.CodeFileUri));
            }

            var builder = StringBuilderPool.Borrow();
            var first   = true;

            foreach (var analyzer in Analyzers.Where(x => x.SupportedDiagnostics.Any(d => d.Id == descriptor.Id)))
            {
                _ = builder.AppendLine("  <tr>")
                    .AppendLine($"    <td>{(first ? "Code" : string.Empty)}</td>")
                    .AppendLine($"     <td><a href=\"{DescriptorInfo.GetCodeFileUri(analyzer)}\">{analyzer.GetType().Name}</a></td>")
                    .AppendLine("  </tr>");

                first = false;
            }

            var text = builder.Return();

            return(stub.Replace("  <tr>\r\n    <td>Code</td>\r\n    <td><a href=\"{URL}\">{TYPENAME}</a></td>\r\n  </tr>\r\n", text, StringComparison.Ordinal)
                   .Replace("  <tr>\n    <td>Code</td>\n    <td><a href=\"{URL}\">{TYPENAME}</a></td>\n  </tr>\n", text, StringComparison.Ordinal));
        }