Do not show advisories text if no version is affected (#4157)

diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart
index 40d84fd..d0a42f1 100644
--- a/lib/src/command/outdated.dart
+++ b/lib/src/command/outdated.dart
@@ -676,10 +676,31 @@
     }
   }
 
+  List<Advisory> advisoriesWithAffectedVersions(_PackageDetails package) {
+    return package.advisories
+        .where(
+          (advisory) => advisory.affectedVersions
+              .intersection(
+                [
+                  package.current,
+                  package.upgradable,
+                  package.resolvable,
+                  package.latest,
+                ].map((e) => e?._pubspec.version.canonicalizedVersion).toSet(),
+              )
+              .isNotEmpty,
+        )
+        .toList();
+  }
+
+  var advisoriesToDisplay = <String, List<Advisory>>{};
+  for (final package in rows) {
+    advisoriesToDisplay[package.name] = advisoriesWithAffectedVersions(package);
+  }
   bool displayExtraInfo(_PackageDetails package) =>
       package.isDiscontinued ||
       package.isCurrentRetracted ||
-      (package.advisories.isNotEmpty);
+      (advisoriesToDisplay[package.name]!.isNotEmpty);
 
   if (rows.any(displayExtraInfo)) {
     log.message('\n');
@@ -700,8 +721,9 @@
           'See https://dart.dev/go/package-retraction',
         );
       }
