Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FED-1863 Required props lint #871

Merged
merged 57 commits into from
Jan 25, 2024
Merged

FED-1863 Required props lint #871

merged 57 commits into from
Jan 25, 2024

Conversation

greglittlefield-wf
Copy link
Contributor

@greglittlefield-wf greglittlefield-wf commented Jan 11, 2024

Dependent PR

This PR is dependent on #870


Motivation

To help provide a good developer experience when working with required late props, we want to provide a lint that warns when a component is missing required props.

That way, devs can get static warnings, and not wait until they hit our runtime errors to find out they were missing props.

This lint should be similar to the warnings you get when you are missing required function parameters.

We had a partial implementation of this lint, and we needed to get it production-ready.

Changes

  • Finish MissingRequiredPropDiagnostic implementation, enable it, add tests
    • This supports both late required and @requiredProp required props, but after some back and forth I opted to only lint for late required by default. For a more detailed explanation, see comment in tools/analyzer_plugin/lib/src/plugin.dart
  • Add utilities used by the diagnostic, with unit tests
    • getAllProps to get all props for a props class and its supertypes
    • getAllRequiredProps to collect info on which props are required
    • isRequiredPropValidationDisabled
  • Raise analyzer plugin SDK language constraint to 2.17 to use enhanced enums
  • Other changes not directly related to this diagnostic
    • Work around InconsistentAnalysisExceptions when typing
      • I noticed when manually testing that we'd get a bunch errors in the IDE about InconsistentAnalysisException, unrelated to this PR. I found similar issues documented in Dart SDK tooling, and applied a similar workaround.
    • Improve debug: over_react_metrics output to aid in performance optimizing
      • Star the top 3 slowest diagnostics
      • Show getAllComponentUsages overhead
    • AnalyzerDebugHelper - make log message and location computation lazy by default, so that we're not slowing down performance when debug flags aren't enabled
      • Update existing usages, add locations in missing_cascade_parens.dart

Release Notes

Review

See CONTRIBUTING.md for more details on review types (+1 / QA +1 / +10) and code review process.

Please review:

QA Checklist

  • Tests were updated and provide good coverage of the changeset and other affected code
  • Manual testing was performed if needed
    • Steps from PR author:
      • Manually verify that diagnostic behaves as expected in tools/analyzer_plugin/playground/web/missing_required_props.dart
      • Remove the over_react_annotation_required_prop line in the playground's analysis_options.yaml and verify that annotation-required props don't lint by default
      • Try typing in the playground and verify there are virtually no InconsistentAnalysisExceptions errors compared to the base branch
      • Verify behavior of debug flag comments looks good:
        • // debug: over_react_metrics
          • Verify new behavior mentioned in Changes section looks good
          • Verify the performance of this diagnostic looks good
        • // debug: over_react_required_props - shows information about required props for each builder
    • Anything falling under manual testing criteria outlined in CONTRIBUTING.md

Merge Checklist

While we perform many automated checks before auto-merging, some manual checks are needed:

  • A Frontend Frameworks Design member has reviewed these changes
  • There are no unaddressed comments - this check can be automated if reviewers use the "Request Changes" feature
  • For release PRs - Version metadata in Rosie comment is correct

import 'package:analyzer/dart/element/element.dart';
import 'package:over_react_analyzer_plugin/src/util/util.dart';

// Performance optimization notes:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I spent quite a bit of time optimizing this function, and I feel pretty happy about its performance!

In practice, usage is optimized further via the caching done with _cachedGetAllRequiredProps.

