diff --git a/.idea/modules.xml b/.idea/modules.xml index 7f80bff4ff..d9306e2cb1 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7ff7bba1..0e6f305916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,62 @@ -## unreleased - -* added a new `--footer-text` command-line option, to allow adding additional - text in the package name and copyright section of the footer -* Reduced stack depth by not recomputing findCanonicalLibraryFor (#1381) +## 0.11.0 + +* Many cleanups to dartdoc stdout/stderr, error messages, and warnings: + * Display fatal errors with 'fatal error' string to distinguish them from ordinary errors + * Upgrades to new Package.warn system. + * Fully integrated all scattered "warnings" (#1369) and added new ones for the link checker. + * Allow for setting which warnings are errors in the library. + * Change location output to something IntelliJ can understand and link to + * Display location output for all warnings including line number plus column, when available + from analyzer (still some bugs in our resolution). It still doesn't do code references quite + right but at least gets you to the neighborhood. + * Add a warn method to ModelElements so they can warn on themselves without help from the + Package. + * Warn correctly and squelch duplicates across doc inheritance and canonicalization almost + everywhere. + * Change --show-warnings to show all warnings, even those that might not be useful yet. + * Display a count of all warnings/errors after document generation. + * Make the progress counter tick slower. +* Added a built-in link checker and orphaned file checker, and tied it into Package.warn so + that when debugging dartdoc we can breakpoint and discover what about that ModelElement + caused us to create the broken link. (#1380) +* Fix bug where canonicalEnclosingElement could return a non-canonical Class. +* Fix bug where findCanonicalModelElementFor could return a non-canonical Class. +* Fix overriddenElement for Accessors to generate using enclosingCombo hint to ModelElement factory. +* Fix fullyQualifiedNameWithoutLibrary when periods are part of the library name. +* Add an allModelElements for Classes to support comment references. +* Make allModelElements for Libraries work using Class.allModelElements recursively. +* Squish some bugs related to duplicate logic for instantiating inherited class members. + * Enum and a few other places could still generate duplicate ModelElements for the + same thing. This is now fixed. + * EnumField is now handled by ModelElement.from factory, fixing #1239. + * Added hints for EnumField and Accessors (index, enclosingCombo) to offload the buggy + logic for figuring this out from callers to ModelElement.from. +* Fix broken link generation when a canonical class's defining library isn't canonical. +* Partial rewrite of GetterSetterCombo and Fields/TopLevelVariable handling + * Link correctly to generic types for Fields/TopLevelVariables. + * Use right, left, and bidirectional arrows for read-only, write-only, and read-write + parameters. +* Partial rewrite of comment reference system (#1391, #1285 partial) + * Handle gracefully a variety of things users try in the real world, like prefixing operators + with 'operator', embedded newlines in comment references, and cases that shouldn't be + considered at all (comment refs that are really array references in sample docs, etc). + * Handle canonicalization correctly for comment references: point to the right places and + only to canonical elements. + * In general, warnings related to comment references should be much more useful now. (#1343) + * Many fewer ambiguous doc reference warnings now and the ones that exist should be more + easily understandable and fixable with the new warning message. + * Understand references to parameters even though we don't do anything useful with them just yet + * Generics outside square brackets (#1250) are now warned with better context information that + takes newlines into account, but there are so many of them in complex packages like Flutter + that we still only show those with --show-warnings. + * Cache the traversal of allModelElements. + * Change handling of enum constant linking in codeRefs to work properly, though warnings about + that aren't right in some edge cases still. + * Only use analyzer resolving of commentRefs as a last resort since they don't take dartdoc + canonicalization into account. +* Added a new `--footer-text` command-line option, to allow adding additional + text in the package name and copyright section of the footer. +* Reduced stack depth by not recomputing findCanonicalLibraryFor. (#1381) * Workaround for (#1367) forces on enableAssertInitializer. * Work around analyzer-0.29 bug where embedded SDK uri's aren't properly reversed. diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart index 290deb6d18..c368664f43 100644 --- a/bin/dartdoc.dart +++ b/bin/dartdoc.dart @@ -55,15 +55,15 @@ main(List arguments) async { var readme = args['sdk-readme']; if (readme != null && !(new File(readme).existsSync())) { - stderr - .write(" Error: unable to locate the SDK description file at $readme."); + stderr.write( + " fatal error: unable to locate the SDK description file at $readme."); exit(1); } Directory inputDir = new Directory(args['input']); if (!inputDir.existsSync()) { stderr.write( - " Error: unable to locate the input directory at ${inputDir.path}."); + " fatal error: unable to locate the input directory at ${inputDir.path}."); exit(1); } @@ -77,7 +77,8 @@ main(List arguments) async { args['header'].map(_resolveTildePath).toList() as List; for (String headerFilePath in headerFilePaths) { if (!new File(headerFilePath).existsSync()) { - stderr.write(" Error: unable to locate header file: ${headerFilePath}."); + stderr.write( + " fatal error: unable to locate header file: ${headerFilePath}."); exit(1); } } @@ -86,7 +87,8 @@ main(List arguments) async { args['footer'].map(_resolveTildePath).toList() as List; for (String footerFilePath in footerFilePaths) { if (!new File(footerFilePath).existsSync()) { - stderr.write(" Error: unable to locate footer file: ${footerFilePath}."); + stderr.write( + " fatal error: unable to locate footer file: ${footerFilePath}."); exit(1); } } @@ -96,7 +98,7 @@ main(List arguments) async { for (String footerFilePath in footerTextFilePaths) { if (!new File(footerFilePath).existsSync()) { stderr.write( - " Error: unable to locate footer-text file: ${footerFilePath}."); + " fatal error: unable to locate footer-text file: ${footerFilePath}."); exit(1); } } @@ -110,7 +112,7 @@ main(List arguments) async { if (args.rest.isNotEmpty) { var unknownArgs = args.rest.join(' '); stderr.write( - 'Error: detected unknown command-line argument(s): $unknownArgs'); + ' fatal error: detected unknown command-line argument(s): $unknownArgs'); _printUsageAndExit(parser, exitCode: 1); } @@ -120,7 +122,7 @@ main(List arguments) async { if (!packageMeta.isValid) { stderr.writeln( - 'Unable to generate documentation: ${packageMeta.getInvalidReasons().first}.'); + ' fatal error: Unable to generate documentation: ${packageMeta.getInvalidReasons().first}.'); exit(1); } @@ -165,10 +167,11 @@ main(List arguments) async { autoIncludeDependencies: args['auto-include-dependencies'], categoryOrder: args['category-order']); - var dartdoc = new DartDoc(inputDir, excludeLibraries, sdkDir, generators, + DartDoc dartdoc = new DartDoc(inputDir, excludeLibraries, sdkDir, generators, outputDir, packageMeta, includeLibraries, includeExternals: includeExternals); + dartdoc.onCheckProgress.listen(_onProgress); Chain.capture(() async { DartDocResults results = await dartdoc.generateDocs(); print('\nSuccess! Docs generated into ${results.outDir.absolute.path}'); @@ -218,7 +221,8 @@ ArgParser _createArgsParser() { parser.addOption('footer-text', allowMultiple: true, splitCommas: true, - help: 'paths to footer-text files (optional text next to the copyright).'); + help: + 'paths to footer-text files (optional text next to the copyright).'); parser.addOption('exclude', allowMultiple: true, splitCommas: true, help: 'Library names to ignore.'); parser.addOption('include', @@ -264,8 +268,12 @@ ArgParser _createArgsParser() { return parser; } -void _onProgress(File file) { - if (_showProgress) stdout.write('.'); +int _progressCounter = 0; +void _onProgress(var file) { + if (_showProgress && _progressCounter % 5 == 0) { + stdout.write('.'); + } + _progressCounter += 1; } /// Print help if we are passed the help option. diff --git a/dartdoc.iml b/dartdoc.iml deleted file mode 100644 index d56c2bad77..0000000000 --- a/dartdoc.iml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 1fba463f4a..ec88c8f781 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -8,7 +8,7 @@ library dartdoc; import 'dart:async'; import 'dart:io'; -import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/element.dart' show LibraryElement; import 'package:analyzer/error/error.dart'; import 'package:analyzer/file_system/file_system.dart' as fileSystem; import 'package:analyzer/file_system/physical_file_system.dart'; @@ -21,9 +21,12 @@ import 'package:analyzer/src/generated/java_io.dart'; import 'package:analyzer/src/generated/sdk.dart'; import 'package:analyzer/src/generated/source.dart'; import 'package:analyzer/src/generated/source_io.dart'; +import 'package:html/dom.dart' show Element, Document; +import 'package:html/parser.dart' show parse; import 'package:package_config/discovery.dart' as package_config; import 'package:path/path.dart' as path; +import 'package:tuple/tuple.dart'; import 'src/config.dart'; import 'src/generator.dart'; import 'src/html/html_generator.dart'; @@ -41,7 +44,7 @@ export 'src/sdk.dart'; const String name = 'dartdoc'; // Update when pubspec version changes. -const String version = '0.10.0'; +const String version = '0.11.0'; final String defaultOutDir = path.join('doc', 'api'); @@ -99,12 +102,20 @@ class DartDoc { final List includes; final List includeExternals; final List excludes; + final Set writtenFiles = new Set(); + + // Fires when the self checks make progress. + StreamController _onCheckProgress; Stopwatch _stopwatch; DartDoc(this.rootDir, this.excludes, this.sdkDir, this.generators, this.outputDir, this.packageMeta, this.includes, - {this.includeExternals: const []}); + {this.includeExternals: const []}) { + _onCheckProgress = new StreamController(sync: true); + } + + Stream get onCheckProgress => _onCheckProgress.stream; /// Generate DartDoc documentation. /// @@ -140,9 +151,17 @@ class DartDoc { }); } + PackageWarningOptions warningOptions = new PackageWarningOptions(); + // TODO(jcollins-g): explode this into detailed command line options. + if (config != null && config.showWarnings) { + for (PackageWarning kind in PackageWarning.values) { + warningOptions.warn(kind); + } + } Package package; if (config != null && config.autoIncludeDependencies) { - package = Package.withAutoIncludedDependencies(libraries, packageMeta); + package = Package.withAutoIncludedDependencies( + libraries, packageMeta, warningOptions); libraries = package.libraries.map((l) => l.element).toList(); // remove excluded libraries again, in case they are picked up through // dependencies. @@ -152,10 +171,10 @@ class DartDoc { }); }); } - package = new Package(libraries, packageMeta); + package = new Package(libraries, packageMeta, warningOptions); print( - 'generating docs for libraries ${package.libraries.map((Library l) => l.name).join(', ')}\n'); + '\ngenerating docs for libraries ${package.libraries.map((Library l) => l.name).join(', ')}\n'); // Go through docs of every model element in package to prebuild the macros index // TODO(jcollins-g): move index building into a cached-on-demand generation @@ -167,21 +186,176 @@ class DartDoc { for (var generator in generators) { await generator.generate(package, outputDir); + writtenFiles.addAll(generator.writtenFiles.map(path.normalize)); } + verifyLinks(package, outputDir.path); + double seconds = _stopwatch.elapsedMilliseconds / 1000.0; print( "\nDocumented ${package.libraries.length} librar${package.libraries.length == 1 ? 'y' : 'ies'} " "in ${seconds.toStringAsFixed(1)} seconds."); + print( + "Finished with: ${package.packageWarningCounter.warningCount} warnings, ${package.packageWarningCounter.errorCount} errors"); if (package.libraries.isEmpty) { - stderr.write( - "\ndartdoc could not find any libraries to document. Run `pub get` and try again."); + throw new DartDocFailure( + "dartdoc could not find any libraries to document. Run `pub get` and try again."); + } + + if (package.packageWarningCounter.errorCount > 0) { + throw new DartDocFailure("dartdoc encountered errors while processing"); } return new DartDocResults(packageMeta, package, outputDir); } + void _warn(Package package, PackageWarning kind, String p, String origin, + {String source}) { + // Ordinarily this would go in [Package.warn], but we don't actually know what + // ModelElement to warn on yet. + Locatable referenceElement; + Set referenceElements; + + // Make all paths relative to origin. + if (path.isWithin(origin, p)) { + p = path.relative(p, from: origin); + } + if (source != null) { + if (path.isWithin(origin, source)) { + source = path.relative(source, from: origin); + } + // Source paths are always relative. + referenceElements = package.allHrefs[source]; + } else { + referenceElements = package.allHrefs[p]; + } + if (referenceElements != null) { + if (referenceElements.any((e) => e.isCanonical)) { + referenceElement = referenceElements.firstWhere((e) => e.isCanonical); + } else { + // If we don't have a canonical element, just pick one. + referenceElement = + referenceElements.isEmpty ? null : referenceElements.first; + } + } + if (referenceElement == null && source == 'index.html') + referenceElement = package; + package.warn(referenceElement, kind, p); + } + + void _doOrphanCheck(Package package, String origin, Set visited) { + String normalOrigin = path.normalize(origin); + String staticAssets = path.joinAll([normalOrigin, 'static-assets', '']); + String indexJson = path.joinAll([normalOrigin, 'index.json']); + bool foundIndex = false; + for (FileSystemEntity f + in new Directory(normalOrigin).listSync(recursive: true)) { + var fullPath = path.normalize(f.path); + if (f is Directory) { + continue; + } + if (fullPath.startsWith(staticAssets)) { + continue; + } + if (fullPath == indexJson) { + foundIndex = true; + _onCheckProgress.add(fullPath); + continue; + } + if (visited.contains(fullPath)) continue; + if (!writtenFiles.contains(fullPath)) { + // This isn't a file we wrote (this time); don't claim we did. + _warn(package, PackageWarning.unknownFile, fullPath, normalOrigin); + } else { + _warn(package, PackageWarning.orphanedFile, fullPath, normalOrigin); + } + _onCheckProgress.add(fullPath); + } + + if (!foundIndex) { + _warn(package, PackageWarning.brokenLink, indexJson, normalOrigin); + _onCheckProgress.add(indexJson); + } + } + + // This is extracted to save memory during the check; be careful not to hang + // on to anything referencing the full file and doc tree. + Tuple2, String> _getStringLinksAndHref(String fullPath) { + File file = new File("$fullPath"); + if (!file.existsSync()) { + return null; + } + Document doc = parse(file.readAsStringSync()); + Element base = doc.querySelector('base'); + String baseHref; + if (base != null) { + baseHref = base.attributes['href']; + } + List links = doc.querySelectorAll('a'); + List stringLinks = links + .map((link) => link.attributes['href']) + .where((href) => href != null) + .toList(); + return new Tuple2(stringLinks, baseHref); + } + + void _doCheck( + Package package, String origin, Set visited, String pathToCheck, + [String source, String fullPath]) { + if (fullPath == null) { + fullPath = path.joinAll([origin, pathToCheck]); + fullPath = path.normalize(fullPath); + } + + Tuple2 stringLinksAndHref = _getStringLinksAndHref(fullPath); + if (stringLinksAndHref == null) { + _warn(package, PackageWarning.brokenLink, pathToCheck, + path.normalize(origin), + source: source); + _onCheckProgress.add(pathToCheck); + return null; + } + Iterable stringLinks = stringLinksAndHref.item1; + String baseHref = stringLinksAndHref.item2; + + for (String href in stringLinks) { + if (!href.startsWith('http') && !href.contains('#')) { + var full; + if (baseHref != null) { + full = '${path.dirname(pathToCheck)}/$baseHref/$href'; + } else { + full = '${path.dirname(pathToCheck)}/$href'; + } + var newPathToCheck = path.normalize(full); + String newFullPath = path.joinAll([origin, newPathToCheck]); + newFullPath = path.normalize(newFullPath); + if (!visited.contains(newFullPath)) { + visited.add(newFullPath); + _doCheck(package, origin, visited, newPathToCheck, pathToCheck, + newFullPath); + } + } + } + _onCheckProgress.add(pathToCheck); + } + + Map> _hrefs; + + /// Don't call this method more than once, and only after you've + /// generated all docs for the Package. + void verifyLinks(Package package, String origin) { + assert(_hrefs == null); + _hrefs = package.allHrefs; + + final Set visited = new Set(); + final String start = 'index.html'; + visited.add(start); + stdout.write('\nvalidating docs'); + _doCheck(package, origin, visited, start); + _doOrphanCheck(package, origin, visited); + } + List _parseLibraries( List files, List includeExternals) { Set libraries = new Set(); @@ -220,6 +394,7 @@ class DartDoc { SourceFactory sourceFactory = new SourceFactory(resolvers); + // TODO(jcollins-g): fix this so it actually obeys analyzer options files. var options = new AnalysisOptionsImpl(); options.enableGenericMethods = true; options.enableAssertInitializer = true; diff --git a/lib/src/generator.dart b/lib/src/generator.dart index afa5a56d97..1c28c0145c 100644 --- a/lib/src/generator.dart +++ b/lib/src/generator.dart @@ -21,4 +21,7 @@ abstract class Generator { /// Fires when a file is created. Stream get onFileCreated; + + /// Fetches all filenames written by this generator. + Set get writtenFiles; } diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart index 37e5118818..f1bb79a862 100644 --- a/lib/src/html/html_generator.dart +++ b/lib/src/html/html_generator.dart @@ -35,6 +35,7 @@ typedef String Renderer(String input); class HtmlGenerator extends Generator { final Templates _templates; final HtmlGeneratorOptions _options; + HtmlGeneratorInstance _instance; final StreamController _onFileCreated = new StreamController(sync: true); @@ -42,6 +43,9 @@ class HtmlGenerator extends Generator { @override Stream get onFileCreated => _onFileCreated.stream; + @override + Set get writtenFiles => _instance.writtenFiles; + /// [url] - optional URL for where the docs will be hosted. static Future create( {HtmlGeneratorOptions options, @@ -60,10 +64,14 @@ class HtmlGenerator extends Generator { HtmlGenerator._(this._options, this._templates); @override + + /// Actually write out the documentation for [package]. + /// Stores the HtmlGeneratorInstance so we can access it in [writtenFiles]. Future generate(Package package, Directory out) { - return new HtmlGeneratorInstance( - _options, _templates, package, out, _onFileCreated) - .generate(); + assert(_instance == null); + _instance = new HtmlGeneratorInstance( + _options, _templates, package, out, _onFileCreated); + return _instance.generate(); } } diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart index a7bdfd4851..436e6a234a 100644 --- a/lib/src/html/html_generator_instance.dart +++ b/lib/src/html/html_generator_instance.dart @@ -178,7 +178,7 @@ class HtmlGeneratorInstance implements HtmlOptions { } void generatePackage() { - stdout.write('documenting ${package.name}'); + stdout.write('\ndocumenting ${package.name}'); TemplateData data = new PackageTemplateData(this, package, useCategories); @@ -188,7 +188,6 @@ class HtmlGeneratorInstance implements HtmlOptions { void generateLibrary(Package package, Library lib) { stdout .write('\ngenerating docs for library ${lib.name} from ${lib.path}...'); - if (!lib.isAnonymous && !lib.hasDocumentation) { package.warn(lib, PackageWarning.noLibraryLevelDocs); } @@ -295,6 +294,8 @@ class HtmlGeneratorInstance implements HtmlOptions { } } + Set get writtenFiles => _writtenFiles; + void _build(String filename, TemplateRenderer template, TemplateData data) { String fullName = path.join(out.path, filename); diff --git a/lib/src/line_number_cache.dart b/lib/src/line_number_cache.dart index 20b3b851d1..a2cdc49c92 100644 --- a/lib/src/line_number_cache.dart +++ b/lib/src/line_number_cache.dart @@ -7,6 +7,8 @@ library dartdoc.cache; import 'dart:collection'; import 'dart:io'; +import 'package:tuple/tuple.dart'; + String _getNewlineChar(String contents) { if (contents.contains("\r\n")) { return "\r\n"; @@ -51,6 +53,20 @@ class LineNumberCache { } } + Tuple2 lineAndColumn(String file, int offset) { + if (offset == 0) { + return new Tuple2(0, 0); + } else { + var lineMap = _lineNumbers.putIfAbsent( + file, () => _createLineNumbersMap(_fileContents(file))); + var lastKey = lineMap.lastKeyBefore(offset); + if (lastKey != null) { + return new Tuple2(lineMap[lastKey] + 1, offset - lastKey); + } + } + return null; + } + String _fileContents(String file) => __fileContents.putIfAbsent(file, () => new File(file).readAsStringSync()); } diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart index e116720ad0..eaa2589bbc 100644 --- a/lib/src/markdown_processor.dart +++ b/lib/src/markdown_processor.dart @@ -9,18 +9,12 @@ import 'dart:convert'; import 'dart:math'; import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart' - show - LibraryElement, - Element, - ConstructorElement, - ParameterElement, - PropertyAccessorElement; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/member.dart' show Member; import 'package:html/parser.dart' show parse; import 'package:markdown/markdown.dart' as md; import 'model.dart'; -import 'reporting.dart'; const validHtmlTags = const [ "a", @@ -122,9 +116,24 @@ const validHtmlTags = const [ "video", "wbr" ]; -final nonHTMLRegexp = +final RegExp nonHTML = new RegExp(" ])\\w+[> ]"); +// Type parameters and other things to ignore at the end of doc references. +final RegExp trailingIgnoreStuff = new RegExp(r'(<.*>|\(.*\))$'); + +// Things to ignore at the beginning of doc references +final RegExp leadingIgnoreStuff = + new RegExp(r'^(const|final|var)[\s]+', multiLine: true); + +// This is explicitly intended as a reference to a constructor. +final RegExp isConstructor = new RegExp(r'^new[\s]+', multiLine: true); + +// This is probably not really intended as a doc reference, so don't try or +// warn about them. +// Covers anything with leading digits/symbols, empty string, weird punctuation, spaces. +final RegExp notARealDocReference = new RegExp(r'''(^[^\w]|^[\d]|[,"'/]|^$)'''); + // We don't emit warnings currently: #572. const List _oneLinerSkipTags = const ["code", "pre"]; @@ -139,23 +148,40 @@ final RegExp _hide_schemes = new RegExp('^(http|https)://'); class MatchingLinkResult { final ModelElement element; final String label; - MatchingLinkResult(this.element, this.label); + final bool warn; + MatchingLinkResult(this.element, this.label, {this.warn: true}); +} + +// Calculate a class hint for findCanonicalModelElementFor. +ModelElement _getPreferredClass(ModelElement modelElement) { + if (modelElement is EnclosedElement && + (modelElement as EnclosedElement).enclosingElement is Class) { + return (modelElement as EnclosedElement).enclosingElement; + } else if (modelElement is Class) { + return modelElement; + } + return null; } // TODO: this is in the wrong place NodeList _getCommentRefs(ModelElement modelElement) { - if (modelElement == null) return null; - if (modelElement.documentation == null && modelElement.canOverride()) { - var melement = modelElement.overriddenElement; - if (melement != null && - melement.element.computeNode() != null && - melement.element.computeNode() is AnnotatedNode) { - var docComment = (melement.element.computeNode() as AnnotatedNode) - .documentationComment; - if (docComment != null) return docComment.references; - return null; + Class preferredClass = _getPreferredClass(modelElement); + ModelElement cModelElement = modelElement.package + .findCanonicalModelElementFor(modelElement.element, + preferredClass: preferredClass); + if (cModelElement == null) return null; + modelElement = cModelElement; + + if (modelElement.element.documentationComment == null && + modelElement.canOverride()) { + var node = modelElement.overriddenElement?.element?.computeNode(); + if (node is AnnotatedNode) { + if (node.documentationComment != null) { + return node.documentationComment.references; + } } } + if (modelElement.element.computeNode() is AnnotatedNode) { final AnnotatedNode annotatedNode = modelElement.element.computeNode(); if (annotatedNode.documentationComment != null) { @@ -174,31 +200,65 @@ NodeList _getCommentRefs(ModelElement modelElement) { } } } + + // Our references might come from somewhere up in the inheritance chain. + // TODO(jcollins-g): rationalize this and all other places where docs are + // inherited to be consistent. + if (modelElement.element is ClassMemberElement) { + var node = modelElement.element + .getAncestor((e) => e is ClassElement) + .computeNode(); + if (node is AnnotatedNode) { + if (node.documentationComment != null) { + return node.documentationComment.references; + } + } + } return null; } /// Returns null if element is a parameter. MatchingLinkResult _getMatchingLinkElement( - String codeRef, ModelElement element, List commentRefs, - {bool isConstructor: false}) { - if (commentRefs == null) return new MatchingLinkResult(null, null); + String codeRef, ModelElement element, List commentRefs) { + // By debugging inspection, it seems correct to not warn when we don't have + // CommentReferences; there's actually nothing that needs resolving in + // that case. + if (commentRefs == null) + return new MatchingLinkResult(null, null, warn: false); + + if (!codeRef.contains(isConstructor) && + codeRef.contains(notARealDocReference)) { + // Don't waste our time on things we won't ever find. + return new MatchingLinkResult(null, null, warn: false); + } Element refElement; - for (CommentReference ref in commentRefs) { - if (ref.identifier.name == codeRef) { - bool isConstrElement = ref.identifier.staticElement is ConstructorElement; - if (isConstructor && isConstrElement || - !isConstructor && !isConstrElement) { - refElement = ref.identifier.staticElement; - break; + // Try expensive not-scoped lookup. + if (refElement == null) { + refElement = _findRefElementInLibrary(codeRef, element); + } + + // This is faster but does not take canonicalization into account; try + // only as a last resort. TODO(jcollins-g): make analyzer comment references + // dartdoc-canonicalization-aware? + if (refElement == null) { + for (CommentReference ref in commentRefs) { + if (ref.identifier.name == codeRef) { + bool isConstrElement = + ref.identifier.staticElement is ConstructorElement; + // Constructors are now handled by library search. + if (!isConstrElement) { + refElement = ref.identifier.staticElement; + break; + } } } } - // Did not find an element in scope + // Did not find it anywhere. if (refElement == null) { - return _findRefElementInLibrary(codeRef, element, commentRefs); + return new MatchingLinkResult(null, null); } if (refElement is PropertyAccessorElement) { @@ -207,10 +267,18 @@ MatchingLinkResult _getMatchingLinkElement( refElement = (refElement as PropertyAccessorElement).variable; } - if (refElement is ParameterElement) return new MatchingLinkResult(null, null); + // Ignore all parameters. + if (refElement is ParameterElement || refElement is TypeParameterElement) + return new MatchingLinkResult(null, null, warn: false); Library refLibrary = element.package.findOrCreateLibraryFor(refElement); - ModelElement refModelElement = refLibrary.modelElementsMap[refElement]; + Element searchElement = + refElement is Member ? refElement.baseElement : refElement; + + Class preferredClass = _getPreferredClass(element); + ModelElement refModelElement = element.package.findCanonicalModelElementFor( + searchElement, + preferredClass: preferredClass); // There have been places in the code which helpfully cache entities // regardless of what package they are associated with. This assert // will protect us from reintroducing that. @@ -218,91 +286,357 @@ MatchingLinkResult _getMatchingLinkElement( if (refModelElement != null) { return new MatchingLinkResult(refModelElement, null); } - return new MatchingLinkResult(null, null); + refModelElement = new ModelElement.from(searchElement, refLibrary); + if (!refModelElement.isCanonical) { + refModelElement.warn(PackageWarning.noCanonicalFound); + // Don't warn about doc references because that's covered by the no + // canonical library found message. + return new MatchingLinkResult(null, null, warn: false); + } + // We should never get here unless there's a bug in findCanonicalModelElementFor. + // findCanonicalModelElementFor(searchElement, preferredClass: preferredClass) + // should only return null if ModelElement.from(searchElement, refLibrary) + // would return a non-canonical element. However, outside of checked mode, + // at least we have a canonical element, so proceed. + assert(false); + return new MatchingLinkResult(refModelElement, null); } -MatchingLinkResult _findRefElementInLibrary( - String codeRef, ModelElement element, List commentRefs) { +/// Returns true if this is a constructor we should consider in +/// _findRefElementInLibrary, or if this isn't a constructor. +bool _ConsiderIfConstructor(String codeRef, ModelElement modelElement) { + if (modelElement is! Constructor) return true; + if (codeRef.contains(isConstructor)) return true; + Constructor aConstructor = modelElement; + List codeRefParts = codeRef.split('.'); + if (codeRefParts.length > 1) { + if (codeRefParts[codeRefParts.length - 1] == + codeRefParts[codeRefParts.length - 2]) { + // Foobar.Foobar -- assume they really do mean the constructor for this class. + return true; + } + } + if (aConstructor.name != aConstructor.enclosingElement.name) { + // This isn't a default constructor so treat it like any other member. + return true; + } + return false; +} + +// Basic map of reference to ModelElement, for cases where we're searching +// outside of scope. +// TODO(jcollins-g): function caches with maps are very common in dartdoc. +// Extract into library. +Map> _findRefElementCache; +// TODO(jcollins-g): Rewrite this to handle constructors in a less hacky way +// TODO(jcollins-g): This function breaks down naturally into many helpers, extract them +// TODO(jcollins-g): Subcomponents of this function shouldn't be adding nulls to results, strip the +// removes out that are gratuitous and debug the individual pieces. +// TODO(jcollins-g): A complex package winds up spending a lot of cycles in here. Optimize. +Element _findRefElementInLibrary(String codeRef, ModelElement element) { + assert(element.package.allLibrariesAdded); + + String codeRefChomped = codeRef.replaceFirst(isConstructor, ''); + final Library library = element.library; final Package package = library.package; - final Map result = {}; - - // TODO(jcollins-g): This is extremely inefficient and no longer necessary - // since allCanonicalModelElements is now stable and doesn't mutate after - // [Package] construction. So precompute and cache the result map somewhere, - // maybe in [Package]. - for (final modelElement in package.allCanonicalModelElements) { - // Constructors are handled in _linkDocReference. - if (codeRef == modelElement.fullyQualifiedName && - modelElement is! Constructor) { - result[modelElement.fullyQualifiedName] = modelElement; + final Set results = new Set(); + + // This might be an operator. Strip the operator prefix and try again. + if (results.isEmpty && codeRef.startsWith('operator')) { + String newCodeRef = codeRef.replaceFirst('operator', ''); + return _findRefElementInLibrary(newCodeRef, element); + } + + results.remove(null); + // Oh, and someone might have some type parameters or other garbage. + if (results.isEmpty && codeRef.contains(trailingIgnoreStuff)) { + String newCodeRef = codeRef.replaceFirst(trailingIgnoreStuff, ''); + return _findRefElementInLibrary(newCodeRef, element); + } + + results.remove(null); + // Oh, and someone might have thrown on a 'const' or 'final' in front. + if (results.isEmpty && codeRef.contains(leadingIgnoreStuff)) { + String newCodeRef = codeRef.replaceFirst(leadingIgnoreStuff, ''); + return _findRefElementInLibrary(newCodeRef, element); + } + + // Maybe this ModelElement has parameters, and this is one of them. + // We don't link these, but this keeps us from emitting warnings. Be sure to + // get members of parameters too. + // TODO(jcollins-g): link to classes that are the types of parameters, where known + results.addAll(element.allParameters.where((p) => + p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}."))); + + results.remove(null); + if (results.isEmpty) { + // Maybe this is local to a class. + // TODO(jcollins-g): tryClasses is a strict subset of the superclass chain. Optimize. + List tryClasses = [_getPreferredClass(element)]; + Class realClass = tryClasses.first; + if (element is Inheritable) { + ModelElement overriddenElement = element.overriddenElement; + while (overriddenElement != null) { + tryClasses.add( + (element.overriddenElement as EnclosedElement).enclosingElement); + overriddenElement = overriddenElement.overriddenElement; + } + } + + for (Class tryClass in tryClasses) { + if (tryClass != null) { + _getResultsForClass( + tryClass, codeRefChomped, results, codeRef, package); + } + if (results.isNotEmpty) break; + } + // Sometimes documentation refers to classes that are further up the chain. + // Get those too. + if (results.isEmpty && realClass != null) { + for (Class superClass + in realClass.superChain.map((et) => et.element as Class)) { + if (!tryClasses.contains(superClass)) { + _getResultsForClass( + superClass, codeRefChomped, results, codeRef, package); + } + if (results.isNotEmpty) break; + } + } + } + results.remove(null); + + // We now need the ref element cache to keep from repeatedly searching [Package.allModelElements]. + // TODO(jcollins-g): Find somewhere to cache elements outside package.libraries + // so we can give the right warning (no canonical found) + // when referring to objects in libraries outside the + // documented set. + if (results.isEmpty && _findRefElementCache == null) { + assert(package.allLibrariesAdded); + _findRefElementCache = new Map(); + for (final modelElement in package.allModelElements) { + _findRefElementCache.putIfAbsent( + modelElement.fullyQualifiedNameWithoutLibrary, () => new Set()); + _findRefElementCache.putIfAbsent( + modelElement.fullyQualifiedName, () => new Set()); + _findRefElementCache[modelElement.fullyQualifiedName].add(modelElement); + _findRefElementCache[modelElement.fullyQualifiedNameWithoutLibrary] + .add(modelElement); } } + // But if not, look for a fully qualified match. (That only makes sense + // if the codeRef might be qualified, and contains periods.) + if (results.isEmpty && + codeRefChomped.contains('.') && + _findRefElementCache.containsKey(codeRefChomped)) { + for (final modelElement in _findRefElementCache[codeRefChomped]) { + if (!_ConsiderIfConstructor(codeRef, modelElement)) continue; + results.add(package.findCanonicalModelElementFor(modelElement.element)); + } + } + results.remove(null); + // Only look for partially qualified matches if we didn't find a fully qualified one. - if (result.isEmpty) { - for (final modelElement in library.allCanonicalModelElements) { - if (codeRef == modelElement.fullyQualifiedNameWithoutLibrary && - modelElement is! Constructor) { - result[modelElement.fullyQualifiedName] = modelElement; + if (results.isEmpty) { + for (final modelElement in library.allModelElements) { + if (!_ConsiderIfConstructor(codeRef, modelElement)) continue; + if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary) { + results.add(package.findCanonicalModelElementFor(modelElement.element)); + } + } + } + results.remove(null); + + // And if we still haven't found anything, just search the whole ball-of-wax. + if (results.isEmpty && _findRefElementCache.containsKey(codeRefChomped)) { + for (final modelElement in _findRefElementCache[codeRefChomped]) { + if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary || + (modelElement is Library && + codeRefChomped == modelElement.fullyQualifiedName)) { + results.add(package.findCanonicalModelElementFor(modelElement.element)); } } } - if (result.isEmpty) { - return new MatchingLinkResult(null, null); - } else if (result.length == 1) { - return new MatchingLinkResult( - result.values.first, result.values.first.name); + // This could conceivably be a reference to an enum member. They don't show up in allModelElements. + // TODO(jcollins-g): Put enum members in allModelElements with useful hrefs without blowing up other assumptions about what that means. + // TODO(jcollins-g): This doesn't provide good warnings if an enum and class have the same name in different libraries in the same package. Fix that. + if (results.isEmpty) { + List codeRefChompedParts = codeRefChomped.split('.'); + if (codeRefChompedParts.length >= 2) { + String maybeEnumName = codeRefChompedParts + .sublist(0, codeRefChompedParts.length - 1) + .join('.'); + String maybeEnumMember = codeRefChompedParts.last; + if (_findRefElementCache.containsKey(maybeEnumName)) { + for (final modelElement in _findRefElementCache[maybeEnumName]) { + if (modelElement is Enum) { + if (modelElement.constants.any((e) => e.name == maybeEnumMember)) { + results.add(modelElement); + break; + } + } + } + } + } + } + + Element result; + + results.remove(null); + if (results.length > 1) { + // If this name could refer to a class or a constructor, prefer the class. + if (results.any((r) => r is Class)) { + results.removeWhere((r) => r is Constructor); + } + } + + if (results.length > 1) { + // Attempt to disambiguate using the library. + // TODO(jcollins-g): we could have saved ourselves some work by using the analyzer + // to search the namespace, somehow. Do that instead. + if (results.any((r) => r.element.isAccessibleIn(element.library.element))) { + results.removeWhere( + (r) => !r.element.isAccessibleIn(element.library.element)); + } + } + + // TODO(jcollins-g): This is only necessary because we had to jettison commentRefs + // as a way to figure this out. We could reintroduce commentRefs, or we could + // compute this via other means. + if (results.length > 1) { + String startName = "${element.fullyQualifiedName}."; + String realName = "${element.fullyQualifiedName}.${codeRefChomped}"; + if (results.any((r) => r.fullyQualifiedName == realName)) { + results.removeWhere((r) => r.fullyQualifiedName != realName); + } + if (results.any((r) => r.fullyQualifiedName.startsWith(startName))) { + results.removeWhere((r) => !r.fullyQualifiedName.startsWith(startName)); + } + } + // TODO(jcollins-g): further disambiguations based on package information? + + if (results.isEmpty) { + result = null; + } else if (results.length == 1) { + result = results.first.element; } else { - warning("Ambiguous reference to [${codeRef}] in ${_elementLocation(element)}. " + - "We found matches to the following elements: ${result.keys.map((k) => "'${k}'").join(", ")}"); - return new MatchingLinkResult(null, null); + element.warn(PackageWarning.ambiguousDocReference, + "[$codeRef] => ${results.map((r) => "'${r.fullyQualifiedName}'").join(", ")}"); + result = results.first.element; } + return result; } -String _linkDocReference(String reference, ModelElement element, - NodeList commentRefs) { - // support for [new Constructor] and [new Class.namedCtr] - var refs = reference.split(' '); - MatchingLinkResult result; - if (refs.length == 2 && refs.first == 'new') { - result = _getMatchingLinkElement(refs[1], element, commentRefs, - isConstructor: true); +// _getResultsForClass assumes codeRefChomped might be a member of tryClass (inherited or not) +// and will add to [results] +void _getResultsForClass(Class tryClass, String codeRefChomped, + Set results, String codeRef, Package package) { + // This might be part of the type arguments for the class, if so, add them. + // Otherwise, search the class. + if ((tryClass.modelType.typeArguments.map((e) => e.name)) + .contains(codeRefChomped)) { + results.add(tryClass.modelType.typeArguments + .firstWhere((e) => e.name == codeRefChomped) + .element); } else { - result = _getMatchingLinkElement(reference, element, commentRefs); + // People like to use 'this' in docrefs too. + if (codeRef == 'this') { + results.add(package.findCanonicalModelElementFor(tryClass.element)); + } else { + // TODO(jcollins-g): get rid of reimplementation of identifier resolution + // or integrate into ModelElement in a simpler way. + List superChain = []; + superChain.add(tryClass); + // This seems duplicitous with our caller, but the preferredClass + // hint matters with findCanonicalModelElementFor. + // TODO(jcollins-g): This makes our caller ~O(n^2) vs length of superChain. + // Fortunately superChains are short, but optimize this if it matters. + superChain + .addAll(tryClass.superChainRaw.map((t) => t.returnElement as Class)); + List codeRefParts = codeRefChomped.split('.'); + for (final c in superChain) { + // TODO(jcollins-g): add a hash-map-enabled lookup function to Class? + for (final modelElement in c.allModelElements) { + if (!_ConsiderIfConstructor(codeRef, modelElement)) continue; + String namePart = modelElement.fullyQualifiedName.split('.').last; + // TODO(jcollins-g): fix operators so we can use 'name' here or similar. + if (codeRefChomped == namePart) { + results.add(package.findCanonicalModelElementFor( + modelElement.element, + preferredClass: tryClass)); + continue; + } + // Handle non-documented class documentation being imported into a + // documented class when it refers to itself (with help from caller's + // iteration on tryClasses). + // TODO(jcollins-g): Fix partial qualifications in _findRefElementInLibrary so it can tell + // when it is referenced from a non-documented element? + // TODO(jcollins-g): We could probably check this early. + if (codeRefParts.first == c.name && codeRefParts.last == namePart) { + results.add(package.findCanonicalModelElementFor( + modelElement.element, + preferredClass: tryClass)); + continue; + } + if (modelElement is Constructor) { + // Constructor names don't include the class, so we might miss them in the above search. + List codeRefParts = codeRefChomped.split('.'); + if (codeRefParts.length > 1) { + String codeRefClass = codeRefParts[codeRefParts.length - 2]; + String codeRefConstructor = codeRefParts.last; + if (codeRefClass == c.name && + codeRefConstructor == + modelElement.fullyQualifiedName.split('.').last) { + results.add(package.findCanonicalModelElementFor( + modelElement.element, + preferredClass: tryClass)); + continue; + } + } + } + } + if (results.isNotEmpty) break; + if (c.fullyQualifiedNameWithoutLibrary == codeRefChomped) { + results.add(c); + break; + } + } + } } +} + +String _linkDocReference(String codeRef, ModelElement element, + NodeList commentRefs) { + // TODO(jcollins-g): Refactor so that doc operations work on the + // documented element. + element = element.overriddenDocumentedElement; + + MatchingLinkResult result; + result = _getMatchingLinkElement(codeRef, element, commentRefs); final ModelElement linkedElement = result.element; - final String label = result.label ?? reference; + final String label = result.label ?? codeRef; if (linkedElement != null) { var classContent = ''; if (linkedElement.isDeprecated) { classContent = 'class="deprecated" '; } - // this would be linkedElement.linkedName, but link bodies are slightly - // different for doc references. sigh. + // This would be linkedElement.linkedName, but link bodies are slightly + // different for doc references. if (linkedElement.href == null) { return '${HTML_ESCAPE.convert(label)}'; } else { return '$label'; } } else { - warning( - "unresolved doc reference '$reference'${element != null ? " (in ${_elementLocation(element)}" : ""}"); + if (result.warn) { + element.warn(PackageWarning.unresolvedDocReference, codeRef); + } return '${HTML_ESCAPE.convert(label)}'; } } -// TODO(jcollins-g): Merge this into new [Package.warn] warning system -String _elementLocation(ModelElement element) { - while ((element.element.documentationComment == null || - element.element.documentationComment == "") && - element.overriddenElement != null) { - element = element.overriddenElement; - } - return "'${element.fullyQualifiedName}' (${element.sourceFileName}:${element.lineNumber})"; -} - String _renderMarkdownToHtml(String text, [ModelElement element]) { md.Node _linkResolver(String name) { NodeList commentRefs = _getCommentRefs(element); @@ -314,22 +648,31 @@ String _renderMarkdownToHtml(String text, [ModelElement element]) { inlineSyntaxes: _markdown_syntaxes, linkResolver: _linkResolver); } +// Maximum number of characters to display before a suspected generic. +const maxPriorContext = 20; +// Maximum number of characters to display after the beginning of a suspected generic. +const maxPostContext = 30; + // Generics should be wrapped into `[]` blocks, to avoid handling them as HTML tags // (like, [Apple]). @Hixie asked for a warning when there's something, that looks // like a non HTML tag (a generic?) outside of a `[]` block. // https://github.com/dart-lang/dartdoc/issues/1250#issuecomment-269257942 -void _showWarningsForGenericsOutsideSquareBracketsBlocks(String text, - [ModelElement element]) { +void _showWarningsForGenericsOutsideSquareBracketsBlocks( + String text, ModelElement element) { List tagPositions = findFreeHangingGenericsPositions(text); if (tagPositions.isNotEmpty) { tagPositions.forEach((int position) { - String errorMessage = "Generic type handled as HTML"; - if (element != null) { - errorMessage += " in ${_elementLocation(element)}"; - } - errorMessage += - " - '${text.substring(max(position - 20, 0), min(position + 20, text.length))}'"; - warning(errorMessage); + String priorContext = + "${text.substring(max(position - maxPriorContext, 0), position)}"; + String postContext = + "${text.substring(position, min(position + maxPostContext, text.length))}"; + priorContext = + priorContext.replaceAll(new RegExp(r'^.*\n', multiLine: true), ''); + postContext = + postContext.replaceAll(new RegExp(r'\n.*$', multiLine: true), ''); + String errorMessage = "$priorContext$postContext"; + // TODO(jcollins-g): allow for more specific error location inside comments + element.warn(PackageWarning.typeAsHtml, errorMessage); }); } } @@ -341,7 +684,7 @@ List findFreeHangingGenericsPositions(String string) { while (true) { final int nextOpenBracket = string.indexOf("[", currentPosition); final int nextCloseBracket = string.indexOf("]", currentPosition); - final int nextNonHTMLTag = string.indexOf(nonHTMLRegexp, currentPosition); + final int nextNonHTMLTag = string.indexOf(nonHTML, currentPosition); final Iterable nextPositions = [ nextOpenBracket, nextCloseBracket, diff --git a/lib/src/model.dart b/lib/src/model.dart index ad2708e8d2..c461f015ba 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -125,6 +125,8 @@ abstract class Inheritable { searchElement = (searchElement as Member).baseElement; } bool foundElement = false; + // TODO(jcollins-g): generate warning if an inherited element's definition + // is in an intermediate non-canonical class in the inheritance chain for (Class c in inheritance.reversed) { if (!foundElement && c.contains(searchElement)) { foundElement = true; @@ -139,7 +141,9 @@ abstract class Inheritable { assert(definingEnclosingElement == _canonicalEnclosingClass); } } else { - _canonicalEnclosingClass = enclosingElement; + if (enclosingElement.isCanonical) { + _canonicalEnclosingClass = enclosingElement; + } } _canonicalEnclosingClassIsSet = true; } @@ -165,9 +169,22 @@ abstract class Inheritable { class Accessor extends ModelElement with SourceCodeMixin implements EnclosedElement { - Accessor(PropertyAccessorElement element, Library library) + ModelElement _enclosingCombo; + Accessor( + PropertyAccessorElement element, Library library, this._enclosingCombo) : super(element, library); + ModelElement get enclosingCombo => _enclosingCombo; + + @override + void warn(PackageWarning kind, [String message]) { + if (enclosingCombo != null) { + enclosingCombo.warn(kind, message); + } else { + super.warn(kind, message); + } + } + @override ModelElement get enclosingElement { if (_accessor.enclosingElement is CompilationUnitElement) { @@ -186,20 +203,33 @@ class Accessor extends ModelElement bool get isGetter => _accessor.isGetter; + ModelElement _overriddenElement; @override Accessor get overriddenElement { - Element parent = element.enclosingElement; - if (parent is ClassElement) { - for (InterfaceType t in getAllSupertypes(parent)) { - var accessor = this.isGetter - ? t.getGetter(element.name) - : t.getSetter(element.name); - if (accessor != null) { - return new ModelElement.from(accessor, library); + assert(package.allLibrariesAdded); + if (_overriddenElement == null) { + Element parent = element.enclosingElement; + if (parent is ClassElement) { + for (InterfaceType t in getAllSupertypes(parent)) { + var accessor = this.isGetter + ? t.getGetter(element.name) + : t.getSetter(element.name); + if (accessor != null) { + Class parentClass = new ModelElement.from( + parent, package.findOrCreateLibraryFor(parent)); + List possibleFields = []; + possibleFields.addAll(parentClass.allInstanceProperties); + possibleFields.addAll(parentClass.staticProperties); + String fieldName = accessor.name.replaceFirst('=', ''); + _overriddenElement = new ModelElement.from(accessor, library, + enclosingCombo: possibleFields + .firstWhere((f) => f.element.name == fieldName)); + break; + } } } } - return null; + return _overriddenElement; } @override @@ -339,6 +369,29 @@ class Class extends ModelElement implements EnclosedElement { return _allElements.contains(element); } + final Set _allModelElements = new Set(); + List get allModelElements { + if (_allModelElements.isEmpty) { + _allModelElements + ..addAll(allInstanceMethods) + ..addAll(allInstanceProperties) + ..addAll(allOperators) + ..addAll(constants) + ..addAll(constructors) + ..addAll(staticMethods) + ..addAll(staticProperties) + ..addAll(allInstanceMethods) + ..addAll(_typeParameters); + } + return _allModelElements.toList(); + } + + List _allCanonicalModelElements; + List get allCanonicalModelElements { + return (_allCanonicalModelElements ??= + allModelElements.where((e) => e.isCanonical).toList()); + } + List get constructors { if (_constructors != null) return _constructors; @@ -467,11 +520,8 @@ class Class extends ModelElement implements EnclosedElement { _inheritedMethods.add(m); _genPageMethods.add(m); } else { - Library lib = package.findOrCreateLibraryFor(value.enclosingElement); - Class enclosingClass = - new ModelElement.from(value.enclosingElement, lib); - _inheritedMethods.add(new ModelElement.from(value, lib, - enclosingClass: enclosingClass)); + _inheritedMethods + .add(new ModelElement.from(value, library, enclosingClass: this)); } } } @@ -528,10 +578,8 @@ class Class extends ModelElement implements EnclosedElement { _inheritedOperators.add(o); _genPageOperators.add(o); } else { - Library lib = package.findOrCreateLibraryFor(value.enclosingElement); - _inheritedOperators.add(new ModelElement.from(value, lib, - enclosingClass: - new ModelElement.from(value.enclosingElement, lib))); + _inheritedOperators + .add(new ModelElement.from(value, library, enclosingClass: this)); } } @@ -892,7 +940,7 @@ class Enum extends Class { _enumFields = _cls.fields .where(isPublic) .where((f) => f.isConst) - .map((field) => new EnumField.forConstant(index++, field, library)) + .map((field) => new ModelElement.from(field, library, index: index++)) .toList(growable: false) ..sort(byName); @@ -903,7 +951,7 @@ class Enum extends Class { List get instanceProperties { return super .instanceProperties - .map((Field p) => new EnumField(p.element, p.library)) + .map((Field p) => new ModelElement.from(p.element, p.library)) .toList(growable: false); } @@ -955,7 +1003,18 @@ class EnumField extends Field { String get linkedName => name; @override - bool get isCanonical => false; + bool get isCanonical { + if (name == 'index') return false; + // If this is something inherited from Object, e.g. hashCode, let the + // normal rules apply. + if (_index == null) { + return super.isCanonical; + } + // TODO(jcollins-g): We don't actually document this as a separate entity; + // do that or change this to false and deal with the + // consequences. + return true; + } @override String get oneLineDoc => documentationAsHtml; @@ -1016,7 +1075,7 @@ class Field extends ModelElement if (enclosingElement is Class) { if (canonicalEnclosingElement == null) return null; retval = - '${canonicalEnclosingElement.library.dirName}/${canonicalEnclosingElement.name}/$_fileName'; + '${canonicalEnclosingElement.canonicalLibrary.dirName}/${canonicalEnclosingElement.name}/$_fileName'; } else if (enclosingElement is Library) { retval = '${canonicalLibrary.dirName}/$_fileName'; } else { @@ -1043,15 +1102,8 @@ class Field extends ModelElement @override String get kind => 'property'; - String get linkedReturnType => modelType.linkedName; - - bool get readOnly => hasGetter && !hasSetter; - bool get readWrite => hasGetter && hasSetter; - String get typeName => "property"; - bool get writeOnly => hasSetter && !hasGetter; - @override List get annotations { List all_annotations = new List(); @@ -1102,20 +1154,16 @@ class Field extends ModelElement t, new ModelElement.from( t.element, _findOrCreateEnclosingLibraryFor(t.element))); - } else { - var s = _field.setter.parameters.first.type; - _modelType = new ElementType( - s, - new ModelElement.from( - s.element, _findOrCreateEnclosingLibraryFor(s.element))); } } } /// Mixin for top-level variables and fields (aka properties) -abstract class GetterSetterCombo { +abstract class GetterSetterCombo implements ModelElement { Accessor get getter { - return _getter == null ? null : new ModelElement.from(_getter, library); + return _getter == null + ? null + : new ModelElement.from(_getter, library, enclosingCombo: this); } String get getterSetterDocumentationComment { @@ -1136,6 +1184,23 @@ abstract class GetterSetterCombo { return buffer.toString(); } + String get linkedReturnType { + if (hasGetter) return modelType.linkedName; + return null; + } + + @override + String get genericParameters { + if (hasSetter) return setter.genericParameters; + return null; + } + + @override + String get linkedParamsNoMetadata { + if (hasSetter) return setter.linkedParamsNoMetadata; + return null; + } + bool get hasExplicitGetter => hasGetter && !_getter.isSynthetic; bool get hasExplicitSetter => hasSetter && !_setter.isSynthetic; @@ -1145,10 +1210,32 @@ abstract class GetterSetterCombo { bool get hasSetter; - Library get library; + bool get hasGetterOrSetterWithoutParams { + return (hasGetter || (hasSetter && !hasExplicitSetter)); + } + + String get arrow { + // → + if (readOnly) return r'→'; + // ← + if (writeOnly) return r'←'; + // ↔ + if (readWrite) return r'↔'; + // A GetterSetterCombo should always be one of readOnly, writeOnly, + // or readWrite. + assert(false); + return null; + } + + bool get readOnly => hasGetter && !hasSetter; + bool get readWrite => hasGetter && hasSetter; + + bool get writeOnly => hasSetter && !hasGetter; Accessor get setter { - return _setter == null ? null : new ModelElement.from(_setter, library); + return _setter == null + ? null + : new ModelElement.from(_setter, library, enclosingCombo: this); } PropertyAccessorElement get _getter; @@ -1207,7 +1294,7 @@ class Library extends ModelElement { .where((element) => element is ClassElement && element.isEnum)); _enums = enumClasses .where(isPublic) - .map((e) => new Enum(e, this)) + .map((e) => new ModelElement.from(e, this)) .toList(growable: false) ..sort(byName); @@ -1442,8 +1529,8 @@ class Library extends ModelElement { } } - Map _modelElementsMap; - Map get modelElementsMap { + Map> _modelElementsMap; + Map> get modelElementsMap { if (_modelElementsMap == null) { final Set results = new Set(); results @@ -1455,23 +1542,28 @@ class Library extends ModelElement { ..addAll(library.typedefs); library.allClasses.forEach((c) { - results.addAll(c.allInstanceMethods); - results.addAll(c.allInstanceProperties); - results.addAll(c.allOperators); - results.addAll(c.constructors); - results.addAll(c.staticMethods); - results.addAll(c.staticProperties); + results.addAll(c.allModelElements); + results.add(c); }); - _modelElementsMap = new Map(); - results.forEach((modelElement) => - _modelElementsMap[modelElement.element] = modelElement); - _modelElementsMap[element] = this; + _modelElementsMap = new Map>(); + results.forEach((modelElement) { + _modelElementsMap.putIfAbsent(modelElement.element, () => new Set()); + _modelElementsMap[modelElement.element].add(modelElement); + }); + _modelElementsMap.putIfAbsent(element, () => new Set()); + _modelElementsMap[element].add(this); } return _modelElementsMap; } - Iterable get allModelElements => modelElementsMap.values; + Iterable get allModelElements sync* { + for (Set modelElements in modelElementsMap.values) { + for (ModelElement modelElement in modelElements) { + yield modelElement; + } + } + } List _allCanonicalModelElements; Iterable get allCanonicalModelElements { @@ -1525,7 +1617,7 @@ class Method extends ModelElement String get href { if (canonicalLibrary == null || canonicalEnclosingElement == null) return null; - return '${canonicalEnclosingElement.library.dirName}/${canonicalEnclosingElement.name}/${fileName}'; + return '${canonicalEnclosingElement.canonicalLibrary.dirName}/${canonicalEnclosingElement.name}/${fileName}'; } @override @@ -1602,7 +1694,8 @@ class Method extends ModelElement /// helps prevent subtle bugs as generated output for a non-canonical /// ModelElement will reference itself as part of the "wrong" [Library] /// from the public interface perspective. -abstract class ModelElement implements Comparable, Nameable, Documentable { +abstract class ModelElement + implements Comparable, Nameable, Documentable, Locatable { final Element _element; final Library _library; @@ -1623,27 +1716,42 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { /// Do not construct any ModelElements unless they are from this constructor. /// TODO(jcollins-g): enforce this. /// Specify enclosingClass only if this is to be an inherited object. + /// Specify index only if this is to be an EnumField.forConstant. + /// Specify enclosingCombo (a GetterSetterCombo) only if this is to be an + /// Accessor. /// TODO(jcollins-g): this way of using the optional parameter is messy, /// clean that up. factory ModelElement.from(Element e, Library library, - {Class enclosingClass}) { - Tuple3 key = - new Tuple3(e, library, enclosingClass); + {Class enclosingClass, int index, ModelElement enclosingCombo}) { + // We don't need index in this key because it isn't a disambiguator. + // It isn't a disambiguator because EnumFields are not inherited, ever. + // TODO(jcollins-g): cleanup class hierarchy so that EnumFields aren't + // Inheritable, somehow? + if (e is Member) e = (e as Member).baseElement; + Tuple4 key = + new Tuple4(e, library, enclosingClass, enclosingCombo); ModelElement newModelElement; if (e.kind != ElementKind.DYNAMIC && - library.package._all_model_elements.containsKey(key)) { - newModelElement = library.package._all_model_elements[key]; + library.package._allConstructedModelElements.containsKey(key)) { + newModelElement = library.package._allConstructedModelElements[key]; } else { if (e.kind == ElementKind.DYNAMIC) { newModelElement = new Dynamic(e, library); } + if (e is LibraryElement) { + newModelElement = new Library(e, library.package); + } // Also handles enums if (e is ClassElement) { - newModelElement = new Class(e, library); - if (newModelElement.library.name == 'dart:core' && - newModelElement.name == 'Object') { - // We've found Object. This is an important object, so save it in the package. - newModelElement.library.package._objectElement = newModelElement; + if (!e.isEnum) { + newModelElement = new Class(e, library); + if (newModelElement.library.name == 'dart:core' && + newModelElement.name == 'Object') { + // We've found Object. This is an important object, so save it in the package. + newModelElement.library.package._objectElement = newModelElement; + } + } else { + newModelElement = new Enum(e, library); } } if (e is FunctionElement) { @@ -1653,10 +1761,19 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { newModelElement = new Typedef(e, library); } if (e is FieldElement) { - if (enclosingClass == null) - newModelElement = new Field(e, library); - else + if (enclosingClass == null) { + if (index != null) { + newModelElement = new EnumField.forConstant(index, e, library); + } else { + if (e.enclosingElement.isEnum) { + newModelElement = new EnumField(e, library); + } else { + newModelElement = new Field(e, library); + } + } + } else { newModelElement = new Field.inherited(e, enclosingClass, library); + } } if (e is ConstructorElement) { newModelElement = new Constructor(e, library); @@ -1677,7 +1794,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { newModelElement = new TopLevelVariable(e, library); } if (e is PropertyAccessorElement) { - newModelElement = new Accessor(e, library); + newModelElement = new Accessor(e, library, enclosingCombo); } if (e is TypeParameterElement) { newModelElement = new TypeParameter(e, library); @@ -1688,8 +1805,21 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { } if (newModelElement == null) throw "Unknown type ${e.runtimeType}"; if (enclosingClass != null) assert(newModelElement is Inheritable); - if (library != null) - library.package._all_model_elements[key] = newModelElement; + if (library != null) { + library.package._allConstructedModelElements[key] = newModelElement; + if (newModelElement is Inheritable) { + Tuple2 iKey = new Tuple2(e, library); + library.package._allInheritableElements + .putIfAbsent(iKey, () => new Set()); + library.package._allInheritableElements[iKey].add(newModelElement); + } + } + if (newModelElement is Accessor) { + assert(newModelElement.enclosingCombo == enclosingCombo); + } else { + assert(enclosingCombo == null); + } + return newModelElement; } @@ -1821,7 +1951,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { // If path inspection or other disambiguation heuristics are needed, // they should go here. if (candidateLibraries.length > 1) { - library.package.warn(this, PackageWarning.ambiguousReexport, + warn(PackageWarning.ambiguousReexport, "${candidateLibraries.map((l) => l.name)}"); } if (candidateLibraries.isNotEmpty) @@ -1842,6 +1972,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { return _canonicalLibrary; } + @override bool get isCanonical { if (library == canonicalLibrary) { if (this is Inheritable) { @@ -1864,36 +1995,47 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { @override String get documentationAsHtml => _documentation.asHtml; + @override Element get element => _element; + @override + String get elementLocation { + // Call nothing from here that can emit warnings or you'll cause stack overflows. + if (lineAndColumn != null) { + return "(${p.toUri(sourceFileName)}:${lineAndColumn.item1}:${lineAndColumn.item2})"; + } + return "(${p.toUri(sourceFileName)})"; + } + /// Returns the fully qualified name. /// /// For example: libraryName.className.methodName + @override String get fullyQualifiedName { return (_fullyQualifiedName ??= _buildFullyQualifiedName()); } String get fullyQualifiedNameWithoutLibrary { - return (_fullyQualifiedNameWithoutLibrary ??= - fullyQualifiedName.split(".").skip(1).join(".")); + // Remember, periods are legal in library names. + if (_fullyQualifiedNameWithoutLibrary == null) { + _fullyQualifiedNameWithoutLibrary = + fullyQualifiedName.replaceFirst("${library.fullyQualifiedName}.", ''); + } + return _fullyQualifiedNameWithoutLibrary; } String get sourceFileName => element.source.fullName; - int _lineNumber; + Tuple2 _lineAndColumn; bool _isLineNumberComputed = false; - int get lineNumber { + @override + Tuple2 get lineAndColumn { + // TODO(jcollins-g): we should always be able to get line numbers. Why can't we, sometimes? if (!_isLineNumberComputed) { - var node = element.computeNode(); - if (node is Declaration && node.element != null) { - var element = node.element; - var lineNumber = lineNumberCache.lineNumber( - element.source.fullName, element.nameOffset); - _lineNumber = lineNumber + 1; - } - _isLineNumberComputed = true; + _lineAndColumn = lineNumberCache.lineAndColumn( + element.source.fullName, element.nameOffset); } - return _lineNumber; + return _lineAndColumn; } bool get hasAnnotations => annotations.isNotEmpty; @@ -1906,6 +2048,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { /// If canonicalLibrary (or canonicalEnclosingElement, for Inheritable /// subclasses) is null, href should be null. + @override String get href; String get htmlId => name; @@ -1986,6 +2129,25 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { ModelElement get overriddenElement => null; + ModelElement _overriddenDocumentedElement; + bool _overriddenDocumentedElementIsSet = false; + // TODO(jcollins-g): This method prefers canonical elements, but it isn't + // guaranteed and is probably the source of bugs or confusing warnings. + ModelElement get overriddenDocumentedElement { + if (!_overriddenDocumentedElementIsSet) { + ModelElement found = this; + while ((found.element.documentationComment == null || + found.element.documentationComment == "") && + !found.isCanonical && + found.overriddenElement != null) { + found = found.overriddenElement; + } + _overriddenDocumentedElement = found; + _overriddenDocumentedElementIsSet = true; + } + return _overriddenDocumentedElement; + } + int _overriddenDepth; int get overriddenDepth { if (_overriddenDepth == null) { @@ -2002,6 +2164,34 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { Package get package => (this is Library) ? (this as Library).package : this.library.package; + List _allParameters; + // TODO(jcollins-g): This is in the wrong place. Move parts to GetterSetterCombo, + // elsewhere as appropriate? + List get allParameters { + if (_allParameters == null) { + final Set recursedParameters = new Set(); + final Set newParameters = new Set(); + if (this is GetterSetterCombo && + (this as GetterSetterCombo).setter != null) { + newParameters.addAll((this as GetterSetterCombo).setter.parameters); + } else { + if (canHaveParameters) newParameters.addAll(parameters); + } + while (newParameters.isNotEmpty) { + recursedParameters.addAll(newParameters); + newParameters.clear(); + for (Parameter p in recursedParameters) { + if (p.modelType.element.canHaveParameters) { + newParameters.addAll(p.modelType.element.parameters + .where((p) => !recursedParameters.contains(p))); + } + } + } + _allParameters = recursedParameters.toList(); + } + return _allParameters; + } + List get parameters { if (!canHaveParameters) { throw new StateError("$element cannot have parameters"); @@ -2027,6 +2217,17 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { return _parameters; } + void warn(PackageWarning kind, [String message]) { + if (kind == PackageWarning.unresolvedDocReference && + overriddenElement != null) { + // The documentation we're using for this element came from somewhere else. + // Attach the warning to that element to deduplicate. + overriddenElement.warn(kind, message); + } else { + library.package.warn(this, kind, message); + } + } + String get _computeDocumentationComment => element.documentationComment; Documentation get _documentation { @@ -2204,7 +2405,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { } if (href == null) { - package.warn(this, PackageWarning.noCanonicalFound); + warn(PackageWarning.noCanonicalFound); return HTML_ESCAPE.convert(name); } @@ -2486,28 +2687,209 @@ class Operator extends Method { // The kinds of warnings that can be displayed when documenting a package. enum PackageWarning { + ambiguousDocReference, ambiguousReexport, noCanonicalFound, noLibraryLevelDocs, categoryOrderGivesMissingPackageName, + unresolvedDocReference, + brokenLink, + orphanedFile, + unknownFile, + typeAsHtml, } -class Package implements Nameable, Documentable { +/// Provides description text and command line flags for warnings. +/// TODO(jcollins-g): Actually use this for command line flags. +Map> packageWarningText = { + PackageWarning.ambiguousDocReference: [ + "ambiguous-doc-reference", + "A comment reference could refer to two or more different objects" + ], + PackageWarning.ambiguousReexport: [ + "ambiguous-reexport", + "A symbol is exported from private to public in more than one place and dartdoc is forced to guess which one is canonical" + ], + PackageWarning.noCanonicalFound: [ + "no-canonical-found", + "A symbol is is part of the public interface for this package, but no library documented with this package documents it so dartdoc can not link to it" + ], + PackageWarning.noLibraryLevelDocs: [ + "no-library-level-docs", + "There are no library level docs for this library" + ], + PackageWarning.categoryOrderGivesMissingPackageName: [ + "category-order-gives-missing-package-name", + "The category-order flag on the command line was given the name of a nonexistent package" + ], + PackageWarning.unresolvedDocReference: [ + "unresolved-doc-reference", + "A comment reference could not be found in parameters, enclosing class, enclosing library, or at the top level of any documented library with the package" + ], + PackageWarning.brokenLink: [ + "brokenLink", + "Dartdoc generated a link to a non-existent file" + ], + PackageWarning.orphanedFile: [ + "orphanedFile", + "Dartdoc generated files that are unreachable from the index" + ], + PackageWarning.unknownFile: [ + "unknownFile", + "A leftover file exists in the tree that dartdoc did not write in this pass" + ], + PackageWarning.typeAsHtml: [ + "typeAsHtml", + "Use of <> in a comment for type parameters is being treated as HTML by markdown" + ], +}; + +// Something that can be located for warning purposes. +abstract class Locatable { + String get fullyQualifiedName; + String get href; + Element get element; + String get elementLocation; + Tuple2 get lineAndColumn; + bool get isCanonical; +} + +class PackageWarningOptions { + // PackageWarnings must be in one of _ignoreWarnings or union(_asWarnings, _asErrors) + final Set _ignoreWarnings = new Set(); + // PackageWarnings can be in both asWarnings and asErrors, latter takes precedence + final Set _asWarnings = new Set(); + final Set _asErrors = new Set(); + + Set get ignoreWarnings => _ignoreWarnings; + Set get asWarnings => _asWarnings; + Set get asErrors => _asErrors; + + PackageWarningOptions() { + _asWarnings.addAll(PackageWarning.values); + ignore(PackageWarning.typeAsHtml); + } + + void _assertInvariantsOk() { + assert(_asWarnings + .union(_asErrors) + .union(_ignoreWarnings) + .containsAll(PackageWarning.values.toSet())); + assert(_asWarnings.union(_asErrors).intersection(_ignoreWarnings).isEmpty); + } + + void ignore(PackageWarning kind) { + _assertInvariantsOk(); + _asWarnings.remove(kind); + _asErrors.remove(kind); + _ignoreWarnings.add(kind); + _assertInvariantsOk(); + } + + void warn(PackageWarning kind) { + _assertInvariantsOk(); + _ignoreWarnings.remove(kind); + _asWarnings.add(kind); + _asErrors.remove(kind); + _assertInvariantsOk(); + } + + void error(PackageWarning kind) { + _assertInvariantsOk(); + _ignoreWarnings.remove(kind); + _asWarnings.add(kind); + _asErrors.add(kind); + _assertInvariantsOk(); + } +} + +class PackageWarningCounter { + final Map>> _countedWarnings = + new Map(); + final Map _warningCounts = new Map(); + final PackageWarningOptions options; + + PackageWarningCounter(this.options); + + /// Actually write out the warning. Assumes it is already counted with add. + void _writeWarning(PackageWarning kind, String fullMessage) { + if (options.ignoreWarnings.contains(kind)) return; + String toWrite; + if (!options.asErrors.contains(kind)) { + if (options.asWarnings.contains(kind)) + toWrite = "warning: ${fullMessage}"; + } else { + if (options.asErrors.contains(kind)) toWrite = "error: ${fullMessage}"; + } + if (toWrite != null) stderr.write("\n ${toWrite}"); + } + + /// Returns true if we've already warned for this. + bool hasWarning(Element element, PackageWarning kind, String message) { + Tuple2 warningData = new Tuple2(kind, message); + if (_countedWarnings.containsKey(element)) { + return _countedWarnings[element].contains(warningData); + } + return false; + } + + /// Adds the warning to the counter, and writes out the fullMessage string + /// if configured to do so. + void addWarning(Element element, PackageWarning kind, String message, + String fullMessage) { + assert(!hasWarning(element, kind, message)); + Tuple2 warningData = new Tuple2(kind, message); + _warningCounts.putIfAbsent(kind, () => 0); + _warningCounts[kind] += 1; + _countedWarnings.putIfAbsent(element, () => new Set()); + _countedWarnings[element].add(warningData); + _writeWarning(kind, fullMessage); + } + + int get errorCount { + return _warningCounts.keys + .map((w) => options.asErrors.contains(w) ? _warningCounts[w] : 0) + .reduce((a, b) => a + b); + } + + int get warningCount { + return _warningCounts.keys + .map((w) => + options.asWarnings.contains(w) && !options.asErrors.contains(w) + ? _warningCounts[w] + : 0) + .reduce((a, b) => a + b); + } + + @override + String toString() { + String errors = '$errorCount ${errorCount == 1 ? "error" : "errors"}'; + String warnings = + '$warningCount ${warningCount == 1 ? "warning" : "warnings"}'; + return [errors, warnings].join(', '); + } +} + +class Package implements Nameable, Documentable, Locatable { // Library objects serving as entry points for documentation. final List _libraries = []; // All library objects related to this package; a superset of _libraries. - final Map _all_libraries = new Map(); - // All ModelElements constructed for this package; a superset of allModelElements. - final Map, ModelElement> _all_model_elements = - new Map(); + final Map _allLibraries = new Map(); + + // Objects to keep track of warnings. + final PackageWarningOptions _packageWarningOptions; + PackageWarningCounter _packageWarningCounter; + // All ModelElements constructed for this package; a superset of allModelElements. + final Map, ModelElement> + _allConstructedModelElements = new Map(); + + // Anything that might be inheritable, place here for later lookup. + final Map, Set> + _allInheritableElements = new Map(); /// Map of Class.href to a list of classes implementing that class final Map> _implementors = new Map(); - final Map> _displayedWarnings = new Map(); - final Map> _displayedNonElementWarnings = - new Map(); - final PackageMeta packageMeta; final Map _elementToLibrary = {}; @@ -2515,15 +2897,17 @@ class Package implements Nameable, Documentable { final Map _macros = {}; bool allLibrariesAdded = false; - Package(Iterable libraryElements, this.packageMeta) { - assert(_all_model_elements.isEmpty); - assert(_all_libraries.isEmpty); + Package(Iterable libraryElements, this.packageMeta, + this._packageWarningOptions) { + assert(_allConstructedModelElements.isEmpty); + assert(_allLibraries.isEmpty); + _packageWarningCounter = new PackageWarningCounter(_packageWarningOptions); libraryElements.forEach((element) { // add only if the element should be included in the public api if (isPublic(element)) { var lib = new Library._(element, this); _libraries.add(lib); - _all_libraries[element] = lib; + _allLibraries[element] = lib; assert(!_elementToLibrary.containsKey(lib.element)); _elementToLibrary[element] = lib; } @@ -2538,40 +2922,59 @@ class Package implements Nameable, Documentable { _implementors.values.forEach((l) => l.sort()); } - void warn(ModelElement modelElement, PackageWarning kind, [String message]) { - String elementName; - String fullElementName; - if (modelElement != null) { - if (_displayedWarnings.containsKey(modelElement.element) && - _displayedWarnings[modelElement.element].contains(kind)) { - return; - } + @override + Element get element => null; + + @override + String get elementLocation => '(top level package)'; - if (modelElement.element is ClassMemberElement) { - Element theElement = modelElement.element; - ClassElement enclosingClass; - while (theElement is! ClassElement && - theElement.enclosingElement != null) { - theElement = theElement.enclosingElement; + @override + Tuple2 get lineAndColumn => null; + + @override + String get fullyQualifiedName => name; + + @override + bool get isCanonical => true; + + PackageWarningCounter get packageWarningCounter => _packageWarningCounter; + + void warn(Locatable modelElement, PackageWarning kind, [String message]) { + if (modelElement != null) { + // This sort of warning is only applicable to top level elements. + if (kind == PackageWarning.ambiguousReexport) { + Element topLevelElement = modelElement.element; + while (topLevelElement.enclosingElement is! CompilationUnitElement) { + topLevelElement = topLevelElement.enclosingElement; } - enclosingClass = theElement as ClassElement; - if (_displayedWarnings.containsKey(enclosingClass) && - _displayedWarnings[enclosingClass].contains(kind)) return; - elementName = "${enclosingClass.name}.${modelElement.name}"; - } else { - elementName = "${modelElement.name}"; + modelElement = new ModelElement.from( + topLevelElement, findOrCreateLibraryFor(topLevelElement)); + } + if (modelElement is Accessor) { + // This might be part of a Field, if so, assign this warning to the field + // rather than the Accessor. + if ((modelElement as Accessor).enclosingCombo != null) + modelElement = (modelElement as Accessor).enclosingCombo; } - fullElementName = - "${elementName} (${modelElement.library.element.location})"; } else { // If we don't have an element, we need a message to disambiguate. assert(message != null); - // Checking for kinds here seems a bit redundant right now, but for - // consistency... - if (_displayedNonElementWarnings.containsKey(message) && - _displayedNonElementWarnings[message].contains(kind)) { - return; - } + } + if (_packageWarningCounter.hasWarning( + modelElement?.element, kind, message)) { + return; + } + // Elements that are part of the Dart SDK can have colons in their FQNs. + // This confuses IntelliJ and makes it so it can't link to the location + // of the error in the console window, so separate out the library from + // the path. + // TODO(jcollins-g): What about messages that may include colons? Substituting + // them out doesn't work as well there since it might confuse + // the user, yet we still want IntelliJ to link properly. + String nameSplitFromColonPieces; + if (modelElement != null) { + nameSplitFromColonPieces = + modelElement.fullyQualifiedName.replaceFirst(':', '-'); } String warningMessage; switch (kind) { @@ -2581,38 +2984,66 @@ class Package implements Nameable, Documentable { // TODO(jcollins-g): add a dartdoc flag to enable external website linking for non-canonical elements, using .packages for versioning // TODO(jcollins-g): support documenting multiple packages at once and linking between them warningMessage = - "no canonical library found for ${fullElementName}, not linking"; + "no canonical library found for ${nameSplitFromColonPieces}, not linking"; break; case PackageWarning.ambiguousReexport: // Fix these warnings by adding the original library exporting the // symbol with --include, or by using --auto-include-dependencies. // TODO(jcollins-g): add a dartdoc flag to force a particular resolution order for (or drop) ambiguous reexports warningMessage = - "ambiguous reexport of ${fullElementName}, canonicalization candidates: ${message}"; + "ambiguous reexport of ${nameSplitFromColonPieces}, canonicalization candidates: ${message}"; break; case PackageWarning.noLibraryLevelDocs: warningMessage = - "${fullElementName} has no library level documentation comments"; + "${modelElement.fullyQualifiedName} has no library level documentation comments"; + break; + case PackageWarning.ambiguousDocReference: + warningMessage = + "ambiguous doc reference in ${nameSplitFromColonPieces}: ${message}"; break; case PackageWarning.categoryOrderGivesMissingPackageName: warningMessage = - "--category-order gives invalid package name: ${message}"; + "--category-order gives invalid package name: '${message}'"; + break; + case PackageWarning.unresolvedDocReference: + warningMessage = + "unresolved doc reference [${message}], from ${nameSplitFromColonPieces}"; + break; + case PackageWarning.brokenLink: + warningMessage = + 'dartdoc generated a broken link to: ${message}, from ${nameSplitFromColonPieces == null ? "" : nameSplitFromColonPieces}'; + break; + case PackageWarning.orphanedFile: + warningMessage = + 'dartdoc generated a file orphan: ${message}, from ${nameSplitFromColonPieces == null ? "" : nameSplitFromColonPieces}'; + break; + case PackageWarning.unknownFile: + warningMessage = + 'dartdoc detected an unknown file in the doc tree: ${message}'; + break; + case PackageWarning.typeAsHtml: + // The message for this warning can contain many punctuation and other symbols, + // so bracket with a triple quote for defense. + warningMessage = 'generic type handled as HTML: """${message}"""'; break; } - stderr.write("\n warning: ${warningMessage}"); - if (modelElement != null) { - _displayedWarnings.putIfAbsent(modelElement.element, () => new Set()); - _displayedWarnings[modelElement.element].add(kind); - } else { - _displayedNonElementWarnings.putIfAbsent(message, () => new Set()); - _displayedNonElementWarnings[message].add(kind); - } + // warningMessage should not contain "file:" or "dart:" -- confuses IntelliJ. + ['file:', 'dart:'].forEach((s) { + // message can contain user text; nothing we can do about that. + assert(!warningMessage.contains(s) || message.contains(s)); + }); + String fullMessage = + "${warningMessage} ${modelElement != null ? modelElement.elementLocation : ''}"; + packageWarningCounter.addWarning( + modelElement?.element, kind, message, fullMessage); } static Package _withAutoIncludedDependencies( - Set libraryElements, PackageMeta packageMeta) { + Set libraryElements, + PackageMeta packageMeta, + PackageWarningOptions options) { var startLength = libraryElements.length; - Package package = new Package(libraryElements, packageMeta); + Package package = new Package(libraryElements, packageMeta, options); // TODO(jcollins-g): this is inefficient; keep track of modelElements better package.allModelElements.forEach((modelElement) { @@ -2632,14 +3063,17 @@ class Package implements Nameable, Documentable { }); if (libraryElements.length > startLength) - return _withAutoIncludedDependencies(libraryElements, packageMeta); + return _withAutoIncludedDependencies( + libraryElements, packageMeta, options); return package; } static Package withAutoIncludedDependencies( - Iterable libraryElements, PackageMeta packageMeta) { + Iterable libraryElements, + PackageMeta packageMeta, + PackageWarningOptions options) { return _withAutoIncludedDependencies( - new Set()..addAll(libraryElements), packageMeta); + new Set()..addAll(libraryElements), packageMeta, options); } List get categories { @@ -2698,6 +3132,24 @@ class Package implements Nameable, Documentable { return hasDocumentationFile ? documentationFile.contents : null; } + /// A lookup index for hrefs to allow warnings to indicate where a broken + /// link or orphaned file may have come from. Not cached because + /// [ModelElement]s can be created at any time and we're basing this + /// on more than just [allModelElements] to make the error messages + /// comprehensive. + Map> get allHrefs { + Map> hrefMap = new Map(); + // TODO(jcollins-g ): handle calculating hrefs causing new elements better + // than toList(). + for (ModelElement modelElement + in _allConstructedModelElements.values.toList()) { + if (modelElement.href == null) continue; + hrefMap.putIfAbsent(modelElement.href, () => new Set()); + hrefMap[modelElement.href].add(modelElement); + } + return hrefMap; + } + @override String get documentationAsHtml { if (_docsAsHtml != null) return _docsAsHtml; @@ -2719,6 +3171,7 @@ class Package implements Nameable, Documentable { bool get hasDocumentationFile => documentationFile != null; // TODO: make this work + @override String get href => 'index.html'; /// Does this package represent the SDK? @@ -2801,30 +3254,103 @@ class Package implements Nameable, Documentable { /// Tries to find a top level library that references this element. Library findCanonicalLibraryFor(Element e) { assert(allLibrariesAdded); + Element searchElement = e; + if (e is PropertyAccessorElement) { + searchElement = e.variable; + } if (_canonicalLibraryFor.containsKey(e)) { return _canonicalLibraryFor[e]; } _canonicalLibraryFor[e] = null; for (Library library in libraries) { - if (library.modelElementsMap.containsKey(e)) { - if (library.modelElementsMap[e].isCanonical) { - _canonicalLibraryFor[e] = library; - break; + if (library.modelElementsMap.containsKey(searchElement)) { + for (ModelElement modelElement + in library.modelElementsMap[searchElement]) { + if (modelElement.isCanonical) { + _canonicalLibraryFor[e] = library; + break; + } } } } return _canonicalLibraryFor[e]; } - /// Tries to find a canonical ModelElement for this element. - /// Do not use this to find Inheritable ModelElements; we can't - /// figure out whether this element is inherited by ourselves. - ModelElement findCanonicalModelElementFor(Element e) { + /// Tries to find a canonical ModelElement for this element. If we know + /// this element is related to a particular class, pass preferredClass to + /// disambiguate. + ModelElement findCanonicalModelElementFor(Element e, {Class preferredClass}) { + assert(allLibrariesAdded); Library lib = findCanonicalLibraryFor(e); ModelElement modelElement; - if (lib != null) modelElement = new ModelElement.from(e, lib); - assert(modelElement is! Inheritable); + // TODO(jcollins-g): The data structures should be changed to eliminate guesswork + // with member elements. + if (e is ClassMemberElement || e is PropertyAccessorElement) { + // Prefer Fields over Accessors. + if (e is PropertyAccessorElement) + e = (e as PropertyAccessorElement).variable; + Set candidates = new Set(); + Tuple2 iKey = new Tuple2(e, lib); + Tuple4 key = + new Tuple4(e, lib, null, null); + Tuple4 keyWithClass = + new Tuple4(e, lib, preferredClass, null); + if (_allConstructedModelElements.containsKey(key)) { + candidates.add(_allConstructedModelElements[key]); + } + if (_allConstructedModelElements.containsKey(keyWithClass)) { + candidates.add(_allConstructedModelElements[keyWithClass]); + } + if (candidates.isEmpty && _allInheritableElements.containsKey(iKey)) { + candidates.addAll( + _allInheritableElements[iKey].where((me) => me.isCanonical)); + } + Class canonicalClass = findCanonicalModelElementFor(e.enclosingElement); + if (canonicalClass != null) { + candidates.addAll(canonicalClass.allCanonicalModelElements.where((m) { + if (m.element is FieldElement) { + FieldElement fieldElement = m.element as FieldElement; + Element getter; + Element setter; + if (fieldElement.getter?.isSynthetic == true) { + getter = fieldElement.getter.variable; + } else { + getter = fieldElement.getter; + } + if (fieldElement.setter?.isSynthetic == true) { + setter = fieldElement.setter.variable; + } else { + setter = fieldElement.setter; + } + if (setter == e || getter == e) return true; + } + if (m.element == e) return true; + return false; + })); + } + Set matches = new Set() + ..addAll(candidates.where((me) => me.isCanonical)); + + // This is for situations where multiple classes may actually be canonical + // for an inherited element whose defining Class is not canonical. + if (matches.length > 1 && preferredClass != null) { + // Search for matches inside our superchain. + List superChain = + preferredClass.superChainRaw.map((et) => et.element).toList(); + superChain.add(preferredClass); + matches.removeWhere((me) => + !superChain.contains((me as EnclosedElement).enclosingElement)); + } + assert(matches.length <= 1); + if (!matches.isEmpty) modelElement = matches.first; + } else { + if (lib != null) modelElement = new ModelElement.from(e, lib); + assert(modelElement is! Inheritable); + if (modelElement != null && !modelElement.isCanonical) { + modelElement = null; + } + } return modelElement; } @@ -2833,8 +3359,8 @@ class Package implements Nameable, Documentable { /// set of canonical Libraries). Library findOrCreateLibraryFor(Element e) { // This is just a cache to avoid creating lots of libraries over and over. - if (_all_libraries.containsKey(e.library)) { - return _all_libraries[e.library]; + if (_allLibraries.containsKey(e.library)) { + return _allLibraries[e.library]; } // can be null if e is for dynamic if (e.library == null) { @@ -2844,13 +3370,14 @@ class Package implements Nameable, Documentable { if (foundLibrary == null) { foundLibrary = new Library._(e.library, this); - _all_libraries[e.library] = foundLibrary; + _allLibraries[e.library] = foundLibrary; } return foundLibrary; } List _allModelElements; Iterable get allModelElements { + assert(allLibrariesAdded); if (_allModelElements == null) { _allModelElements = []; this.libraries.forEach((library) { @@ -2984,7 +3511,7 @@ abstract class SourceCodeMixin { } } - int get lineNumber; + Tuple2 get lineAndColumn; Element get element; @@ -3068,9 +3595,9 @@ abstract class SourceCodeMixin { } String get _crossdartUrl { - if (lineNumber != null && _crossdartPath != null) { + if (lineAndColumn != null && _crossdartPath != null) { String url = "//www.crossdart.info/p/${_crossdartPath}.html"; - return "${url}#line-${lineNumber}"; + return "${url}#line-${lineAndColumn.item1}"; } else { return null; } @@ -3142,13 +3669,6 @@ class TopLevelVariable extends ModelElement @override String get kind => 'top-level property'; - String get linkedReturnType => modelType.linkedName; - - bool get readOnly => hasGetter && !hasSetter; - bool get readWrite => hasGetter && hasSetter; - - bool get writeOnly => hasSetter && !hasGetter; - @override Set get features { Set all_features = super.features; diff --git a/lib/src/reporting.dart b/lib/src/reporting.dart deleted file mode 100644 index f52f9748d7..0000000000 --- a/lib/src/reporting.dart +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file -// 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. - -library reporting; - -import 'dart:io'; - -import 'config.dart'; - -// TODO(jcollins-g): Merge this with the new [Package] error reporting. -void warning(String message) { - // TODO: Could handle fatal warnings here, or print to stderr, or remember - // that we had at least one warning, and exit with non-null exit code in this case. - if (config != null && config.showWarnings) { - stderr.writeln("warning: ${message}"); - } -} diff --git a/lib/templates/_property.html b/lib/templates/_property.html index 47f83107dd..7ac2c275e2 100644 --- a/lib/templates/_property.html +++ b/lib/templates/_property.html @@ -1,7 +1,16 @@ +{{ #hasExplicitSetter }} +
+ {{{linkedName}}}{{{genericParameters}}} + {{{ arrow }}} {{{ linkedParamsNoMetadata }}} + +
+{{ /hasExplicitSetter }} +{{ #hasGetterOrSetterWithoutParams }}
{{{linkedName}}} - → {{{ linkedReturnType }}} + {{{ arrow }}} {{{ linkedReturnType }}}
+{{ /hasGetterOrSetterWithoutParams }} {{{ oneLineDoc }}} {{>features}} diff --git a/pubspec.yaml b/pubspec.yaml index 9a163c5889..0c7e9536ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dartdoc # Also update the `version` field in lib/dartdoc.dart. -version: 0.10.0 +version: 0.11.0 author: Dart Team description: A documentation generator for Dart. homepage: https://github.com/dart-lang/dartdoc diff --git a/test/model_test.dart b/test/model_test.dart index c2aa4a3702..96b68ec131 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -37,7 +37,8 @@ void main() { Package sdkAsPackage = Package.withAutoIncludedDependencies( getSdkLibrariesToDocument(utils.sdkDir, utils.analyzerHelper.context), - new PackageMeta.fromSdk(sdkDir)); + new PackageMeta.fromSdk(sdkDir), + new PackageWarningOptions()); group('Package', () { group('test package', () { @@ -314,9 +315,12 @@ void main() { }); test( - 'link to a name in another library in this package, but is not imported into this library, is codeified', + 'link to a name in another library in this package, but is not imported into this library, should still be linked', () { - expect(docsAsHtml, contains('doesStuff')); + expect( + docsAsHtml, + contains( + 'doesStuff')); }); test( @@ -327,7 +331,7 @@ void main() { expect(helperClass.documentationAsHtml, contains('Apple')); expect(helperClass.documentationAsHtml, - contains('B')); + contains('ex.B')); }); test( @@ -478,7 +482,8 @@ void main() { expect(resolved, isNotNull); expect(resolved, contains('BaseClass')); - expect(resolved, contains('linking over to Apple.')); + expect(resolved, + contains('Linking over to Apple')); }); test('references to class and constructors', () { @@ -1469,7 +1474,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect(appleDefaultConstructor.enclosingElement.name, equals(apple.name)); }); - test('has contructor', () { + test('has constructor', () { expect(appleDefaultConstructor, isNotNull); expect(appleDefaultConstructor.name, equals('Apple')); expect(appleDefaultConstructor.shortName, equals('Apple')); diff --git a/test/src/utils.dart b/test/src/utils.dart index e69de759bc..ac12e3bc54 100644 --- a/test/src/utils.dart +++ b/test/src/utils.dart @@ -69,10 +69,14 @@ Package _bootPackage(Iterable libPaths, String dirPath, if (withAutoIncludedDependencies) { return Package.withAutoIncludedDependencies( - libElements, new PackageMeta.fromDir(new Directory(dirPath))); + libElements, + new PackageMeta.fromDir(new Directory(dirPath)), + new PackageWarningOptions()); } else { return new Package( - libElements, new PackageMeta.fromDir(new Directory(dirPath))); + libElements, + new PackageMeta.fromDir(new Directory(dirPath)), + new PackageWarningOptions()); } } diff --git a/testing/test_package/lib/src/extending.dart b/testing/test_package/lib/src/extending.dart index c60eb1d95f..c84e5cfbe0 100644 --- a/testing/test_package/lib/src/extending.dart +++ b/testing/test_package/lib/src/extending.dart @@ -8,5 +8,5 @@ int topLevelVariable = 1; /// /// Also check out [topLevelVariable]. /// -/// This should not work: linking over to [Apple]. +/// Linking over to [Apple] should work. class ExtendingClass extends BaseClass {} diff --git a/testing/test_package_docs/css/css-library.html b/testing/test_package_docs/css/css-library.html index d2a331e4c4..d6603ad877 100644 --- a/testing/test_package_docs/css/css-library.html +++ b/testing/test_package_docs/css/css-library.html @@ -97,7 +97,7 @@

Properties

theOnlyThingInTheLibrary - → String + ↔ String

diff --git a/testing/test_package_docs/ex/Animal/hashCode.html b/testing/test_package_docs/ex/Animal/hashCode.html index d5c18e1c7d..01a7383e8a 100644 --- a/testing/test_package_docs/ex/Animal/hashCode.html +++ b/testing/test_package_docs/ex/Animal/hashCode.html @@ -110,12 +110,12 @@
Animal

The hash code for this object.

A hash code is a single integer which represents the state of the object that affects == comparisons.

-

All objects have hash codes. -The default hash code represents only the identity of the object, +

All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

-

If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

+

If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Animal/operator_equals.html b/testing/test_package_docs/ex/Animal/operator_equals.html index 03ef1986a4..73707d6ec0 100644 --- a/testing/test_package_docs/ex/Animal/operator_equals.html +++ b/testing/test_package_docs/ex/Animal/operator_equals.html @@ -116,7 +116,7 @@

Animal
either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Apple-class.html b/testing/test_package_docs/ex/Apple-class.html index a6ba69caaa..100a01a1e1 100644 --- a/testing/test_package_docs/ex/Apple-class.html +++ b/testing/test_package_docs/ex/Apple-class.html @@ -191,15 +191,20 @@

    Properties

    m - → int + ↔ int

    The read-write field m.

    read / write
    +
    + s + String something + +
    s - → String + ↔ String

    The getter for s

    @@ -332,7 +337,7 @@

    Static Properties

    string - → String + ↔ String

    diff --git a/testing/test_package_docs/ex/Apple/hashCode.html b/testing/test_package_docs/ex/Apple/hashCode.html index c30625c02f..65fbd473b8 100644 --- a/testing/test_package_docs/ex/Apple/hashCode.html +++ b/testing/test_package_docs/ex/Apple/hashCode.html @@ -121,12 +121,12 @@
    Apple

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Apple/operator_equals.html b/testing/test_package_docs/ex/Apple/operator_equals.html index 306885c7a4..0876e37ed3 100644 --- a/testing/test_package_docs/ex/Apple/operator_equals.html +++ b/testing/test_package_docs/ex/Apple/operator_equals.html @@ -127,7 +127,7 @@

    Apple
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/B-class.html b/testing/test_package_docs/ex/B-class.html index 2266668605..265fdb4233 100644 --- a/testing/test_package_docs/ex/B-class.html +++ b/testing/test_package_docs/ex/B-class.html @@ -180,7 +180,7 @@

    Properties

    autoCompress - → bool + ↔ bool

    The default value is false (compression disabled). @@ -197,7 +197,7 @@

    Properties

    list - → List<String> + ↔ List<String>

    A list of Strings

    @@ -229,7 +229,7 @@

    Properties

    m - → int + ↔ int

    The read-write field m.

    diff --git a/testing/test_package_docs/ex/Cat/hashCode.html b/testing/test_package_docs/ex/Cat/hashCode.html index d5cd2d826d..e8dd98febd 100644 --- a/testing/test_package_docs/ex/Cat/hashCode.html +++ b/testing/test_package_docs/ex/Cat/hashCode.html @@ -108,12 +108,12 @@
    Cat

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Cat/operator_equals.html b/testing/test_package_docs/ex/Cat/operator_equals.html index a5f5679850..92c823ade6 100644 --- a/testing/test_package_docs/ex/Cat/operator_equals.html +++ b/testing/test_package_docs/ex/Cat/operator_equals.html @@ -114,7 +114,7 @@

    Cat
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/CatString-class.html b/testing/test_package_docs/ex/CatString-class.html index a00f3b668c..fcbe2a9563 100644 --- a/testing/test_package_docs/ex/CatString-class.html +++ b/testing/test_package_docs/ex/CatString-class.html @@ -259,7 +259,7 @@

    Methods

    -

    Iterates over the given objects and writes them in sequence.

    +

    Iterates over the given objects and writes them in sequence.

    inherited
    diff --git a/testing/test_package_docs/ex/CatString/hashCode.html b/testing/test_package_docs/ex/CatString/hashCode.html index 842d809447..820424adf3 100644 --- a/testing/test_package_docs/ex/CatString/hashCode.html +++ b/testing/test_package_docs/ex/CatString/hashCode.html @@ -114,12 +114,12 @@
    CatString

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/CatString/operator_equals.html b/testing/test_package_docs/ex/CatString/operator_equals.html index 91a7250f6c..05bb9b4221 100644 --- a/testing/test_package_docs/ex/CatString/operator_equals.html +++ b/testing/test_package_docs/ex/CatString/operator_equals.html @@ -120,7 +120,7 @@

    CatString
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/CatString/writeAll.html b/testing/test_package_docs/ex/CatString/writeAll.html index 33c5b428ec..6cf5419995 100644 --- a/testing/test_package_docs/ex/CatString/writeAll.html +++ b/testing/test_package_docs/ex/CatString/writeAll.html @@ -107,7 +107,7 @@

    CatString
    writeAll(Iterable objects, [ String separator = "" ])
    -

    Iterates over the given objects and writes them in sequence.

    +

    Iterates over the given objects and writes them in sequence.

    diff --git a/testing/test_package_docs/ex/ConstantCat/hashCode.html b/testing/test_package_docs/ex/ConstantCat/hashCode.html index 4a6b3abcb4..4e34f54a4a 100644 --- a/testing/test_package_docs/ex/ConstantCat/hashCode.html +++ b/testing/test_package_docs/ex/ConstantCat/hashCode.html @@ -109,12 +109,12 @@
    ConstantCat

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/ConstantCat/operator_equals.html b/testing/test_package_docs/ex/ConstantCat/operator_equals.html index 8f4103a532..68fef4f80f 100644 --- a/testing/test_package_docs/ex/ConstantCat/operator_equals.html +++ b/testing/test_package_docs/ex/ConstantCat/operator_equals.html @@ -115,7 +115,7 @@

    ConstantCat
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Deprecated/hashCode.html b/testing/test_package_docs/ex/Deprecated/hashCode.html index 2d393e72e8..3c116f094d 100644 --- a/testing/test_package_docs/ex/Deprecated/hashCode.html +++ b/testing/test_package_docs/ex/Deprecated/hashCode.html @@ -107,12 +107,12 @@

    Deprecated

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Deprecated/operator_equals.html b/testing/test_package_docs/ex/Deprecated/operator_equals.html index 0398088484..2ba28d5678 100644 --- a/testing/test_package_docs/ex/Deprecated/operator_equals.html +++ b/testing/test_package_docs/ex/Deprecated/operator_equals.html @@ -113,7 +113,7 @@

    Deprecated
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Dog-class.html b/testing/test_package_docs/ex/Dog-class.html index 7d82d10bca..01a9947c21 100644 --- a/testing/test_package_docs/ex/Dog-class.html +++ b/testing/test_package_docs/ex/Dog-class.html @@ -220,7 +220,7 @@

    Properties

    deprecatedField - → int + ↔ int

    @@ -235,8 +235,9 @@

    Properties

    @deprecated, read-only
    - deprecatedSetter - → int + deprecatedSetter + int value +

    @@ -252,7 +253,7 @@

    Properties

    name - → String + ↔ String

    @@ -409,9 +410,14 @@

    Static Properties

    A tasty static + final property.

    final
    +
    + staticGetterSetter + x + +
    staticGetterSetter - → int + ↔ int

    diff --git a/testing/test_package_docs/ex/Dog/hashCode.html b/testing/test_package_docs/ex/Dog/hashCode.html index 2963aaa398..fcae183b52 100644 --- a/testing/test_package_docs/ex/Dog/hashCode.html +++ b/testing/test_package_docs/ex/Dog/hashCode.html @@ -131,12 +131,12 @@
    Dog

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Dog/operator_equals.html b/testing/test_package_docs/ex/Dog/operator_equals.html index c94cedf92a..721ad0678b 100644 --- a/testing/test_package_docs/ex/Dog/operator_equals.html +++ b/testing/test_package_docs/ex/Dog/operator_equals.html @@ -142,7 +142,7 @@

    Dog
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/E/hashCode.html b/testing/test_package_docs/ex/E/hashCode.html index 9f13ff1d81..7830b694cf 100644 --- a/testing/test_package_docs/ex/E/hashCode.html +++ b/testing/test_package_docs/ex/E/hashCode.html @@ -106,12 +106,12 @@

    E

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/E/operator_equals.html b/testing/test_package_docs/ex/E/operator_equals.html index 7f18a9f51c..2906598cc3 100644 --- a/testing/test_package_docs/ex/E/operator_equals.html +++ b/testing/test_package_docs/ex/E/operator_equals.html @@ -112,7 +112,7 @@

    E
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/F-class.html b/testing/test_package_docs/ex/F-class.html index 3464e6cc22..cdd8b02726 100644 --- a/testing/test_package_docs/ex/F-class.html +++ b/testing/test_package_docs/ex/F-class.html @@ -195,7 +195,7 @@

    Properties

    deprecatedField - → int + ↔ int

    @@ -210,8 +210,9 @@

    Properties

    @deprecated, read-only, inherited
    - deprecatedSetter - → int + deprecatedSetter + int value +

    @@ -235,7 +236,7 @@

    Properties

    name - → String + ↔ String

    diff --git a/testing/test_package_docs/ex/ForAnnotation/hashCode.html b/testing/test_package_docs/ex/ForAnnotation/hashCode.html index 835387950a..52aba875c2 100644 --- a/testing/test_package_docs/ex/ForAnnotation/hashCode.html +++ b/testing/test_package_docs/ex/ForAnnotation/hashCode.html @@ -107,12 +107,12 @@
    ForAnnotation

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/ForAnnotation/operator_equals.html b/testing/test_package_docs/ex/ForAnnotation/operator_equals.html index ba5bd8d4f2..7904e47f03 100644 --- a/testing/test_package_docs/ex/ForAnnotation/operator_equals.html +++ b/testing/test_package_docs/ex/ForAnnotation/operator_equals.html @@ -113,7 +113,7 @@

    ForAnnotation
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/HasAnnotation/hashCode.html b/testing/test_package_docs/ex/HasAnnotation/hashCode.html index a231dd1cba..c711b3d424 100644 --- a/testing/test_package_docs/ex/HasAnnotation/hashCode.html +++ b/testing/test_package_docs/ex/HasAnnotation/hashCode.html @@ -106,12 +106,12 @@

    HasAnnotation

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/HasAnnotation/operator_equals.html b/testing/test_package_docs/ex/HasAnnotation/operator_equals.html index 6e87e683ae..2b56344e51 100644 --- a/testing/test_package_docs/ex/HasAnnotation/operator_equals.html +++ b/testing/test_package_docs/ex/HasAnnotation/operator_equals.html @@ -112,7 +112,7 @@

    HasAnnotation
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Helper-class.html b/testing/test_package_docs/ex/Helper-class.html index 76cf0cd2c6..c1b4701a78 100644 --- a/testing/test_package_docs/ex/Helper-class.html +++ b/testing/test_package_docs/ex/Helper-class.html @@ -139,7 +139,7 @@

    ex

    Even unresolved references in the same library should be resolved Apple -B

    +ex.B

    diff --git a/testing/test_package_docs/ex/Helper/hashCode.html b/testing/test_package_docs/ex/Helper/hashCode.html index 2abda39210..5d3f3f10da 100644 --- a/testing/test_package_docs/ex/Helper/hashCode.html +++ b/testing/test_package_docs/ex/Helper/hashCode.html @@ -107,12 +107,12 @@
    Helper

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Helper/operator_equals.html b/testing/test_package_docs/ex/Helper/operator_equals.html index 96158da057..1c5c87659c 100644 --- a/testing/test_package_docs/ex/Helper/operator_equals.html +++ b/testing/test_package_docs/ex/Helper/operator_equals.html @@ -113,7 +113,7 @@

    Helper
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Klass/hashCode.html b/testing/test_package_docs/ex/Klass/hashCode.html index 6bc06e31f5..7183b9df02 100644 --- a/testing/test_package_docs/ex/Klass/hashCode.html +++ b/testing/test_package_docs/ex/Klass/hashCode.html @@ -111,12 +111,12 @@

    Klass

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Klass/operator_equals.html b/testing/test_package_docs/ex/Klass/operator_equals.html index 57cf125c6d..ed5ca14dee 100644 --- a/testing/test_package_docs/ex/Klass/operator_equals.html +++ b/testing/test_package_docs/ex/Klass/operator_equals.html @@ -117,7 +117,7 @@

    Klass
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/MyError/hashCode.html b/testing/test_package_docs/ex/MyError/hashCode.html index af1ab6d8e3..ec1d9d2d9b 100644 --- a/testing/test_package_docs/ex/MyError/hashCode.html +++ b/testing/test_package_docs/ex/MyError/hashCode.html @@ -107,12 +107,12 @@

    MyError

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/MyError/operator_equals.html b/testing/test_package_docs/ex/MyError/operator_equals.html index 71a8acf70e..b225eb6940 100644 --- a/testing/test_package_docs/ex/MyError/operator_equals.html +++ b/testing/test_package_docs/ex/MyError/operator_equals.html @@ -113,7 +113,7 @@

    MyError
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/MyErrorImplements/hashCode.html b/testing/test_package_docs/ex/MyErrorImplements/hashCode.html index f91f5fe1d7..2512c0642b 100644 --- a/testing/test_package_docs/ex/MyErrorImplements/hashCode.html +++ b/testing/test_package_docs/ex/MyErrorImplements/hashCode.html @@ -107,12 +107,12 @@

    MyErrorImplements

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/MyErrorImplements/operator_equals.html b/testing/test_package_docs/ex/MyErrorImplements/operator_equals.html index 7b2d61d4bd..bafe071e36 100644 --- a/testing/test_package_docs/ex/MyErrorImplements/operator_equals.html +++ b/testing/test_package_docs/ex/MyErrorImplements/operator_equals.html @@ -113,7 +113,7 @@

    MyErrorImplements
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/MyException/hashCode.html b/testing/test_package_docs/ex/MyException/hashCode.html index 94cf102186..cc9f542ab9 100644 --- a/testing/test_package_docs/ex/MyException/hashCode.html +++ b/testing/test_package_docs/ex/MyException/hashCode.html @@ -106,12 +106,12 @@

    MyException

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/MyException/operator_equals.html b/testing/test_package_docs/ex/MyException/operator_equals.html index 74c4d6088d..af1216230d 100644 --- a/testing/test_package_docs/ex/MyException/operator_equals.html +++ b/testing/test_package_docs/ex/MyException/operator_equals.html @@ -112,7 +112,7 @@

    MyException
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/MyExceptionImplements/hashCode.html b/testing/test_package_docs/ex/MyExceptionImplements/hashCode.html index df4606aff1..9dcf8dc4e1 100644 --- a/testing/test_package_docs/ex/MyExceptionImplements/hashCode.html +++ b/testing/test_package_docs/ex/MyExceptionImplements/hashCode.html @@ -106,12 +106,12 @@

    MyExceptionImplements

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/MyExceptionImplements/operator_equals.html b/testing/test_package_docs/ex/MyExceptionImplements/operator_equals.html index 4615901bdb..076faa4f31 100644 --- a/testing/test_package_docs/ex/MyExceptionImplements/operator_equals.html +++ b/testing/test_package_docs/ex/MyExceptionImplements/operator_equals.html @@ -112,7 +112,7 @@

    MyExceptionImplements
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/hashCode.html b/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/hashCode.html index b74a5e2629..488af7d7eb 100644 --- a/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/hashCode.html +++ b/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/hashCode.html @@ -107,12 +107,12 @@

    PublicClassExtendsPri

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/operator_equals.html b/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/operator_equals.html index 0c423bdf0d..a1f35950f8 100644 --- a/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/operator_equals.html +++ b/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/operator_equals.html @@ -113,7 +113,7 @@

    PublicClassExtendsPri either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/hashCode.html b/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/hashCode.html index 42bcd3519d..28876f4138 100644 --- a/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/hashCode.html +++ b/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/hashCode.html @@ -107,12 +107,12 @@

    PublicClassImp

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/operator_equals.html b/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/operator_equals.html index 3031490b27..09a75a047f 100644 --- a/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/operator_equals.html +++ b/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/operator_equals.html @@ -113,7 +113,7 @@

    PublicClassImp either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/ShapeType/hashCode.html b/testing/test_package_docs/ex/ShapeType/hashCode.html index 329e5e4ccf..7be697f8c0 100644 --- a/testing/test_package_docs/ex/ShapeType/hashCode.html +++ b/testing/test_package_docs/ex/ShapeType/hashCode.html @@ -108,12 +108,12 @@

    ShapeType

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/ShapeType/operator_equals.html b/testing/test_package_docs/ex/ShapeType/operator_equals.html index 34b93923e3..6ecb649626 100644 --- a/testing/test_package_docs/ex/ShapeType/operator_equals.html +++ b/testing/test_package_docs/ex/ShapeType/operator_equals.html @@ -114,7 +114,7 @@

    ShapeType
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/SpecializedDuration/hashCode.html b/testing/test_package_docs/ex/SpecializedDuration/hashCode.html index 7569eebd4f..8fd78415ca 100644 --- a/testing/test_package_docs/ex/SpecializedDuration/hashCode.html +++ b/testing/test_package_docs/ex/SpecializedDuration/hashCode.html @@ -124,12 +124,12 @@

    SpecializedDuration

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/WithGeneric-class.html b/testing/test_package_docs/ex/WithGeneric-class.html index f295f681fc..f1e4adb7fa 100644 --- a/testing/test_package_docs/ex/WithGeneric-class.html +++ b/testing/test_package_docs/ex/WithGeneric-class.html @@ -169,7 +169,7 @@

    Properties

    prop - → T + ↔ T

    diff --git a/testing/test_package_docs/ex/WithGeneric/hashCode.html b/testing/test_package_docs/ex/WithGeneric/hashCode.html index 6d74340fbb..e867de5dbd 100644 --- a/testing/test_package_docs/ex/WithGeneric/hashCode.html +++ b/testing/test_package_docs/ex/WithGeneric/hashCode.html @@ -107,12 +107,12 @@
    WithGeneric

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/WithGeneric/operator_equals.html b/testing/test_package_docs/ex/WithGeneric/operator_equals.html index 4b5e566c20..e3103d8bf2 100644 --- a/testing/test_package_docs/ex/WithGeneric/operator_equals.html +++ b/testing/test_package_docs/ex/WithGeneric/operator_equals.html @@ -113,7 +113,7 @@

    WithGeneric
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/WithGenericSub-class.html b/testing/test_package_docs/ex/WithGenericSub-class.html index 4326ef2f9c..68dd03019c 100644 --- a/testing/test_package_docs/ex/WithGenericSub-class.html +++ b/testing/test_package_docs/ex/WithGenericSub-class.html @@ -179,7 +179,7 @@

    Properties

    prop - Apple + ↔ T

    diff --git a/testing/test_package_docs/ex/aThingToDo/hashCode.html b/testing/test_package_docs/ex/aThingToDo/hashCode.html index d6308badbb..69f6f0f7d7 100644 --- a/testing/test_package_docs/ex/aThingToDo/hashCode.html +++ b/testing/test_package_docs/ex/aThingToDo/hashCode.html @@ -108,12 +108,12 @@
    aThingToDo

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/aThingToDo/operator_equals.html b/testing/test_package_docs/ex/aThingToDo/operator_equals.html index 0f1e1caf02..faeb52bc1e 100644 --- a/testing/test_package_docs/ex/aThingToDo/operator_equals.html +++ b/testing/test_package_docs/ex/aThingToDo/operator_equals.html @@ -114,7 +114,7 @@

    aThingToDo
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/ex-library.html b/testing/test_package_docs/ex/ex-library.html index c388687b9e..5bdc4d055b 100644 --- a/testing/test_package_docs/ex/ex-library.html +++ b/testing/test_package_docs/ex/ex-library.html @@ -182,7 +182,7 @@

    Classes

    Even unresolved references in the same library should be resolved Apple -B

    +ex.B

    Klass @@ -342,7 +342,7 @@

    Properties

    deprecatedField - → int + ↔ int

    @@ -357,8 +357,9 @@

    Properties

    read-only
    - deprecatedSetter - → int + deprecatedSetter + int value +

    @@ -366,7 +367,7 @@

    Properties

    number - → double + ↔ double

    diff --git a/testing/test_package_docs/fake/Annotation/hashCode.html b/testing/test_package_docs/fake/Annotation/hashCode.html index 1806621bf3..07a3f14239 100644 --- a/testing/test_package_docs/fake/Annotation/hashCode.html +++ b/testing/test_package_docs/fake/Annotation/hashCode.html @@ -107,12 +107,12 @@
    Annotation

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Annotation/operator_equals.html b/testing/test_package_docs/fake/Annotation/operator_equals.html index a4b54588cb..99d25aeefd 100644 --- a/testing/test_package_docs/fake/Annotation/operator_equals.html +++ b/testing/test_package_docs/fake/Annotation/operator_equals.html @@ -113,7 +113,7 @@

    Annotation
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/AnotherInterface/hashCode.html b/testing/test_package_docs/fake/AnotherInterface/hashCode.html index 48c2785f18..c330a55865 100644 --- a/testing/test_package_docs/fake/AnotherInterface/hashCode.html +++ b/testing/test_package_docs/fake/AnotherInterface/hashCode.html @@ -106,12 +106,12 @@

    AnotherInterface

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/AnotherInterface/operator_equals.html b/testing/test_package_docs/fake/AnotherInterface/operator_equals.html index 5f5e167613..0be4a23da6 100644 --- a/testing/test_package_docs/fake/AnotherInterface/operator_equals.html +++ b/testing/test_package_docs/fake/AnotherInterface/operator_equals.html @@ -112,7 +112,7 @@

    AnotherInterface
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html b/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html index 2f8f68f34c..365fb6b169 100644 --- a/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html +++ b/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html @@ -115,7 +115,7 @@

    BaseForDocComments

    Reference to a top-level const in another library incorrectDocReferenceFromEx

    Reference to prefixed-name from another lib css.theOnlyThingInTheLibrary xx

    Reference to a name that exists in this package, but is not imported -in this library doesStuff xx

    +in this library doesStuff xx

    Reference to a name of a class from an import of a library that exported the name BaseClass xx

    diff --git a/testing/test_package_docs/fake/BaseForDocComments/hashCode.html b/testing/test_package_docs/fake/BaseForDocComments/hashCode.html index a12e2b2273..a6dc06e724 100644 --- a/testing/test_package_docs/fake/BaseForDocComments/hashCode.html +++ b/testing/test_package_docs/fake/BaseForDocComments/hashCode.html @@ -108,12 +108,12 @@
    BaseForDocComments

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/BaseForDocComments/operator_equals.html b/testing/test_package_docs/fake/BaseForDocComments/operator_equals.html index 89b435345c..5a5774430a 100644 --- a/testing/test_package_docs/fake/BaseForDocComments/operator_equals.html +++ b/testing/test_package_docs/fake/BaseForDocComments/operator_equals.html @@ -114,7 +114,7 @@

    BaseForDocComments
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Color/hashCode.html b/testing/test_package_docs/fake/Color/hashCode.html index b424393d1a..0bda804355 100644 --- a/testing/test_package_docs/fake/Color/hashCode.html +++ b/testing/test_package_docs/fake/Color/hashCode.html @@ -114,12 +114,12 @@

    Color

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Color/operator_equals.html b/testing/test_package_docs/fake/Color/operator_equals.html index af37750aa7..66fd402bf5 100644 --- a/testing/test_package_docs/fake/Color/operator_equals.html +++ b/testing/test_package_docs/fake/Color/operator_equals.html @@ -120,7 +120,7 @@

    Color
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/ConstantClass/hashCode.html b/testing/test_package_docs/fake/ConstantClass/hashCode.html index e443b5ddb6..d49d3ec4da 100644 --- a/testing/test_package_docs/fake/ConstantClass/hashCode.html +++ b/testing/test_package_docs/fake/ConstantClass/hashCode.html @@ -109,12 +109,12 @@

    ConstantClass

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/ConstantClass/operator_equals.html b/testing/test_package_docs/fake/ConstantClass/operator_equals.html index 7afb37377f..dabf509f65 100644 --- a/testing/test_package_docs/fake/ConstantClass/operator_equals.html +++ b/testing/test_package_docs/fake/ConstantClass/operator_equals.html @@ -115,7 +115,7 @@

    ConstantClass
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Cool/hashCode.html b/testing/test_package_docs/fake/Cool/hashCode.html index 83b5b27fb2..0de602ac67 100644 --- a/testing/test_package_docs/fake/Cool/hashCode.html +++ b/testing/test_package_docs/fake/Cool/hashCode.html @@ -107,12 +107,12 @@

    Cool

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Cool/operator_equals.html b/testing/test_package_docs/fake/Cool/operator_equals.html index 30cd768a6e..d868c1636f 100644 --- a/testing/test_package_docs/fake/Cool/operator_equals.html +++ b/testing/test_package_docs/fake/Cool/operator_equals.html @@ -113,7 +113,7 @@

    Cool
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Doh/hashCode.html b/testing/test_package_docs/fake/Doh/hashCode.html index b92cc8244b..b8c976bb00 100644 --- a/testing/test_package_docs/fake/Doh/hashCode.html +++ b/testing/test_package_docs/fake/Doh/hashCode.html @@ -107,12 +107,12 @@

    Doh

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Doh/operator_equals.html b/testing/test_package_docs/fake/Doh/operator_equals.html index 37e6164651..1e530a7feb 100644 --- a/testing/test_package_docs/fake/Doh/operator_equals.html +++ b/testing/test_package_docs/fake/Doh/operator_equals.html @@ -113,7 +113,7 @@

    Doh
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/ExtraSpecialList-class.html b/testing/test_package_docs/fake/ExtraSpecialList-class.html index 61194114c3..926ea2ba95 100644 --- a/testing/test_package_docs/fake/ExtraSpecialList-class.html +++ b/testing/test_package_docs/fake/ExtraSpecialList-class.html @@ -188,7 +188,7 @@

    Properties

    first - → dynamic + → E

    @@ -220,7 +220,7 @@

    Properties

    iterator - → Iterator + → Iterator<E>

    @@ -228,15 +228,20 @@

    Properties

    last - → dynamic + → E

    read-only, inherited
    +
    + length + int length + +
    length - → int + ↔ int

    @@ -244,7 +249,7 @@

    Properties

    reversed - → Iterable + → Iterable<E>

    @@ -260,7 +265,7 @@

    Properties

    single - → dynamic + → E

    @@ -273,7 +278,7 @@

    Properties

    Methods

    - add(element) + add(E element) → void
    @@ -283,7 +288,7 @@

    Methods

    inherited
    - addAll(Iterable iterable) + addAll(Iterable<E> iterable) → void
    @@ -302,7 +307,7 @@

    Methods

    asMap() - → Map<int, dynamic> + → Map<int, E>
    @@ -330,7 +335,7 @@

    Methods

    elementAt(int index) - → dynamic + → E
    @@ -356,7 +361,7 @@

    Methods

    inherited
    - fillRange(int start, int end, [ fill ]) + fillRange(int start, int end, [ E fill ]) → void
    @@ -366,8 +371,8 @@

    Methods

    inherited
    - firstWhere(bool test(E element), { dynamic orElse() }) - → dynamic + firstWhere(bool test(E element), { E orElse() }) + → E
    @@ -396,7 +401,7 @@

    Methods

    getRange(int start, int end) - → Iterable + → Iterable<E>
    @@ -414,7 +419,7 @@

    Methods

    inherited
    - insert(int index, element) + insert(int index, E element) → void
    @@ -423,7 +428,7 @@

    Methods

    inherited
    - insertAll(int index, Iterable iterable) + insertAll(int index, Iterable<E> iterable) → void
    @@ -452,8 +457,8 @@

    Methods

    inherited
    - lastWhere(bool test(E element), { dynamic orElse() }) - → dynamic + lastWhere(bool test(E element), { E orElse() }) + → E
    @@ -480,8 +485,8 @@

    Methods

    inherited
    - reduce(dynamic combine(E previousValue, E element)) - → dynamic + reduce(E combine(E previousValue, E element)) + → E
    @@ -500,7 +505,7 @@

    Methods

    removeAt(int index) - → dynamic + → E
    @@ -509,7 +514,7 @@

    Methods

    removeLast() - → dynamic + → E
    @@ -535,7 +540,7 @@

    Methods

    inherited
    - replaceRange(int start, int end, Iterable newContents) + replaceRange(int start, int end, Iterable<E> newContents) → void
    @@ -554,7 +559,7 @@

    Methods

    inherited
    - setAll(int index, Iterable iterable) + setAll(int index, Iterable<E> iterable) → void
    @@ -564,7 +569,7 @@

    Methods

    inherited
    - setRange(int start, int end, Iterable iterable, [ int skipCount = 0 ]) + setRange(int start, int end, Iterable<E> iterable, [ int skipCount = 0 ]) → void
    @@ -584,7 +589,7 @@

    Methods

    singleWhere(bool test(E element)) - → dynamic + → E
    @@ -593,7 +598,7 @@

    Methods

    skip(int count) - → Iterable + → Iterable<E>
    @@ -602,7 +607,7 @@

    Methods

    skipWhile(bool test(E element)) - → Iterable + → Iterable<E>
    @@ -620,7 +625,7 @@

    Methods

    sublist(int start, [ int end ]) - → List + → List<E>
    @@ -630,7 +635,7 @@

    Methods

    take(int count) - → Iterable + → Iterable<E>
    @@ -639,7 +644,7 @@

    Methods

    takeWhile(bool test(E element)) - → Iterable + → Iterable<E>
    @@ -648,7 +653,7 @@

    Methods

    toList({bool growable: true }) - → List + → List<E>
    @@ -657,7 +662,7 @@

    Methods

    toSet() - → Set + → Set<E>
    @@ -675,7 +680,7 @@

    Methods

    where(bool test(E element)) - → Iterable + → Iterable<E>
    @@ -700,7 +705,7 @@

    Operators

    operator [](int index) - → dynamic + → E
    @@ -708,7 +713,7 @@

    Operators

    inherited
    - operator []=(int index, value) + operator []=(int index, E value) → void
    diff --git a/testing/test_package_docs/fake/Foo2/hashCode.html b/testing/test_package_docs/fake/Foo2/hashCode.html index b3d86e49ef..73622be4bd 100644 --- a/testing/test_package_docs/fake/Foo2/hashCode.html +++ b/testing/test_package_docs/fake/Foo2/hashCode.html @@ -110,12 +110,12 @@
    Foo2

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Foo2/operator_equals.html b/testing/test_package_docs/fake/Foo2/operator_equals.html index 2aaeb6975b..ca93fc4e93 100644 --- a/testing/test_package_docs/fake/Foo2/operator_equals.html +++ b/testing/test_package_docs/fake/Foo2/operator_equals.html @@ -116,7 +116,7 @@

    Foo2
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/HasGenericWithExtends/hashCode.html b/testing/test_package_docs/fake/HasGenericWithExtends/hashCode.html index 44adfcb2bd..841877474d 100644 --- a/testing/test_package_docs/fake/HasGenericWithExtends/hashCode.html +++ b/testing/test_package_docs/fake/HasGenericWithExtends/hashCode.html @@ -106,12 +106,12 @@

    HasGenericWithExtendsThe hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/HasGenericWithExtends/operator_equals.html b/testing/test_package_docs/fake/HasGenericWithExtends/operator_equals.html index 279a7fefea..cbf50282c9 100644 --- a/testing/test_package_docs/fake/HasGenericWithExtends/operator_equals.html +++ b/testing/test_package_docs/fake/HasGenericWithExtends/operator_equals.html @@ -112,7 +112,7 @@

    HasGenericWithExtends
  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/HasGenerics/hashCode.html b/testing/test_package_docs/fake/HasGenerics/hashCode.html index b4ea3cd76d..95e72d7632 100644 --- a/testing/test_package_docs/fake/HasGenerics/hashCode.html +++ b/testing/test_package_docs/fake/HasGenerics/hashCode.html @@ -110,12 +110,12 @@

    HasGenerics

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/HasGenerics/operator_equals.html b/testing/test_package_docs/fake/HasGenerics/operator_equals.html index 4213c97e67..dedc508f66 100644 --- a/testing/test_package_docs/fake/HasGenerics/operator_equals.html +++ b/testing/test_package_docs/fake/HasGenerics/operator_equals.html @@ -116,7 +116,7 @@

    HasGenerics
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Interface/hashCode.html b/testing/test_package_docs/fake/Interface/hashCode.html index 8e62d1a8dd..2b33b03215 100644 --- a/testing/test_package_docs/fake/Interface/hashCode.html +++ b/testing/test_package_docs/fake/Interface/hashCode.html @@ -106,12 +106,12 @@

    Interface

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Interface/operator_equals.html b/testing/test_package_docs/fake/Interface/operator_equals.html index 4879cf49e3..7f30bb524a 100644 --- a/testing/test_package_docs/fake/Interface/operator_equals.html +++ b/testing/test_package_docs/fake/Interface/operator_equals.html @@ -112,7 +112,7 @@

    Interface
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/LongFirstLine-class.html b/testing/test_package_docs/fake/LongFirstLine-class.html index ab354b6086..679fd26adc 100644 --- a/testing/test_package_docs/fake/LongFirstLine-class.html +++ b/testing/test_package_docs/fake/LongFirstLine-class.html @@ -219,7 +219,7 @@

    Properties

    aStringProperty - → String + ↔ String

    An instance string property. Readable and writable.

    @@ -234,8 +234,9 @@

    Properties

    read-only
    - onlySetter - → double + onlySetter + double d +

    Only a setter, with a single param, of type double.

    @@ -251,7 +252,7 @@

    Properties

    powers - → List<String> + ↔ List<String>

    In the super class.

    @@ -385,7 +386,7 @@

    Static Properties

    meaningOfLife - → int + ↔ int

    A static int property.

    @@ -400,8 +401,9 @@

    Static Properties

    read-only
    - staticOnlySetter - → bool + staticOnlySetter + bool thing +

    diff --git a/testing/test_package_docs/fake/MixMeIn/hashCode.html b/testing/test_package_docs/fake/MixMeIn/hashCode.html index 4783f47420..a9fc3cf529 100644 --- a/testing/test_package_docs/fake/MixMeIn/hashCode.html +++ b/testing/test_package_docs/fake/MixMeIn/hashCode.html @@ -106,12 +106,12 @@
    MixMeIn

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/MixMeIn/operator_equals.html b/testing/test_package_docs/fake/MixMeIn/operator_equals.html index c83eebb85a..182d3f86d6 100644 --- a/testing/test_package_docs/fake/MixMeIn/operator_equals.html +++ b/testing/test_package_docs/fake/MixMeIn/operator_equals.html @@ -112,7 +112,7 @@

    MixMeIn
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Oops/hashCode.html b/testing/test_package_docs/fake/Oops/hashCode.html index 83e358e7b0..d5b137cec2 100644 --- a/testing/test_package_docs/fake/Oops/hashCode.html +++ b/testing/test_package_docs/fake/Oops/hashCode.html @@ -107,12 +107,12 @@

    Oops

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Oops/operator_equals.html b/testing/test_package_docs/fake/Oops/operator_equals.html index 995f457827..16d8174e85 100644 --- a/testing/test_package_docs/fake/Oops/operator_equals.html +++ b/testing/test_package_docs/fake/Oops/operator_equals.html @@ -113,7 +113,7 @@

    Oops
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/OperatorReferenceClass/hashCode.html b/testing/test_package_docs/fake/OperatorReferenceClass/hashCode.html index 36f7898467..190a350509 100644 --- a/testing/test_package_docs/fake/OperatorReferenceClass/hashCode.html +++ b/testing/test_package_docs/fake/OperatorReferenceClass/hashCode.html @@ -106,12 +106,12 @@

    OperatorReferenceClass<

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/OperatorReferenceClass/operator_equals.html b/testing/test_package_docs/fake/OperatorReferenceClass/operator_equals.html index a1d337e8ae..01a09e2290 100644 --- a/testing/test_package_docs/fake/OperatorReferenceClass/operator_equals.html +++ b/testing/test_package_docs/fake/OperatorReferenceClass/operator_equals.html @@ -117,7 +117,7 @@

    OperatorReferenceClass< either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/OtherGenericsThing/hashCode.html b/testing/test_package_docs/fake/OtherGenericsThing/hashCode.html index 402046c294..ced19403b1 100644 --- a/testing/test_package_docs/fake/OtherGenericsThing/hashCode.html +++ b/testing/test_package_docs/fake/OtherGenericsThing/hashCode.html @@ -107,12 +107,12 @@

    OtherGenericsThing

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/OtherGenericsThing/operator_equals.html b/testing/test_package_docs/fake/OtherGenericsThing/operator_equals.html index 9d3b4f731b..ac1672c33b 100644 --- a/testing/test_package_docs/fake/OtherGenericsThing/operator_equals.html +++ b/testing/test_package_docs/fake/OtherGenericsThing/operator_equals.html @@ -113,7 +113,7 @@

    OtherGenericsThing
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/SpecialList-class.html b/testing/test_package_docs/fake/SpecialList-class.html index 24d2dfc6b9..f9b0e0c43a 100644 --- a/testing/test_package_docs/fake/SpecialList-class.html +++ b/testing/test_package_docs/fake/SpecialList-class.html @@ -189,9 +189,14 @@

    Constructors

    Properties

    +
    + length + int length + +
    length - → int + ↔ int

    diff --git a/testing/test_package_docs/fake/SpecialList/contains.html b/testing/test_package_docs/fake/SpecialList/contains.html index 97f01065b6..4503a938a3 100644 --- a/testing/test_package_docs/fake/SpecialList/contains.html +++ b/testing/test_package_docs/fake/SpecialList/contains.html @@ -159,7 +159,7 @@
    SpecialList
    the iterable defaults to the Object.== of the element.

    Some types of iterable may have a different equality used for its elements. For example, a Set may have a custom equality -(see Set.identical) that its contains uses. +(see Set.identity) that its contains uses. Likewise the Iterable returned by a Map.keys call should use the same equality that the Map uses for keys.

    diff --git a/testing/test_package_docs/fake/SpecialList/elementAt.html b/testing/test_package_docs/fake/SpecialList/elementAt.html index c8ab943294..5fff7cdb5a 100644 --- a/testing/test_package_docs/fake/SpecialList/elementAt.html +++ b/testing/test_package_docs/fake/SpecialList/elementAt.html @@ -152,7 +152,7 @@
    SpecialList

    Returns the indexth element.

    -

    The index must be non-negative and less than length. +

    The index must be non-negative and less than length. Index zero represents the first element (so iterable.elementAt(0) is equivalent to iterable.first).

    May iterate through the elements in iteration order, skipping the diff --git a/testing/test_package_docs/fake/SpecialList/hashCode.html b/testing/test_package_docs/fake/SpecialList/hashCode.html index 23578081d8..3d9ffab70b 100644 --- a/testing/test_package_docs/fake/SpecialList/hashCode.html +++ b/testing/test_package_docs/fake/SpecialList/hashCode.html @@ -158,12 +158,12 @@

    SpecialList

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/SpecialList/operator_equals.html b/testing/test_package_docs/fake/SpecialList/operator_equals.html index 8eafef8060..d0afe1f01c 100644 --- a/testing/test_package_docs/fake/SpecialList/operator_equals.html +++ b/testing/test_package_docs/fake/SpecialList/operator_equals.html @@ -164,7 +164,7 @@

    SpecialList
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/SpecialList/setAll.html b/testing/test_package_docs/fake/SpecialList/setAll.html index 9bd51922be..6981d6a7d6 100644 --- a/testing/test_package_docs/fake/SpecialList/setAll.html +++ b/testing/test_package_docs/fake/SpecialList/setAll.html @@ -158,9 +158,9 @@

    SpecialList
    list.join(', '); // 'a, bee, sea'

    This operation does not increase the length of this.

    -

    The index must be non-negative and no greater than length.

    +

    The index must be non-negative and no greater than length.

    The iterable must not have more elements than what can fit from index -to length.

    +to length.

    If iterable is based on this list, its values may change /during/ the setAll operation.

    diff --git a/testing/test_package_docs/fake/SpecialList/sublist.html b/testing/test_package_docs/fake/SpecialList/sublist.html index bf6a57eaa2..764cc2a2cc 100644 --- a/testing/test_package_docs/fake/SpecialList/sublist.html +++ b/testing/test_package_docs/fake/SpecialList/sublist.html @@ -156,7 +156,7 @@
    SpecialList
    List<String> colors = ['red', 'green', 'blue', 'orange', 'pink'];
     colors.sublist(1, 3); // ['green', 'blue']
     
    -

    If end is omitted, the length of this is used.

    +

    If end is omitted, the length of this is used.

    colors.sublist(1);  // ['green', 'blue', 'orange', 'pink']
     

    An error occurs if start is outside the range 0 .. length or if diff --git a/testing/test_package_docs/fake/SuperAwesomeClass-class.html b/testing/test_package_docs/fake/SuperAwesomeClass-class.html index 6b6e7239d6..ed954de849 100644 --- a/testing/test_package_docs/fake/SuperAwesomeClass-class.html +++ b/testing/test_package_docs/fake/SuperAwesomeClass-class.html @@ -189,7 +189,7 @@

    Properties

    powers - → List<String> + ↔ List<String>

    In the super class.

    diff --git a/testing/test_package_docs/fake/SuperAwesomeClass/hashCode.html b/testing/test_package_docs/fake/SuperAwesomeClass/hashCode.html index fa91a26cfa..15cbf7b5bf 100644 --- a/testing/test_package_docs/fake/SuperAwesomeClass/hashCode.html +++ b/testing/test_package_docs/fake/SuperAwesomeClass/hashCode.html @@ -109,12 +109,12 @@
    SuperAwesomeClass

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/SuperAwesomeClass/operator_equals.html b/testing/test_package_docs/fake/SuperAwesomeClass/operator_equals.html index 6b73e7c63d..3465381b81 100644 --- a/testing/test_package_docs/fake/SuperAwesomeClass/operator_equals.html +++ b/testing/test_package_docs/fake/SuperAwesomeClass/operator_equals.html @@ -115,7 +115,7 @@

    SuperAwesomeClass
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/WithGetterAndSetter-class.html b/testing/test_package_docs/fake/WithGetterAndSetter-class.html index bdecb1d22d..a2f0daa109 100644 --- a/testing/test_package_docs/fake/WithGetterAndSetter-class.html +++ b/testing/test_package_docs/fake/WithGetterAndSetter-class.html @@ -183,9 +183,14 @@

    Constructors

    Properties

    +
    + lengthX + int _length + +
    lengthX - → int + ↔ int

    Returns a length.

    diff --git a/testing/test_package_docs/fake/WithGetterAndSetter/hashCode.html b/testing/test_package_docs/fake/WithGetterAndSetter/hashCode.html index 80b405d04d..9070fa6fd3 100644 --- a/testing/test_package_docs/fake/WithGetterAndSetter/hashCode.html +++ b/testing/test_package_docs/fake/WithGetterAndSetter/hashCode.html @@ -107,12 +107,12 @@
    WithGetterAndSetter

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/WithGetterAndSetter/operator_equals.html b/testing/test_package_docs/fake/WithGetterAndSetter/operator_equals.html index dc53a7d481..ce60d29ea7 100644 --- a/testing/test_package_docs/fake/WithGetterAndSetter/operator_equals.html +++ b/testing/test_package_docs/fake/WithGetterAndSetter/operator_equals.html @@ -113,7 +113,7 @@

    WithGetterAndSetter
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/fake-library.html b/testing/test_package_docs/fake/fake-library.html index 0817b2c394..a72bc27484 100644 --- a/testing/test_package_docs/fake/fake-library.html +++ b/testing/test_package_docs/fake/fake-library.html @@ -390,8 +390,9 @@

    Properties

    read-only
    - justSetter - → int + justSetter + int value +

    Just a setter. No partner getter.

    @@ -399,7 +400,7 @@

    Properties

    mapWithDynamicKeys - → Map<dynamic, String> + ↔ Map<dynamic, String>

    @@ -413,9 +414,14 @@

    Properties

    Final property.

    final
    +
    + setAndGet + String thing + +
    setAndGet - → String + ↔ String

    The getter for setAndGet.

    @@ -423,7 +429,7 @@

    Properties

    simpleProperty - → String + ↔ String

    Simple property

    diff --git a/testing/test_package_docs/index.html b/testing/test_package_docs/index.html index 74245fbfe1..53ad189993 100644 --- a/testing/test_package_docs/index.html +++ b/testing/test_package_docs/index.html @@ -4,7 +4,7 @@ - + test_package - Dart API docs diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass/hashCode.html b/testing/test_package_docs/test_package_imported.main/Whataclass/hashCode.html index d2a3b8f0c3..e2b614e22e 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass/hashCode.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass/hashCode.html @@ -106,12 +106,12 @@
    WhataclassThe hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass/noSuchMethod.html b/testing/test_package_docs/test_package_imported.main/Whataclass/noSuchMethod.html index 3ff5fcdec7..a496e89654 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass/noSuchMethod.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass/noSuchMethod.html @@ -100,7 +100,7 @@

    Whataclass

    Invoked when a non-existent method or property is accessed.

    -

    Classes can override noSuchMethod to provide custom behavior.

    +

    Classes can override noSuchMethod to provide custom behavior.

    If a value is returned, it becomes the result of the original invocation.

    The default behavior is to throw a NoSuchMethodError.

    diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html b/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html index 9a51e33579..6670115618 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html @@ -112,11 +112,11 @@
    Whataclass
  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override -the hashCode method as well to maintain consistency.

    +the hashCode method as well to maintain consistency.

    diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass2/hashCode.html b/testing/test_package_docs/test_package_imported.main/Whataclass2/hashCode.html index 06cd89b236..223e3c7588 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass2/hashCode.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass2/hashCode.html @@ -106,12 +106,12 @@
    Whataclass2<

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass2/noSuchMethod.html b/testing/test_package_docs/test_package_imported.main/Whataclass2/noSuchMethod.html index 55738eef96..cb1eadf0a4 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass2/noSuchMethod.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass2/noSuchMethod.html @@ -100,7 +100,7 @@

    Whataclass2<

    Invoked when a non-existent method or property is accessed.

    -

    Classes can override noSuchMethod to provide custom behavior.

    +

    Classes can override noSuchMethod to provide custom behavior.

    If a value is returned, it becomes the result of the original invocation.

    The default behavior is to throw a NoSuchMethodError.

    diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html b/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html index bef098f517..e7acb8e7bf 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html @@ -112,11 +112,11 @@
    Whataclass2< either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override -the hashCode method as well to maintain consistency.

    +the hashCode method as well to maintain consistency.

    diff --git a/testing/test_package_docs/two_exports/BaseClass-class.html b/testing/test_package_docs/two_exports/BaseClass-class.html index e9db42b99c..2fdca4253d 100644 --- a/testing/test_package_docs/two_exports/BaseClass-class.html +++ b/testing/test_package_docs/two_exports/BaseClass-class.html @@ -135,9 +135,14 @@

    Properties

    The hash code for this object.

    read-only, inherited
    +
    + lengthX + int _length + +
    lengthX - → int + ↔ int

    Returns a length.

    diff --git a/testing/test_package_docs/two_exports/ExtendingClass-class.html b/testing/test_package_docs/two_exports/ExtendingClass-class.html index 8fcca5f4d0..839b97f7fe 100644 --- a/testing/test_package_docs/two_exports/ExtendingClass-class.html +++ b/testing/test_package_docs/two_exports/ExtendingClass-class.html @@ -93,7 +93,7 @@
    two_exports

    Extending class extends BaseClass.

    Also check out topLevelVariable.

    -

    This should not work: linking over to Apple.

    +

    Linking over to Apple should work.

    @@ -137,9 +137,14 @@

    Properties

    The hash code for this object.

    read-only, inherited
    +
    + lengthX + int _length + +
    lengthX - → int + ↔ int

    Returns a length.

    diff --git a/testing/test_package_docs/two_exports/two_exports-library.html b/testing/test_package_docs/two_exports/two_exports-library.html index 3fc1e3a2f4..c1c3dd70b0 100644 --- a/testing/test_package_docs/two_exports/two_exports-library.html +++ b/testing/test_package_docs/two_exports/two_exports-library.html @@ -112,7 +112,7 @@

    Properties

    topLevelVariable - → int + ↔ int