Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

add lint voidness_propagation #1206

Closed
wants to merge 3 commits into from

Conversation

a14n
Copy link
Contributor

@a14n a14n commented Oct 10, 2018

@pq
Copy link
Contributor

pq commented Oct 10, 2018

Interesting that we're seeing exceptions in benchmarking:

https://travis-ci.org/dart-lang/linter/jobs/439879861

Thoughts on that?

void visitMethodDeclaration(MethodDeclaration node) {
final inheritedMethod = DartTypeUtilities.lookUpInheritedMethod(node);
if (inheritedMethod != null) {
_check(inheritedMethod.returnType, node.returnType.type, node.returnType);
Copy link
Contributor

Choose a reason for hiding this comment

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

Both here and below, node.returnType can return null when the method declaration has no declared return type.

@bwilkerson
Copy link
Contributor

@MichaelRFairhurst

@pq
Copy link
Contributor

pq commented Oct 15, 2018

ping: @MichaelRFairhurst 👍

import 'package:linter/src/analyzer.dart';
import 'package:linter/src/util/dart_type_utilities.dart';

const _desc = r'Do not loose voidness.';
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: "lose"


const _details = r'''

**DO** not loose voidness. This is likely to lead to a runtime error.
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

registry.addMethodDeclaration(this, visitor);
registry.addMethodInvocation(this, visitor);
registry.addReturnStatement(this, visitor);
registry.addVariableDeclaration(this, visitor);
Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to maybe see about giving this lint better support in the analyzer API.

There are many more places that downcasts can occur -- for instance:

class DefinesPlus {
  operator +(DefinesPlus other) => null;
}

void main() {
  var x = DefinesPlus();
  void y;
  print(x + y); // downcast from void to DefinesPlus
}

The CFE project that lowers Dart to kernel code will include an explicit cast here, and the analyzer can do the same sort of resolution. If we could expose that in the tree, that would make it easier for you to, for instance, find any expressions that are downcasted and then check what the from + to types were. This would get a performance boost (by not having to check every node for downcasts again), make this code simpler (hopefully!), and make it easy to have this lint and others like it be consistently conclusive in coverage.

Are you interested in doing that for this lint or do you want me to keep that in my mind as a theoretical? Note: I have not discussed this with the other analyzer teammembers and they may not agree with me on it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to maybe see about giving this lint better support in the analyzer API.

I'm not sure what kind of support you're thinking about, so it's hard for me to say what I think about it. :-)

I would expect that we already have the "from" and "to" types in the AST. It should just be a matter of testing the relationships between those types. You might be proposing that we encapsulate that test in a requiresImplicitDowncast member so that clients don't have to repeat the test in multiple locations and risk getting it wrong, which seems reasonable.


@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
DartType returnType;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you want to check parameters too to catch this case?

void foo(int x) => print(x);

void main {
  void Function(void) f = foo; // fails at runtime
}

?

final inheritedMethod = DartTypeUtilities.lookUpInheritedMethod(node);
if (inheritedMethod != null) {
_check(
inheritedMethod.returnType, node.returnType?.type, node.returnType);
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we already caught this?

class B {                                                                        
  int f() => 123;                                                                
}                                                                                
                                                                                 
class C extends B{                                                               
  void f() => null;                                                              
} 

gives me "invalid override"

} else if (type is InterfaceType &&
expectedType is InterfaceType &&
type != expectedType &&
type.element == expectedType.element) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, this won't catch Iterable<void> being assigned to List<int>, or List<int> being assigned to Iterable<void>.

There are other pathological cases too:

abstract class IV implements Iterable<void> {}
abstract class LI implements List<int> {}

void bad(IV iv, LI li) {
  li = iv;
}

I guess this is probably the most complex part and the API change I described earlier wouldn't help you solve this...once again, though, this is logic we have implemented in the analyzer. I wonder if we can expose that API to you at all in a way where you can provide custom hooks into the subtype comparison. FWIW the subtype compare code is here: https://github.com/dart-lang/sdk/blob/master/pkg/analyzer/lib/src/generated/type_system.dart#L1480

Copy link
Contributor

Choose a reason for hiding this comment

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

type != expectedType &&
type.element == expectedType.element) {
for (var i = 0; i < type.typeArguments.length; i++) {
if (type.typeArguments[i].isVoid &&
Copy link
Contributor

@MichaelRFairhurst MichaelRFairhurst Oct 15, 2018

Choose a reason for hiding this comment

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

I would call _check on typeArguments[i], and have _check handle where type.isVoid && !expectedType.isVoid

I think the current code misses this:

void Function() x;
int Function() y = x;

@mit-mit
Copy link
Contributor

mit-mit commented Aug 20, 2019

Hi @a14n this appears stale; can we close it?

@a14n a14n closed this Aug 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Development

Successfully merging this pull request may close these issues.

voidness propagation as a lint
6 participants