Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/link-2020-integration' into clea…
Browse files Browse the repository at this point in the history
…n-up-diagnostics
  • Loading branch information
greglittlefield-wf committed Jun 16, 2020
2 parents abd93e4 + 162132e commit 1538341
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';

class BoolPropNameReadabilityDiagnostic extends DiagnosticContributor {
Expand All @@ -18,11 +17,11 @@ class BoolPropNameReadabilityDiagnostic extends DiagnosticContributor {

result.unit.accept(visitor);

final returnClasses = visitor.returnClasses;
final returnMixins = visitor.returnMixins;

for (final propsClass in returnClasses) {
final classFields = propsClass.declaredElement.fields;
for (final field in classFields) {
for (final propsClass in returnMixins) {
final mixinFields = propsClass.declaredElement.fields;
for (final field in mixinFields) {
final propName = field.name;
if (field.type != typeProvider.boolType) continue;
if (propName == null) continue; // just in case
Expand Down Expand Up @@ -70,10 +69,16 @@ bool hasBooleanContain(String propName) {
return propName.toLowerCase().contains(RegExp('(${allowedContainsForBoolProp.join("|")})'));
}

bool isPropsClass(ClassDeclaration c) => c.declaredElement.allSupertypes.any((m) => m.name == 'UiProps');
bool isPropsClass(ClassDeclaration classDecl) {
return classDecl.declaredElement.allSupertypes.any((m) => m.getDisplayString() == 'UiProps');
}

bool isPropsMixin(MixinDeclaration mixinDecl) {
return mixinDecl.onClause.superclassConstraints.any((m) => m.name.name == 'UiProps');
}

class PropsVisitor extends SimpleAstVisitor<void> {
List<ClassDeclaration> returnClasses = [];
List<ClassOrMixinDeclaration> returnMixins = [];
@override
void visitCompilationUnit(CompilationUnit node) {
node.visitChildren(this);
Expand All @@ -82,7 +87,14 @@ class PropsVisitor extends SimpleAstVisitor<void> {
@override
void visitClassDeclaration(ClassDeclaration node) {
if (isPropsClass(node)) {
returnClasses.add(node);
returnMixins.add(node);
}
}

@override
void visitMixinDeclaration(MixinDeclaration node) {
if (isPropsMixin(node)) {
returnMixins.add(node);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
import 'package:over_react_analyzer_plugin/src/fluent_interface_util.dart';

/// A diagnostic that warns when an HTML attribute set on an OverReact `Dom` component builder is invalid
/// based on the `<attribute>: [<allowed_html_elems>]` schema found within [allowedHtmlElementsForAttribute].
class InvalidDomAttributeDiagnostic extends ComponentUsageDiagnosticContributor {
static const code = DiagnosticCode(
'over_react_invalid_dom_attribute',
"{}' isn't a valid HTML attribute prop for '{}'. It may only be used on: {}",
"'{0}' isn't a valid HTML attribute prop for '{1}'. It may only be used on: {2}",
AnalysisErrorSeverity.WARNING,
AnalysisErrorType.STATIC_WARNING,
);
Expand All @@ -29,7 +31,7 @@ class InvalidDomAttributeDiagnostic extends ComponentUsageDiagnosticContributor

if (!allowedElements.contains(nodeName)) {
collector.addError(code, result.locationFor(lhs.propertyName),
errorMessageArgs: [propName, nodeName, allowedElements.join(',')]);
errorMessageArgs: [propName, 'Dom.$nodeName()', allowedElements.map((name) => 'Dom.$name()').join(',')]);
}
});
}
Expand All @@ -50,6 +52,9 @@ String _camelToSpinalCase(String camel) {
.toLowerCase();
}

/// A map keyed with HTML attributes and iterable values of the HTML element names they are allowed on.
///
/// > See: [InvalidDomAttributeDiagnostic]
const allowedHtmlElementsForAttribute = {
'accept': ['form', 'input'],
'accept-charset': ['form'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';

const staticMethodNames = ['getDefaultProps', 'getDerivedStateFromProps'];
const staticMethodNames = ['getDefaultProps', 'defaultProps', 'getDerivedStateFromProps'];
const instanceMemberWhitelist = [
'newProps',
'newState',
Expand Down
62 changes: 62 additions & 0 deletions playground/web/dom_prop_types.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'package:over_react/over_react.dart';

part 'dom_prop_types.over_react.g.dart';

main() {
final content = Fragment()(
(Dom.a()
// This should have a lint.
..size = 1
// These should have no lint
..href = null
..hrefLang = null
..download = null
..rel = null
..target = null
)(),
(Dom.abbr()
// This should have a lint.
..size = 1
// These should have no lint
..title = 'foo'
)(),
(Dom.address()
// This should have a lint.
..size = 1
// These should have no lint
..title = 'foo'
)(),
(Dom.area()
// This should have a lint.
..size = 1
// These should have no lint
..coords = 1
..download = null
..href = null
..hrefLang = null
..rel = null
..shape = null
..target = null
)(),
(Dom.article()
// This should have a lint.
..size = 1
// These should have no lint
..title = 'foo'
)(),
(Dom.aside()
// This should have a lint.
..size = 1
// These should have no lint
..title = 'foo'
)(),
(Dom.audio()
// This should have a lint.
..size = 1
// These should have no lint
..autoPlay = true
..controls = true
..muted = true
)(),
);
}
40 changes: 40 additions & 0 deletions playground/web/pseudo_static_lifecycle.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:over_react/over_react.dart';

part 'dom_prop_types.over_react.g.dart';

UiFactory<HammerTimeProps> HammerTime =
// ignore: undefined_identifier
_$HammerTime;

mixin HammerTimeProps on UiProps {
String somethingThatCanBeTouched;
}

mixin HammerTimeState on UiState {}

class HammerTimeComponent extends UiStatefulComponent2<HammerTimeProps, HammerTimeState> {
final mcHammer = 'cant touch this';

@override
get defaultProps {
return newProps()
..somethingThatCanBeTouched = mcHammer;
}

@override
getDerivedStateFromProps(Map nextProps, Map prevState) {
final tNextProps = typedPropsFactory(nextProps);
final tNextPropsJs = typedPropsFactoryJs(nextProps);
final tPrevState = typedStateFactory(prevState);
final tPrevStateJs = typedStateFactoryJs(prevState);

if (props.somethingThatCanBeTouched == mcHammer) {
return newState();
}

return null;
}

@override
render() {}
}

0 comments on commit 1538341

Please sign in to comment.