Skip to content

Canonicalization overhaul #1368

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

Merged
merged 42 commits into from
Apr 13, 2017
Merged

Conversation

jcollins-g
Copy link
Contributor

The giant canonicalization overhaul is shaped up well enough I'd prefer to try to submit a PR before improving it further. There are definitely still some known issues, but I'm now reasonably satisfied it introduces no new problems.

This is big. Some tips on reviewing: Changes to ModelElement and the new mixin class, Inheritable, in lib/src/model.dart are the most important, especially any method with the word "canonical" in its name. If you understand the changes there, most of the rest of what you see can be understood as downstream consequences. I've tried to apply comments and inline documentation liberally, please feel free to ask for more where it isn't clear.

…e-deps had us throwing half the SDK docs into there.
/// clean that up.
factory ModelElement.from(Element e, Library library,
{Class enclosingClass}) {
if (library != null &&
Copy link
Contributor Author

Choose a reason for hiding this comment

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

get rid of this breakpoint

else
newModelElement = new Operator.inherited(e, enclosingClass, library);
}
if (library != null &&
Copy link
Contributor Author

Choose a reason for hiding this comment

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

delete breakpoint

@devoncarew
Copy link
Member

Taking a first pass through today -

Copy link
Member

@devoncarew devoncarew left a comment

Choose a reason for hiding this comment

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

One pass through:

  • the canonicalization work is excellent; this will be a night and day difference for exports from a correctness POV
  • the single factory ctor of model elements will really help the code quality, instead of having construction logic spread throughout the codebase
  • we'll need to examine the use of asserts going forward, and make sure that there aren't any asserts that would be better handled as user facing errors

Given the scope of the fixes, this would make a good line in the sand for a 1.0 version. Perhaps we should do some light dartlang.org / css upgrades to give the generated html a slight facelift at the same time?

@@ -41,7 +41,7 @@ main(List<String> arguments) async {

Directory sdkDir = getSdkDir();
if (sdkDir == null) {
print("Error: unable to locate the Dart SDK.");
stderr.write(" Error: unable to locate the Dart SDK.");
Copy link
Member

Choose a reason for hiding this comment

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

+1 to using stderr here

Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

_writeFile(filename, content);
// If you see this assert, we're probably being called to build non-canonical
// docs somehow. Check data.self.isCanonical and callers for bugs.
assert(!_writtenFiles.contains(fullName));
Copy link
Member

Choose a reason for hiding this comment

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

Given these new asserts, we should make sure that whenever we run dartdoc during development (or on our CI) we run in checked mode. I believe there are some cases where the grind script is not run in checked mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Grind script now always runs dartdoc in checked mode.

break;
}
}
if (definingEnclosingElement.isCanonical)
Copy link
Member

@devoncarew devoncarew Apr 11, 2017

Choose a reason for hiding this comment

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

nit: we should have braces around single line if statements

Map<String, ExecutableElement> imap =
manager.getMembersInheritedFromInterfaces(element);
library.inheritanceManager.getMembersInheritedFromInterfaces(element);

_inheritedProperties = [];
List<ExecutableElement> vs = [];
Copy link
Member

@devoncarew devoncarew Apr 11, 2017

Choose a reason for hiding this comment

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

I believe vs here stands for values. If so, perhaps we should rename the variable for understandability?

@@ -610,13 +683,15 @@ class Class extends ModelElement implements EnclosedElement {
return _operators;
}

List<Operator> get operatorsForPages => _genPageOperators;
List<Operator> get operatorsForPages =>
_genPageOperators.toList(growable: false);
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious, is there a reason why you're setting growable to false?

@@ -19,9 +19,9 @@ dependencies:
mustache4dart: ^1.0.9
package_config: '>=0.1.5 <2.0.0'
path: ^1.3.0
quiver: '>=0.18.0 <0.25.0'
resource: '>=1.1.0 <3.0.0'
resource: ^1.1.0
Copy link
Member

Choose a reason for hiding this comment

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

resource is on 2.1.2; are we compatible with that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure. I just didn't see the reason for the ceiling so removed it (if we can cross one major version, likely we can cross others).

I can bump to ^2.1.0, add the ceiling, or both. What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

I'd just check that the sdk deps is bringing in a 2.0 version, and then bump our use up to ^2.0.0.

expect(ExtendingClass.supertype.name, equals('BaseClass'));
expect(
ExtendingClass.supertype.element.library.name, equals('two_exports'));
expect(ExtendingClass.superChain[0].name, equals('BaseClass'));
Copy link
Member

Choose a reason for hiding this comment

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

I tend to use foo.first instead of foo[0] in many cases - it's a bit more semantic; YMMV.

tool/grind.dart Outdated
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async' show Future;
import 'dart:async' show Future, Stream;
Copy link
Member

Choose a reason for hiding this comment

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

you could also just drop the show clause here...

tool/grind.dart Outdated
@@ -259,6 +265,34 @@ int _findCount(String str, String match) {
return count;
}

Stream<FileSystemEntity> dirContents(String dir) {
var lister = new Directory(dir).list(recursive: true);
Copy link
Member

Choose a reason for hiding this comment

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

return immediately instead of creating a local var?

tool/grind.dart Outdated
return lister;
}

_doFileCheck(String origin, Set<String> visited, bool error) {
Copy link
Member

Choose a reason for hiding this comment

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

void?

@jcollins-g
Copy link
Contributor Author

Tagging more issues this solves:

partial fix for: #1239, #1197
fixes: #589, #1090

@jcollins-g
Copy link
Contributor Author

On general comments:

Re: canonicalization, thanks. Working with the code like this makes more sense to me than it did before this, and hopefully other developers will have the same experience.

Single factory constructor for ModelElements is functionally correct but I am thinking of restructuring it so that it's possible to construct elements of a known type without harmful consequences. I couldn't think of a way to do this quickly that was actually clearer (or small) so put that off for now. This is definitely better than before; in general I want less constructory logic outside of the classes we're instantiating and there's more to do there with Package in particular.

Yes on asserts; for a while I didn't even realize Dart asserts weren't valid outside of checked mode because IntelliJ was helpfully defaulting to having that on. Eventually I want all asserts to be errors, possibly fatal ones.

@jcollins-g
Copy link
Contributor Author

As for 1.0, there are a few things I'd like to nail down before that for correctness. Created dartdoc 1.0 to track it.

Copy link
Collaborator

@keertip keertip left a comment

Choose a reason for hiding this comment

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

Some initial comments, review in progress. Like the direction of the PR, great to see dartdoc get some tlc.

@@ -41,7 +41,7 @@ main(List<String> arguments) async {

Directory sdkDir = getSdkDir();
if (sdkDir == null) {
print("Error: unable to locate the Dart SDK.");
stderr.write(" Error: unable to locate the Dart SDK.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

package.allModelElements.forEach((m) => m.documentation);
// TODO(jcollins-g): move index building into a cached-on-demand generation
// like most other bits in [Package].
package.allCanonicalModelElements.forEach((m) => m.documentation);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Eliminates repeats? Were we looking at documentation for elements that were not documented?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right.

lib/dartdoc.dart Outdated
@@ -279,13 +286,40 @@ class DartDoc {
// Use the includeExternals.
for (Source source in context.librarySources) {
LibraryElement library = context.computeLibraryElement(source);
if (library == null) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are you seeing use cases when the analyzer returns null for LibraryElement . Pretty sure it should not do so. Could you check with @bwilkerson on 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.

Poked at this and I'm pretty sure this was an intermediate debugging step that got forgotten. Ran through my cases without it and saw no issues.

@@ -95,54 +97,81 @@ class HtmlGeneratorInstance implements HtmlOptions {
generateLibrary(package, lib);

for (var clazz in lib.allClasses) {
// TODO(jcollins-g): consider refactor so that only the canonical
// ModelElements show up in these lists
if (!clazz.isCanonical) continue;
Copy link
Collaborator

Choose a reason for hiding this comment

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

So essentially there is a always one library that a class is canonical to (not sure if I got the lingo right here) and that's the only place it should be documented? If a class is not canonical, don't document it here, as it will be documented in the canonical library.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct.

@jcollins-g
Copy link
Contributor Author

I seem to be unable to reply to comments once they are "out of date" with github's interface. But, @devoncarew, I believe all your comments are addressed. resource is 1.1.0 in the bleeding edge SDK.

Working on @keertip's now.

@devoncarew
Copy link
Member

I seem to be unable to reply to comments once they are "out of date" with github's interface.

You should be able to; perhaps it's not easy to reply to the review header?

I believe all your comments are addressed. resource is 1.1.0 in the bleeding edge SDK.

lgtm for my part! It'll be great to have these fixes.

Copy link
Collaborator

@keertip keertip left a comment

Choose a reason for hiding this comment

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

The changes look good. Like the canonicalization changes, the logic makes sense, especially with the flutter use case. Looked through the changes in model.dart, nothing really stood out as a no no. The ones that I had some doubts about already had a todo, or else there was documentation!

Dartdoc needed this overhaul, as it has been in maintenance mode for a very long time and has organically grown to add support for the more complex use cases. Good to see this happen. Hoping to see a 1.0 release soon!

@@ -143,7 +195,7 @@ class Accessor extends ModelElement
? t.getGetter(element.name)
: t.getSetter(element.name);
if (accessor != null) {
return new Accessor(accessor, library);
return new ModelElement.from(accessor, library);
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 Great to have just one way of creating ModelElement

}

var classContent = isDeprecated ? 'class="deprecated" ' : '';
return '<a ${classContent}href="${href}">$name</a>';
}

// TODO(keertip): consolidate all the find library methods
// This differs from package.findOrCreateLibraryFor in a small way,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is the TODO still relevant? Also maybe change the names of the methods so that they don't sound so similar.

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 think the TODO is still relevant; the way these functions are reusing parts of each other's functionality suggests that the lines are wrong between them, even if it isn't obvious yet exactly how to draw them.

Renamed this method and tweaked comments in a couple related places to improve clarity.

/// clean that up.
factory ModelElement.from(Element e, Library library,
{Class enclosingClass}) {
Tuple3<Element, Library, Class> key =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, this is what was needed. Knew that dartdoc was likely creating ModelElements for the same element multiple times, but this was just a todo, until now!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep. It turns out you just can't build things like canonicalization or most of the other things we'd like to do (like machine-readable public API descriptions) without this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Google CLA check succeeded.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants