Skip to content

Add SimplifyObjectCreation analyzer#52077

Merged
CyrusNajmabadi merged 13 commits intodotnet:mainfrom
Youssef1313:simplify-obj-creation-analyzer
Apr 14, 2021
Merged

Add SimplifyObjectCreation analyzer#52077
CyrusNajmabadi merged 13 commits intodotnet:mainfrom
Youssef1313:simplify-obj-creation-analyzer

Conversation

@Youssef1313
Copy link
Copy Markdown
Member

@Youssef1313 Youssef1313 commented Mar 23, 2021

Fixes #45166

@ghost ghost added the Area-IDE label Mar 23, 2021
@Youssef1313 Youssef1313 deleted the simplify-obj-creation-analyzer branch March 23, 2021 12:11
@Youssef1313 Youssef1313 restored the simplify-obj-creation-analyzer branch March 23, 2021 12:11
@Youssef1313 Youssef1313 reopened this Mar 23, 2021
@Youssef1313 Youssef1313 requested a review from a team as a code owner March 23, 2021 12:42
Comment thread src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs
MyBase.New(
diagnosticId:=IDEDiagnosticIds.SimplifyObjectCreationDiagnosticId,
enforceOnBuild:=EnforceOnBuildValues.SimplifyObjectCreation,
[option]:=Nothing,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have an option

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The day where this isn't needed anymore can't get here soon enough

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the option to be useful, it needs to be involved in code generation. Otherwise, setting severity should be enough. Anything I'm missing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, we need an option so that people can configure this from the UI. for example, stating that they want this, and what severity this should be.

Copy link
Copy Markdown
Member Author

@Youssef1313 Youssef1313 Mar 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi Won't @jmarolf's PR (#51069) address this in a more global way (for all analyzers)?

I'll add an option for now, but may jump into discord to get more information.

Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
For Each diagnostic In diagnostics
Dim node = DirectCast(root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie:=True), VariableDeclaratorSyntax)
Dim asNewClause = SyntaxFactory.AsNewClause(node.AsClause.AsKeyword, DirectCast(node.Initializer.Value, NewExpressionSyntax))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the analyzer you check for Object creation. Here you cast to New expression. (I'm not at computer), is this safe?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi Nice catch! I think the cast may fail. Will come up with a test and fix that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast doesn't fail since an ObjectCreationExpressionSyntax derives from NewExpressionSyntax (i.e, it's always safe to cast an ObjectCreationExpressionSyntax to its base type. However, the analyzer is missing cases.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi The syntactic simplification of an expression like Dim result As C() = New C() { } (i.e, ArrayCreationExpression) will be different since Dim result As New C() { } isn't allowed by the language. Let me know if you want me to handle this or keep it simple and only catch ObjectCreationExpressionSyntax.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just keep it simple. make sure we have tests and whatnot.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

Needs an option, and update to tools|options for vb. But it's otherwise ready to go in :-)

@jinujoseph jinujoseph added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Mar 23, 2021
No editorconfig based code style option

# IDE0131
No editorconfig based code style option
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we would want an editorconfig value for this. otherwise people can't disable the hint. or, alternatively, some projects might want to make it an error if you use the old form and not the new form.

Comment thread src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs Outdated

Dim symbolInfo = context.SemanticModel.GetTypeInfo(objectCreation, cancellationToken)
If symbolInfo.Type IsNot Nothing AndAlso symbolInfo.Type.Equals(symbolInfo.ConvertedType, SymbolEqualityComparer.Default) Then
context.ReportDiagnostic(Diagnostic.Create(Descriptor, variableDeclarator.GetLocation()))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this loses the value of hte style option. i.e. the severity.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi Can you elaborate more on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this loses the value of hte style option. i.e. the severity.

Aren't these actively being deprecated? The form used in this PR does account for the standard dotnet_diagnostic.*.severity option.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify how that works? How did the diagnostic know what severity the user has set this particular option to?

Copy link
Copy Markdown
Member Author

@Youssef1313 Youssef1313 Apr 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this happens around the following, not sure enough:

https://github.com/dotnet/roslyn/blob/1cca63b5d8ea170f8d8e88e1574aa3ebe354c23b/src/Compilers/Core/Portable/Diagnostic/DiagnosticDescriptor.cs#L213-L224

// Apply syntax tree options, if applicable.
if (syntaxTreeOptions != null &&
(!isSpecified || specifiedWarnAsErrorMinus))
{
// 3. Editor config options (syntax tree level)
// 4. Global analyzer config options (compilation level)
// Do not apply config options if it is bumping a warning to an error and "/warnaserror-:DiagnosticId" was specified on the command line.
if ((tree != null && syntaxTreeOptions.TryGetDiagnosticValue(tree, id, cancellationToken, out var reportFromSyntaxTreeOptions) ||
syntaxTreeOptions.TryGetGlobalDiagnosticValue(id, cancellationToken, out reportFromSyntaxTreeOptions)) &&
!(specifiedWarnAsErrorMinus && severity == DiagnosticSeverity.Warning && reportFromSyntaxTreeOptions == ReportDiagnostic.Error))
{
isSpecified = true;
report = reportFromSyntaxTreeOptions;
// '/warnaserror' should promote warnings configured in analyzer config to error.
if (!specifiedWarnAsErrorMinus && report == ReportDiagnostic.Warn && generalDiagnosticOption == ReportDiagnostic.Error)
{
report = ReportDiagnostic.Error;
}
}
}

private static Diagnostic? GetFilteredDiagnostic(Diagnostic diagnostic, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, CancellationToken cancellationToken)
{
var filteredDiagnostic = compilation.Options.FilterDiagnostic(diagnostic, cancellationToken);
return applyFurtherFiltering(filteredDiagnostic);
Diagnostic? applyFurtherFiltering(Diagnostic? diagnostic)
{
// Apply bulk configuration from analyzer options for analyzer diagnostics, if applicable.
if (diagnostic?.Location.SourceTree is { } tree &&
analyzerOptions.TryGetSeverityFromBulkConfiguration(tree, compilation, diagnostic.Descriptor, cancellationToken, out ReportDiagnostic severity))
{
diagnostic = diagnostic.WithReportDiagnostic(severity);
}
if (diagnostic != null &&
severityFilter.Contains(DiagnosticDescriptor.MapSeverityToReport(diagnostic.Severity)))
{
return null;
}
return diagnostic;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand why some of these anlayzers are using this form, and some are using DiagnosticHelper.Create. Can you please use DiagnosticHelper.Create to be consistent with the rest of the analyzers in this domain. If that approach is incorrect we cna fix them all up together.

@sharwell @mavasani to advise. Note: this is one of the reasons i really dislike making pattern changes in a piecemeal fashion. I cannot go and look and see what is the right way to do something because we aren't consistent in our own codebase.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mavasani can you verify that all the above is correct? That it's fine for an anlyzer to just create the Diagnostic and not pass the severity. If so, i'm fine with this piece.

Copy link
Copy Markdown
Contributor

@mavasani mavasani Apr 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have 2 core scenarios here to consider, let me elaborate:

  1. Command line builds - This scenario will have no impact on whether we use DiagnosticHelper.Create(..., Notification.Severity, ...) or Diagnostic.Create(...), with the latter ignoring Notification.Severity from the code style option.
  2. IDE background analysis - This scenario will have different user experience based on the chosen approach. Using DiagnosticHelper.Create(..., Notification.Severity, ...) would ensure the option severity set in Tools/Option or via the :severity syntax in editorconfig will be respected. Using Diagnostic.Create(...) would lead to this severity setting being ignored even in the IDE. We normally use Diagnostic.Create(...) for reporting analyzer diagnostics only when the IDE analyzer does not have corresponding code style option where :severity can be specified.

Overall it proves the point that we were just discussing at the design meeting - determining the reported severity of diagnostics should not be determined by the analyzers, but instead better for analyzer driver to do the right thing. I think it is fine to leave it in Dev16 and we can hopefully fix the core issue properly in the compiler/analyzer driver layer in Dev17 so users and analyzer authors have the minimal overhead for getting the most intuitive functionality.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. This code is incorrect then. @Youssef1313 can you revise this to address this issue?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall it proves the point that we were just discussing at the design meeting - determining the reported severity of diagnostics should not be determined by the analyzers, but instead better for analyzer driver to do the right thing.

I'm fine with that. My general point is that that should still be done in a way taht is palatable to the person who owns the editorconfig file. :)

Inherits CustomCodeActions.DocumentChangeAction

Friend Sub New(title As String, createChangedDocument As Func(Of CancellationToken, Task(Of Document)))
MyBase.New(title, createChangedDocument, NameOf(VisualBasicCodeFixesResources.Simplify_object_creation))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass title for equivalence key?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi It was suggested by @sharwell to pass NameOf(resource) as equivalence key. I don't know if it matters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-localized equivalence keys may make it easier to use equivalence keys in the future for configuring/disabling specific code actions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that won't seem to work anyways as we use the title in tons of code actions today. i woudl rather us just be consistent everywhere. if and when we have a need to make changes for future work, we can then change everything togehter. Being inconsistent here is worse, to me, because it means no one can know for certain what is going on and any particular PR has to deal with trying to rationalize what pattern should be used where.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi The situation is already inconsistent unfortunately. There exist usage of both patterns already 😕
So I think we should go to whatever helps in the future. We can also create an analyzer that makes sure equivalence keys are compile-time constants.

Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.SimplifyObjectCreation
Public Class SimplifyObjectCreationTests
<Fact>
Public Async Function SimplifyObjectCreation() As Task
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a fix-all test?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi See SimplifyObjectCreation_MultipleDeclarators.

Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm if feedback is addressed. Thanks!

@Youssef1313
Copy link
Copy Markdown
Member Author

@CyrusNajmabadi Anything left here?

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

lgtm, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-IDE Community The pull request was submitted by a contributor who is not a Microsoft employee. Feature Request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Analyzer Suggestion] Remove repetetive type in VB

6 participants