@@ -147,59 +150,71 @@ class _DiagnosticGenerator {
/// by the given [unitResult]. If any of the contributors throws an exception,
/// also create a non-fatal 'plugin.error' notification.
Future<_GeneratorResult<List<AnalysisError>>> generateErrors(ResolvedUnitResult unitResult) async {
return _generateErrors(unitResult, DiagnosticCollectorImpl(shouldComputeFixes: false));
return _generateErrors(unitResult, DiagnosticCollectorImpl(shouldComputeFixes: false),
catchInconsistentAnalysisException: true);
}

/// Creates an 'edit.getFixes' response for the location in the file specified
/// by the given [request]. If any of the contributors throws an exception,
/// also create a non-fatal 'plugin.error' notification.
Future<_GeneratorResult<EditGetFixesResult>> generateFixesResponse(DartFixesRequest request) async {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd recommend using whitespace-agnostic diff for these diffs

@@ -53,62 +54,121 @@ render() {
// </editor-fold>

class MissingRequiredPropDiagnostic extends ComponentUsageDiagnosticContributor {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since this diagnostic always existed but was only partially implemented, was always disabled, and was never fully reviewed, this full file should be reviewed as if it were newly-added, as opposed to reviewing just the diff

@greglittlefield-wf greglittlefield-wf changed the base branch from analyzer_plugin_null_safety to v5_wip January 18, 2024 21:38
Comment on lines +80 to +82
/// Represents a prop that was considered required in its declaration,
/// but is ignored by the consuming class.
ignoredByConsumingClass(isRequired: false, requirednessLevel: 1),
Copy link
Contributor Author

@greglittlefield-wf greglittlefield-wf Jan 19, 2024

Choose a reason for hiding this comment

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

This is currently unused, but will get used as part of updates to this file in FED-2015.

While working on that, I wanted to come back to this PR to do a little bit of refactoring, and also added requirednessLevel and new PropRequiredness enum test cases, so I figured I'd just stub it in now.

Copy link
Contributor

@aaronlademann-wf aaronlademann-wf left a comment

Choose a reason for hiding this comment

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

Couple questions.

Insane test coverage. 🥇

/// Returns whether the debug helper should be enabled, caching results per result
/// so we don't have to run it in in every [computeErrorsForUsage].
///
/// TODO Add shared debug flag computation in DiagnosticContributor/DiagnosticCollector
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be addressed in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, but I would love to circle back to it later on, maybe next time we add a new debug flag

Comment on lines +137 to +139
// DOM and SVG components are common and have a large number of props, none of which are required;
// short-circuit here for performance.
if (const {'DomProps', 'SvgProps'}.contains(propsClassElement.name)) return; // [1]
Copy link
Contributor

Choose a reason for hiding this comment

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

💯

over_react:
errors:
# Opt into this diagnostic that's disabled by default.
over_react_annotation_required_prop: info
Copy link
Contributor

Choose a reason for hiding this comment

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

Is info the default severity of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't really have a default severity since it's disabled by default, and to enable it you also have to specify a severity

final incompleteBuilder = WithLateRequired();
incompleteBuilder();

WithLateRequired()();
Copy link
Contributor

@sydneyjodon-wk sydneyjodon-wk Jan 24, 2024

Choose a reason for hiding this comment

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

I haven't looked at this PR, but I was just messing around in the playground and wondering if it shouldn't be linting when required prop validation is disabled on the usage like this: (or maybe I missed something)

(WithLateRequired()..disableRequiredPropValidation())();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh good catch, I forgot about that. Hmm. Yeah either we could not lint when that call is present, or make them also add an ignore comment when it's present.

I'm leaning toward not linting when it's present? What do you guys think? @sydneyjodon-wk @aaronlademann-wf

Copy link
Contributor

Choose a reason for hiding this comment

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

I also like not linting when it's present

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines +40 to +41
// FIXME(FED-2015) remove once these are properly ignored in generated code
.where((e) => e.errorCode.name.toLowerCase() != 'invalid_use_of_visible_for_overriding_member')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was cherry-picked from this PR here: c859acb to get CI passing after merging the WIP branch, and will get addressed in that PR (FIXME removed in a later commit here 008939c)

@sydneyjodon-wk
Copy link
Contributor

Starting QA on this :loading:

Copy link
Contributor

@sydneyjodon-wk sydneyjodon-wk left a comment

Choose a reason for hiding this comment

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

QA +1 Smoke tested

  • Manually verify that diagnostic behaves as expected in tools/analyzer_plugin/playground/web/missing_required_props.dart
  • Remove the over_react_annotation_required_prop line in the playground's analysis_options.yaml and verify that annotation-required props don't lint by default
  • Try typing in the playground and verify there are virtually no InconsistentAnalysisExceptions errors compared to the base branch
  • Verify behavior of debug flag comments looks good:
    • // debug: over_react_metrics
      • Verify new behavior mentioned in Changes section looks good
      • Verify the performance of this diagnostic looks good
    • // debug: over_react_required_props - shows information about required props for each builder

@greglittlefield-wf greglittlefield-wf merged commit 4555104 into v5_wip Jan 25, 2024
9 checks passed
@greglittlefield-wf greglittlefield-wf deleted the required-props-lint branch January 25, 2024 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants