public static CodeStyleOption2 <ExpressionBodyPreference> ParseExpressionBodyPreference( string optionString, CodeStyleOption2 <ExpressionBodyPreference> @default) { // optionString must be similar to true:error or when_on_single_line:suggestion. if (CodeStyleHelpers.TryGetCodeStyleValueAndOptionalNotification(optionString, out var value, out var notificationOpt)) { // A notification value must be provided. if (notificationOpt != null) { if (bool.TryParse(value, out var boolValue)) { return(boolValue ? new CodeStyleOption2 <ExpressionBodyPreference>(ExpressionBodyPreference.WhenPossible, notificationOpt) : new CodeStyleOption2 <ExpressionBodyPreference>(ExpressionBodyPreference.Never, notificationOpt)); } if (value == "when_on_single_line") { return(new CodeStyleOption2 <ExpressionBodyPreference>(ExpressionBodyPreference.WhenOnSingleLine, notificationOpt)); } } } return(@default); }
internal static async Task<Document> TransformDocumentIfRequiredAsync( Document document, SimplifierOptions simplifierOptions, CodeStyleOption2<AddImportPlacement> importPlacementStyleOption, CancellationToken cancellationToken) { var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var compilationUnit = (CompilationUnitSyntax)syntaxRoot; var (placement, preferPreservation) = DeterminePlacement(compilationUnit, importPlacementStyleOption); if (preferPreservation) { return document; } // We are called from a diagnostic, but also for all new documents, so check if there are any usings at all // otherwise there is nothing to do. var allUsingDirectives = GetAllUsingDirectives(compilationUnit); if (allUsingDirectives.Count == 0) { return document; } return await GetTransformedDocumentAsync(document, compilationUnit, allUsingDirectives, placement, simplifierOptions, cancellationToken).ConfigureAwait(false); }
public void TestOptionSerialization2() { // Verify that ExpressionBodyPreference-options can migrate to bool-options. var option = new CodeStyleOption2 <ExpressionBodyPreference>( ExpressionBodyPreference.Never, NotificationOption2.Silent ); var serialized = option.ToXElement(); var deserialized = CodeStyleOption2 <bool> .FromXElement(serialized); Assert.False(deserialized.Value); option = new CodeStyleOption2 <ExpressionBodyPreference>( ExpressionBodyPreference.WhenPossible, NotificationOption2.Silent ); serialized = option.ToXElement(); deserialized = CodeStyleOption2 <bool> .FromXElement(serialized); Assert.True(deserialized.Value); // This new values can't actually translate back to a bool. So we'll just get the default // value for this option. option = new CodeStyleOption2 <ExpressionBodyPreference>( ExpressionBodyPreference.WhenOnSingleLine, NotificationOption2.Silent ); serialized = option.ToXElement(); deserialized = CodeStyleOption2 <bool> .FromXElement(serialized); Assert.Equal(default, deserialized.Value);
public bool CanOfferUseExpressionBody( CodeStyleOption2 <ExpressionBodyPreference> preference, TDeclaration declaration, bool forAnalyzer) { var userPrefersExpressionBodies = preference.Value != ExpressionBodyPreference.Never; var analyzerDisabled = preference.Notification.Severity == ReportDiagnostic.Suppress; // If the user likes expression bodies, then we offer expression bodies from the diagnostic analyzer. // If the user does not like expression bodies then we offer expression bodies from the refactoring provider. // If the analyzer is disabled completely, the refactoring is enabled in both directions. if (userPrefersExpressionBodies == forAnalyzer || (!forAnalyzer && analyzerDisabled)) { var expressionBody = GetExpressionBody(declaration); if (expressionBody == null) { // They don't have an expression body. See if we could convert the block they // have into one. var conversionPreference = forAnalyzer ? preference.Value : ExpressionBodyPreference.WhenPossible; return(TryConvertToExpressionBody(declaration, conversionPreference, expressionWhenOnSingleLine: out _, semicolonWhenOnSingleLine: out _)); } } return(false); }
private Diagnostic AnalyzeSyntax( SemanticModel semanticModel, CodeStyleOption2 <ExpressionBodyPreference> option, LambdaExpressionSyntax declaration, CancellationToken cancellationToken) { if (CanOfferUseExpressionBody(option.Value, declaration)) { var location = GetDiagnosticLocation(declaration); var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); var properties = ImmutableDictionary <string, string> .Empty; return(DiagnosticHelper.Create( CreateDescriptorWithId(UseExpressionBodyTitle, UseExpressionBodyTitle), location, option.Notification.Severity, additionalLocations, properties)); } if (CanOfferUseBlockBody(semanticModel, option.Value, declaration, cancellationToken)) { // They have an expression body. Create a diagnostic to convert it to a block // if they don't want expression bodies for this member. var location = GetDiagnosticLocation(declaration); var properties = ImmutableDictionary <string, string> .Empty; var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); return(DiagnosticHelper.Create( CreateDescriptorWithId(UseBlockBodyTitle, UseBlockBodyTitle), location, option.Notification.Severity, additionalLocations, properties)); } return(null); }
private void SetXmlOption <T>(Option2 <CodeStyleOption2 <T> > option, string value) { var convertedValue = CodeStyleOption2 <T> .FromXElement(XElement.Parse(value)); _workspace.TryApplyChanges(_workspace.CurrentSolution.WithOptions(_workspace.Options .WithChangedOption(option, convertedValue))); }
private void SetXmlOption(PerLanguageOption2 <CodeStyleOption2 <bool> > option, string value) { var convertedValue = CodeStyleOption2 <bool> .FromXElement(XElement.Parse(value)); _workspace.TryApplyChanges(_workspace.CurrentSolution.WithOptions(_workspace.Options .WithChangedOption(option, LanguageNames.CSharp, convertedValue))); }
protected override void ProcessCompilationUnit( SyntaxTreeAnalysisContext context, CodeStyleOption2 <AccessibilityModifiersRequired> option, CompilationUnitSyntax compilationUnit ) { ProcessMembers(context, option, compilationUnit.Members); }
/// <summary> /// Helper to get the true ReportDiagnostic severity for a given option. Importantly, this /// handle ReportDiagnostic.Default and will map that back to the appropriate value in that /// case. /// </summary> internal static ReportDiagnostic GetOptionSeverity(CodeStyleOption2 <ExpressionBodyPreference> optionValue) { var severity = optionValue.Notification.Severity; return(severity == ReportDiagnostic.Default ? severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) : severity); }
public void TestParseExpressionBodyPreference(string optionString, int parsedValue, ReportDiagnostic severity) { var defaultValue = new CodeStyleOption2 <ExpressionBodyPreference>(ExpressionBodyPreference.Never, NotificationOption2.Error); var codeStyleOption = CSharpCodeStyleOptions.ParseExpressionBodyPreference(optionString, defaultValue); Assert.NotSame(defaultValue, codeStyleOption); Assert.Equal((ExpressionBodyPreference)parsedValue, codeStyleOption.Value); Assert.Equal(severity, codeStyleOption.Notification.Severity); }
public bool CanOfferUseBlockBody( CodeStyleOption2 <ExpressionBodyPreference> preference, TDeclaration declaration, bool forAnalyzer, out bool fixesError, [NotNullWhen(true)] out ArrowExpressionClauseSyntax?expressionBody) { var userPrefersBlockBodies = preference.Value == ExpressionBodyPreference.Never; var analyzerDisabled = preference.Notification.Severity == ReportDiagnostic.Suppress; expressionBody = GetExpressionBody(declaration); if (expressionBody?.TryConvertToBlock( SyntaxFactory.Token(SyntaxKind.SemicolonToken), false, block: out _) != true) { fixesError = false; return(false); } var languageVersion = declaration.GetLanguageVersion(); if (languageVersion < LanguageVersion.CSharp7) { if (expressionBody !.Expression.IsKind(SyntaxKind.ThrowExpression)) { // If they're using a throw expression in a declaration and it's prior to C# 7 // then always mark this as something that can be fixed by the analyzer. This way // we'll also get 'fix all' working to fix all these cases. fixesError = true; return(true); } if (declaration is AccessorDeclarationSyntax or ConstructorDeclarationSyntax) { // If they're using expression bodies for accessors/constructors and it's prior to C# 7 // then always mark this as something that can be fixed by the analyzer. This way // we'll also get 'fix all' working to fix all these cases. fixesError = true; return(true); } } if (languageVersion < LanguageVersion.CSharp6) { // If they're using expression bodies prior to C# 6, then always mark this as something // that can be fixed by the analyzer. This way we'll also get 'fix all' working to fix // all these cases. fixesError = true; return(true); } // If the user likes block bodies, then we offer block bodies from the diagnostic analyzer. // If the user does not like block bodies then we offer block bodies from the refactoring provider. // If the analyzer is disabled completely, the refactoring is enabled in both directions. fixesError = false; return(userPrefersBlockBodies == forAnalyzer || (!forAnalyzer && analyzerDisabled)); }
private void ProcessMembers( SyntaxTreeAnalysisContext context, CodeStyleOption2 <AccessibilityModifiersRequired> option, SyntaxList <MemberDeclarationSyntax> members) { foreach (var memberDeclaration in members) { ProcessMemberDeclaration(context, option, memberDeclaration); } }
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, CodeStyleOption2 <ExpressionBodyPreference> option) { var declaration = (LambdaExpressionSyntax)context.Node; var diagnostic = AnalyzeSyntax(context.SemanticModel, option, declaration, context.CancellationToken); if (diagnostic != null) { context.ReportDiagnostic(diagnostic); } }
private static string GetExpressionBodyPreferenceEditorConfigString(CodeStyleOption2 <ExpressionBodyPreference> value, CodeStyleOption2 <ExpressionBodyPreference> defaultValue) { var notificationString = CodeStyleHelpers.GetEditorConfigStringNotificationPart(value, defaultValue); return(value.Value switch { ExpressionBodyPreference.Never => $"false{notificationString}", ExpressionBodyPreference.WhenPossible => $"true{notificationString}", ExpressionBodyPreference.WhenOnSingleLine => $"when_on_single_line{notificationString}", _ => throw new NotSupportedException(), });
internal void TestParseUsingDirectivesPlacement(string optionString, AddImportPlacement parsedValue, ReportDiagnostic?severity) { var defaultValue = new CodeStyleOption2 <AddImportPlacement>(AddImportPlacement.InsideNamespace, NotificationOption2.Error); severity ??= ReportDiagnostic.Error; var codeStyleOption = CSharpCodeStyleOptions.ParseUsingDirectivesPlacement(optionString, defaultValue); Assert.NotSame(defaultValue, codeStyleOption); Assert.Equal(parsedValue, codeStyleOption.Value); Assert.Equal(severity, codeStyleOption.Notification.Severity); }
public static Result?AnalyzeInvocation( IInvocationOperation invocation, InfoCache infoCache, AnalyzerOptions analyzerOptionsOpt, CancellationToken cancellationToken ) { // Validate we're on a piece of syntax we expect. While not necessary for analysis, we // want to make sure we're on something the fixer will know how to actually fix. if ( !(invocation.Syntax is InvocationExpressionSyntax invocationSyntax) || invocationSyntax.ArgumentList is null ) { return(null); } CodeStyleOption2 <bool> option = null; if (analyzerOptionsOpt != null) { // Check if we're at least on C# 8, and that the user wants these operators. var syntaxTree = invocationSyntax.SyntaxTree; var parseOptions = (CSharpParseOptions)syntaxTree.Options; if (parseOptions.LanguageVersion < LanguageVersion.CSharp8) { return(null); } option = analyzerOptionsOpt.GetOption( CSharpCodeStyleOptions.PreferRangeOperator, syntaxTree, cancellationToken ); if (!option.Value) { return(null); } } // look for `s.Slice(e1, end - e2)` or `s.Slice(e1)` if (invocation.Instance is null) { return(null); } return(invocation.Arguments.Length switch { 1 => AnalyzeOneArgumentInvocation(invocation, infoCache, invocationSyntax, option), 2 => AnalyzeTwoArgumentInvocation(invocation, infoCache, invocationSyntax, option), _ => null, });
public static bool CanOfferUseTopLevelStatements(CodeStyleOption2 <bool> option, bool forAnalyzer) { var userPrefersTopLevelStatements = option.Value == true; var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; var forRefactoring = !forAnalyzer; // If the user likes top level statements, then we offer to convert to them from the diagnostic analyzer. // If the user prefers Program.Main then we offer to use top-level-statements from the refactoring provider. // If the analyzer is disabled completely, the refactoring is enabled in both directions. var canOffer = userPrefersTopLevelStatements == forAnalyzer || (forRefactoring && analyzerDisabled); return(canOffer); }
private static void ReportDiagnostics( SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, IEnumerable <UsingDirectiveSyntax> usingDirectives, CodeStyleOption2 <AddImportPlacement> option) { foreach (var usingDirective in usingDirectives) { context.ReportDiagnostic(DiagnosticHelper.Create( descriptor, usingDirective.GetLocation(), option.Notification.Severity, additionalLocations: null, properties: null)); } }
public void TestOptionSerialization1() { // Verify that bool-options can migrate to ExpressionBodyPreference-options. var option = new CodeStyleOption2<bool>(false, NotificationOption2.Silent); var serialized = option.ToXElement(); var deserialized = CodeStyleOption2<ExpressionBodyPreference>.FromXElement(serialized); Assert.Equal(ExpressionBodyPreference.Never, deserialized.Value); option = new CodeStyleOption2<bool>(true, NotificationOption2.Silent); serialized = option.ToXElement(); deserialized = CodeStyleOption2<ExpressionBodyPreference>.FromXElement(serialized); Assert.Equal(ExpressionBodyPreference.WhenPossible, deserialized.Value); }
public Result( ResultKind kind, CodeStyleOption2 <bool> option, IInvocationOperation invocationOperation, InvocationExpressionSyntax invocation, IMethodSymbol sliceLikeMethod, MemberInfo memberInfo, IOperation op1, IOperation?op2) { Kind = kind; Option = option; InvocationOperation = invocationOperation; Invocation = invocation; SliceLikeMethod = sliceLikeMethod; MemberInfo = memberInfo; Op1 = op1; Op2 = op2; }
private void ProcessMemberDeclaration( SyntaxTreeAnalysisContext context, CodeStyleOption2 <AccessibilityModifiersRequired> option, MemberDeclarationSyntax member) { if (member is BaseNamespaceDeclarationSyntax namespaceDeclaration) { ProcessMembers(context, option, namespaceDeclaration.Members); } // If we have a class or struct, recurse inwards. if (member.IsKind(SyntaxKind.ClassDeclaration, out TypeDeclarationSyntax? typeDeclaration) || member.IsKind(SyntaxKind.StructDeclaration, out typeDeclaration) || member.IsKind(SyntaxKind.RecordDeclaration, out typeDeclaration) || member.IsKind(SyntaxKind.RecordStructDeclaration, out typeDeclaration)) { ProcessMembers(context, option, typeDeclaration.Members); } #if false // Add this once we have the language version for C# that supports accessibility // modifiers on interface methods. if (option.Value == AccessibilityModifiersRequired.Always && member.IsKind(SyntaxKind.InterfaceDeclaration, out typeDeclaration)) { // Only recurse into an interface if the user wants accessibility modifiers on ProcessTypeDeclaration(context, generator, option, typeDeclaration); } #endif if (!CSharpAddAccessibilityModifiers.Instance.ShouldUpdateAccessibilityModifier(CSharpAccessibilityFacts.Instance, member, option.Value, out var name)) { return; } // Have an issue to flag, either add or remove. Report issue to user. var additionalLocations = ImmutableArray.Create(member.GetLocation()); context.ReportDiagnostic(DiagnosticHelper.Create( Descriptor, name.GetLocation(), option.Notification.Severity, additionalLocations: additionalLocations, properties: null)); }
private Diagnostic?AnalyzeNamespace(CodeStyleOption2 <NamespaceDeclarationPreference> option, FileScopedNamespaceDeclarationSyntax declaration) { if (!ConvertNamespaceAnalysis.CanOfferUseBlockScoped(option, declaration, forAnalyzer: true)) { return(null); } // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. // otherwise, if it's not hidden, just squiggle the name. var severity = option.Notification.Severity; var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) != ReportDiagnostic.Hidden ? declaration.Name.GetLocation() : declaration.SyntaxTree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.SemicolonToken.Span.End)); return(DiagnosticHelper.Create( this.Descriptor, diagnosticLocation, severity, ImmutableArray.Create(declaration.GetLocation()), ImmutableDictionary <string, string?> .Empty)); }
public async Task OptionSet_Serialization_CustomValue() { using var workspace = CreateWorkspace(); var newQualifyFieldAccessValue = new CodeStyleOption2 <bool>(false, NotificationOption2.Error); var newQualifyMethodAccessValue = new CodeStyleOption2 <bool>(true, NotificationOption2.Warning); var newVarWhenTypeIsApparentValue = new CodeStyleOption2 <bool>(false, NotificationOption2.Suggestion); var newPreferIntrinsicPredefinedTypeKeywordInMemberAccessValue = new CodeStyleOption2 <bool>(true, NotificationOption2.Silent); workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options .WithChangedOption(CodeStyleOptions2.QualifyFieldAccess, LanguageNames.CSharp, newQualifyFieldAccessValue) .WithChangedOption(CodeStyleOptions2.QualifyMethodAccess, LanguageNames.VisualBasic, newQualifyMethodAccessValue) .WithChangedOption(CSharpCodeStyleOptions.VarWhenTypeIsApparent, newVarWhenTypeIsApparentValue) .WithChangedOption(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInMemberAccess, LanguageNames.VisualBasic, newPreferIntrinsicPredefinedTypeKeywordInMemberAccessValue))); var validator = new SerializationValidator(workspace.Services); await VerifyOptionSetsAsync(workspace, VerifyOptions).ConfigureAwait(false); void VerifyOptions(OptionSet options) { var actualQualifyFieldAccessValue = options.GetOption(CodeStyleOptions2.QualifyFieldAccess, LanguageNames.CSharp); Assert.Equal(newQualifyFieldAccessValue, actualQualifyFieldAccessValue); var actualQualifyMethodAccessValue = options.GetOption(CodeStyleOptions2.QualifyMethodAccess, LanguageNames.VisualBasic); Assert.Equal(newQualifyMethodAccessValue, actualQualifyMethodAccessValue); var actualVarWhenTypeIsApparentValue = options.GetOption(CSharpCodeStyleOptions.VarWhenTypeIsApparent); Assert.Equal(newVarWhenTypeIsApparentValue, actualVarWhenTypeIsApparentValue); var actualPreferIntrinsicPredefinedTypeKeywordInMemberAccessValue = options.GetOption(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInMemberAccess, LanguageNames.VisualBasic); Assert.Equal(newPreferIntrinsicPredefinedTypeKeywordInMemberAccessValue, actualPreferIntrinsicPredefinedTypeKeywordInMemberAccessValue); } }
private IFieldSymbol CreateField( CodeStyleOption2<AccessibilityModifiersRequired> requireAccessibilityModifiers, IParameterSymbol parameter, ImmutableArray<NamingRule> rules, ImmutableArray<string> parameterNameParts) { foreach (var rule in rules) { if (rule.SymbolSpecification.AppliesTo(SymbolKind.Field, Accessibility.Private)) { var uniqueName = GenerateUniqueName(parameter, parameterNameParts, rule); var accessibilityLevel = Accessibility.Private; if (requireAccessibilityModifiers.Value == AccessibilityModifiersRequired.Never || requireAccessibilityModifiers.Value == AccessibilityModifiersRequired.OmitIfDefault) { var defaultAccessibility = DetermineDefaultFieldAccessibility(parameter.ContainingType); if (defaultAccessibility == Accessibility.Private) { accessibilityLevel = Accessibility.NotApplicable; } } return CodeGenerationSymbolFactory.CreateFieldSymbol(
public async Task CustomizableTagsForUnnecessaryCode() { var workspaceXml = @"<Workspace> <Project Language=""C#"" CommonReferences=""true""> <Document FilePath = ""Test.cs"" > // System is used - rest are unused. using System.Collections; using System; using System.Diagnostics; using System.Collections.Generic; class Program { void Test() { Int32 x = 2; // Int32 can be simplified. x += 1; } } </Document> </Project> </Workspace>"; using var workspace = TestWorkspace.Create(workspaceXml); var options = new Dictionary <OptionKey2, object>(); var language = workspace.Projects.Single().Language; var preferIntrinsicPredefinedTypeOption = new OptionKey2(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInDeclaration, language); var preferIntrinsicPredefinedTypeOptionValue = new CodeStyleOption2 <bool>(value: true, notification: NotificationOption2.Error); options.Add(preferIntrinsicPredefinedTypeOption, preferIntrinsicPredefinedTypeOptionValue); workspace.ApplyOptions(options); var analyzerMap = new Dictionary <string, ImmutableArray <DiagnosticAnalyzer> > { { LanguageNames.CSharp, ImmutableArray.Create <DiagnosticAnalyzer>( new CSharpSimplifyTypeNamesDiagnosticAnalyzer(), new CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer()) } }; var spans = (await _producer.GetDiagnosticsAndErrorSpans(workspace, analyzerMap)).Item2 .OrderBy(s => s.Span.Span.Start).ToImmutableArray(); Assert.Equal(3, spans.Length); var first = spans[0]; var second = spans[1]; var third = spans[2]; Assert.Equal(PredefinedErrorTypeNames.Suggestion, first.Tag.ErrorType); Assert.Equal(CSharpAnalyzersResources.Using_directive_is_unnecessary, first.Tag.ToolTipContent); Assert.Equal(40, first.Span.Start); Assert.Equal(25, first.Span.Length); Assert.Equal(PredefinedErrorTypeNames.Suggestion, second.Tag.ErrorType); Assert.Equal(CSharpAnalyzersResources.Using_directive_is_unnecessary, second.Tag.ToolTipContent); Assert.Equal(82, second.Span.Start); Assert.Equal(60, second.Span.Length); Assert.Equal(PredefinedErrorTypeNames.SyntaxError, third.Tag.ErrorType); Assert.Equal(WorkspacesResources.Name_can_be_simplified, third.Tag.ToolTipContent); Assert.Equal(196, third.Span.Start); Assert.Equal(5, third.Span.Length); }
public TestOptionSet(CodeStyleOption2 <T> value) => _value = value;
protected abstract void ProcessCompilationUnit(SyntaxTreeAnalysisContext context, CodeStyleOption2 <AccessibilityModifiersRequired> option, TCompilationUnitSyntax compilationUnitSyntax);
private protected static async Task AssertCodeCleanupResult(string expected, string code, CodeStyleOption2 <AddImportPlacement> preferredImportPlacement, bool systemUsingsFirst = true, bool separateUsingGroups = false) { using var workspace = TestWorkspace.CreateCSharp(code, composition: EditorTestCompositions.EditorFeaturesWpf); var options = CodeActionOptions.Default; var solution = workspace.CurrentSolution .WithOptions(workspace.Options .WithChangedOption(GenerationOptions.PlaceSystemNamespaceFirst, LanguageNames.CSharp, systemUsingsFirst) .WithChangedOption(GenerationOptions.SeparateImportDirectiveGroups, LanguageNames.CSharp, separateUsingGroups) .WithChangedOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, preferredImportPlacement)) .WithAnalyzerReferences(new[] { new AnalyzerFileReference(typeof(CSharpCompilerDiagnosticAnalyzer).Assembly.Location, TestAnalyzerAssemblyLoader.LoadFromFile), new AnalyzerFileReference(typeof(UseExpressionBodyDiagnosticAnalyzer).Assembly.Location, TestAnalyzerAssemblyLoader.LoadFromFile) }); workspace.TryApplyChanges(solution); // register this workspace to solution crawler so that analyzer service associate itself with given workspace var incrementalAnalyzerProvider = workspace.ExportProvider.GetExportedValue <IDiagnosticAnalyzerService>() as IIncrementalAnalyzerProvider; incrementalAnalyzerProvider.CreateIncrementalAnalyzer(workspace); var hostdoc = workspace.Documents.Single(); var document = workspace.CurrentSolution.GetDocument(hostdoc.Id); var codeCleanupService = document.GetLanguageService <ICodeCleanupService>(); var enabledDiagnostics = codeCleanupService.GetAllDiagnostics(); var newDoc = await codeCleanupService.CleanupAsync( document, enabledDiagnostics, new ProgressTracker(), options, CancellationToken.None); var actual = await newDoc.GetTextAsync(); Assert.Equal(expected, actual.ToString()); }
public async Task CustomizableTagsForUnnecessaryCode() { var workspaceXml = @"<Workspace> <Project Language=""C#"" CommonReferences=""true""> <Document FilePath = ""Test.cs"" > // System is used - rest are unused. using System.Collections; using System; using System.Diagnostics; using System.Collections.Generic; class Program { void Test() { Int32 x = 2; // Int32 can be simplified. x += 1; } } </Document> </Project> </Workspace>"; using var workspace = TestWorkspace.Create(workspaceXml, composition: SquiggleUtilities.CompositionWithSolutionCrawler); var options = new Dictionary <OptionKey2, object>(); var language = workspace.Projects.Single().Language; var preferIntrinsicPredefinedTypeOption = new OptionKey2(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInDeclaration, language); var preferIntrinsicPredefinedTypeOptionValue = new CodeStyleOption2 <bool>(value: true, notification: NotificationOption2.Error); options.Add(preferIntrinsicPredefinedTypeOption, preferIntrinsicPredefinedTypeOptionValue); workspace.ApplyOptions(options); var analyzerMap = new Dictionary <string, ImmutableArray <DiagnosticAnalyzer> > { { LanguageNames.CSharp, ImmutableArray.Create <DiagnosticAnalyzer>( new CSharpSimplifyTypeNamesDiagnosticAnalyzer(), new CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer(), new ReportOnClassWithLink()) } }; var diagnosticsAndSpans = await TestDiagnosticTagProducer <DiagnosticsSquiggleTaggerProvider> .GetDiagnosticsAndErrorSpans(workspace, analyzerMap); var spans = diagnosticsAndSpans.Item1 .Zip(diagnosticsAndSpans.Item2, (diagnostic, span) => (diagnostic, span)) .OrderBy(s => s.span.Span.Span.Start).ToImmutableArray(); Assert.Equal(4, spans.Length); var first = spans[0].span; var second = spans[1].span; var third = spans[2].span; var fourth = spans[3].span; var expectedToolTip = new ContainerElement( ContainerElementStyle.Wrapped, new ClassifiedTextElement( new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0005"), new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), new ClassifiedTextRun(ClassificationTypeNames.Text, CSharpAnalyzersResources.Using_directive_is_unnecessary))); Assert.Equal(PredefinedErrorTypeNames.Suggestion, first.Tag.ErrorType); ToolTipAssert.EqualContent(expectedToolTip, first.Tag.ToolTipContent); Assert.Equal(40, first.Span.Start); Assert.Equal(25, first.Span.Length); expectedToolTip = new ContainerElement( ContainerElementStyle.Wrapped, new ClassifiedTextElement( new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0005"), new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), new ClassifiedTextRun(ClassificationTypeNames.Text, CSharpAnalyzersResources.Using_directive_is_unnecessary))); Assert.Equal(PredefinedErrorTypeNames.Suggestion, second.Tag.ErrorType); ToolTipAssert.EqualContent(expectedToolTip, second.Tag.ToolTipContent); Assert.Equal(82, second.Span.Start); Assert.Equal(60, second.Span.Length); expectedToolTip = new ContainerElement( ContainerElementStyle.Wrapped, new ClassifiedTextElement( new ClassifiedTextRun(ClassificationTypeNames.Text, "id", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(new Uri("https://github.com/dotnet/roslyn", UriKind.Absolute)), "https://github.com/dotnet/roslyn"), new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), new ClassifiedTextRun(ClassificationTypeNames.Text, "messageFormat"))); Assert.Equal(PredefinedErrorTypeNames.Warning, third.Tag.ErrorType); ToolTipAssert.EqualContent(expectedToolTip, third.Tag.ToolTipContent); Assert.Equal(152, third.Span.Start); Assert.Equal(7, third.Span.Length); expectedToolTip = new ContainerElement( ContainerElementStyle.Wrapped, new ClassifiedTextElement( new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0049"), new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), new ClassifiedTextRun(ClassificationTypeNames.Text, WorkspacesResources.Name_can_be_simplified))); Assert.Equal(PredefinedErrorTypeNames.SyntaxError, fourth.Tag.ErrorType); ToolTipAssert.EqualContent(expectedToolTip, fourth.Tag.ToolTipContent); Assert.Equal(196, fourth.Span.Start); Assert.Equal(5, fourth.Span.Length); }
private void ProcessMemberDeclaration( SyntaxTreeAnalysisContext context, CodeStyleOption2 <AccessibilityModifiersRequired> option, MemberDeclarationSyntax member) { if (member.IsKind(SyntaxKind.NamespaceDeclaration, out NamespaceDeclarationSyntax namespaceDeclaration)) { ProcessMembers(context, option, namespaceDeclaration.Members); } // If we have a class or struct, recurse inwards. if (member.IsKind(SyntaxKind.ClassDeclaration, out TypeDeclarationSyntax typeDeclaration) || member.IsKind(SyntaxKind.StructDeclaration, out typeDeclaration)) { ProcessMembers(context, option, typeDeclaration.Members); } #if false // Add this once we have the language version for C# that supports accessibility // modifiers on interface methods. if (option.Value == AccessibilityModifiersRequired.Always && member.IsKind(SyntaxKind.InterfaceDeclaration, out typeDeclaration)) { // Only recurse into an interface if the user wants accessibility modifiers on ProcessTypeDeclaration(context, generator, option, typeDeclaration); } #endif // Have to have a name to report the issue on. var name = member.GetNameToken(); if (name.Kind() == SyntaxKind.None) { return; } // Certain members never have accessibility. Don't bother reporting on them. if (!SyntaxFacts.CanHaveAccessibility(member)) { return; } // This analyzer bases all of its decisions on the accessibility var accessibility = SyntaxFacts.GetAccessibility(member); // Omit will flag any accessibility values that exist and are default // The other options will remove or ignore accessibility var isOmit = option.Value == AccessibilityModifiersRequired.OmitIfDefault; if (isOmit) { if (accessibility == Accessibility.NotApplicable) { return; } var parentKind = member.Parent.Kind(); switch (parentKind) { // Check for default modifiers in namespace and outside of namespace case SyntaxKind.CompilationUnit: case SyntaxKind.NamespaceDeclaration: { // Default is internal if (accessibility != Accessibility.Internal) { return; } } break; case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: { // Inside a type, default is private if (accessibility != Accessibility.Private) { return; } } break; default: return; // Unknown parent kind, don't do anything } } else { // Mode is always, so we have to flag missing modifiers if (accessibility != Accessibility.NotApplicable) { return; } } // Have an issue to flag, either add or remove. Report issue to user. var additionalLocations = ImmutableArray.Create(member.GetLocation()); context.ReportDiagnostic(DiagnosticHelper.Create( Descriptor, name.GetLocation(), option.Notification.Severity, additionalLocations: additionalLocations, properties: null)); }