-      if (package.advisories.isNotEmpty) {
-        final advisoriesText = package.advisories.length > 1
+      var displayedAdvisories = advisoriesToDisplay[package.name]!;
+      if (displayedAdvisories.isNotEmpty) {
+        final advisoriesText = displayedAdvisories.length > 1
             ? 'security advisories'
             : 'a security advisory';
         log.message(
@@ -709,21 +731,16 @@
           'See https://dart.dev//go/pub-security-advisories',
         );
         log.message('\n');
-        for (final advisory in package.advisories) {
-          final displayedVersions = <String>{};
-          for (final versionDetails in [
-            package.current,
-            package.upgradable,
-            package.resolvable,
-            package.latest,
-          ]) {
-            final version =
-                versionDetails?._pubspec.version.canonicalizedVersion;
-            if (version != null &&
-                advisory.affectedVersions.contains(version)) {
-              displayedVersions.add(version);
-            }
-          }
+
+        for (final advisory in displayedAdvisories) {
+          var displayedVersions = advisory.affectedVersions.intersection(
+            [
+              package.current,
+              package.upgradable,
+              package.resolvable,
+              package.latest,
+            ].map((e) => e?._pubspec.version.canonicalizedVersion).toSet(),
+          );
           log.message('    - "${advisory.summary}"');
           log.message('      Affects: ${displayedVersions.join(', ')}');
           log.message('      ${advisoriesDisplayUrl(advisory.id)}');
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index c078cb5..aa8ed7b 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -680,7 +680,7 @@
           'Advisory $id does not contain $packageName among its affected packages.',
         );
       }
-      var affectedVersions = <String>[];
+      final affectedVersions = <String>{};
       final versions = affectedPkg['versions'];
       if (versions is! List) {
         throw FormatException('package versions must be a list');
@@ -1810,7 +1810,7 @@
 /// is retrieved from /api/packages/$package/advisories
 class Advisory {
   String id;
-  List<String> affectedVersions;
+  Set<String> affectedVersions;
   List<String> aliases;
   String summary;
   Advisory(this.id, this.affectedVersions, this.aliases, this.summary);
diff --git a/test/outdated/outdated_test.dart b/test/outdated/outdated_test.dart
index aa8affa..2034ca8 100644
--- a/test/outdated/outdated_test.dart
+++ b/test/outdated/outdated_test.dart
@@ -449,7 +449,32 @@
       advisoryId: 'VXYZ-1234-5678-9101',
       affectedVersions: ['1.0.0'],
     );
+    builder.serve('foo', '1.2.0');
+    await ctx.runOutdatedTests();
+  });
 
+  testWithGolden('do not show advisories if no version is affected',
+      (ctx) async {
+    final builder = await servePackages();
+    builder
+      ..serve('foo', '1.0.0', deps: {'transitive': '^1.0.0'})
+      ..serve('transitive', '1.2.3');
+
+    await d.dir(appPath, [
+      d.pubspec({
+        'name': 'app',
+        'dependencies': {
+          'foo': '^1.0.0',
+        },
+      }),
+    ]).create();
+    await pubGet();
+
+    builder.affectVersionsByAdvisory(
+      packageName: 'foo',
+      advisoryId: 'ABCD-1234-5678-9101',
+      affectedVersions: ['0.1.0'],
+    );
     builder.serve('foo', '1.2.0');
     await ctx.runOutdatedTests();
   });
diff --git a/test/testdata/goldens/outdated/outdated_test/do not show advisories if no version is affected.txt b/test/testdata/goldens/outdated/outdated_test/do not show advisories if no version is affected.txt
new file mode 100644
index 0000000..e19e537
--- /dev/null
+++ b/test/testdata/goldens/outdated/outdated_test/do not show advisories if no version is affected.txt
@@ -0,0 +1,146 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
+$ pub outdated --json
+{
+  "packages": [
+    {
+      "package": "foo",
+      "kind": "direct",
+      "isDiscontinued": false,
+      "isCurrentRetracted": false,
+      "isCurrentAffectedByAdvisory": false,
+      "current": {
+        "version": "1.0.0"
+      },
+      "upgradable": {
+        "version": "1.2.0"
+      },
+      "resolvable": {
+        "version": "1.2.0"
+      },
+      "latest": {
+        "version": "1.2.0"
+      }
+    }
+  ]
+}
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
+$ pub outdated --no-color
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name  Current  Upgradable  Resolvable  Latest  
+
+direct dependencies:
+foo           *1.0.0   1.2.0       1.2.0       1.2.0   
+
+1 upgradable dependency is locked (in pubspec.lock) to an older version.
+To update it, use `dart pub upgrade`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
+$ pub outdated --no-color --no-transitive
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name  Current  Upgradable  Resolvable  Latest  
+
+direct dependencies:
+foo           *1.0.0   1.2.0       1.2.0       1.2.0   
+
+1 upgradable dependency is locked (in pubspec.lock) to an older version.
+To update it, use `dart pub upgrade`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
+$ pub outdated --no-color --up-to-date
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name  Current  Upgradable  Resolvable  Latest  
+
+direct dependencies:
+foo           *1.0.0   1.2.0       1.2.0       1.2.0   
+
+1 upgradable dependency is locked (in pubspec.lock) to an older version.
+To update it, use `dart pub upgrade`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
+$ pub outdated --no-color --prereleases
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name  Current  Upgradable  Resolvable  Latest  
+
+direct dependencies:
+foo           *1.0.0   1.2.0       1.2.0       1.2.0   
+
+1 upgradable dependency is locked (in pubspec.lock) to an older version.
+To update it, use `dart pub upgrade`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
+$ pub outdated --no-color --no-dev-dependencies
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name  Current  Upgradable  Resolvable  Latest  
+
+direct dependencies:
+foo           *1.0.0   1.2.0       1.2.0       1.2.0   
+
+1 upgradable dependency is locked (in pubspec.lock) to an older version.
+To update it, use `dart pub upgrade`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
+$ pub outdated --no-color --no-dependency-overrides
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name  Current  Upgradable  Resolvable  Latest  
+
+direct dependencies:
+foo           *1.0.0   1.2.0       1.2.0       1.2.0   
+
+1 upgradable dependency is locked (in pubspec.lock) to an older version.
+To update it, use `dart pub upgrade`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
+$ pub outdated --json --no-dev-dependencies
+{
+  "packages": [
+    {
+      "package": "foo",
+      "kind": "direct",
+      "isDiscontinued": false,
+      "isCurrentRetracted": false,
+      "isCurrentAffectedByAdvisory": false,
+      "current": {
+        "version": "1.0.0"
+      },
+      "upgradable": {
+        "version": "1.2.0"
+      },
+      "resolvable": {
+        "version": "1.2.0"
+      },
+      "latest": {
+        "version": "1.2.0"
+      }
+    }
+  ]
+}
+