[warning] Records import warnings when initialize api

R=iannucci

Change-Id: Id9674c785ee0da2b26a3ed056f99ad2f8e470203
Reviewed-on: https://chromium-review.googlesource.com/c/infra/luci/recipes-py/+/2363284
Commit-Queue: Yiwei Zhang <yiwzhang@google.com>
Reviewed-by: Robbie Iannucci <iannucci@chromium.org>
diff --git a/README.recipes.md b/README.recipes.md
index 1be0317..ce2802b 100644
--- a/README.recipes.md
+++ b/README.recipes.md
@@ -165,7 +165,7 @@
 
 [DEPS](/recipe_modules/archive/__init__.py#5): [json](#recipe_modules-json), [path](#recipe_modules-path), [platform](#recipe_modules-platform), [python](#recipe_modules-python), [step](#recipe_modules-step)
 
-#### **class [ArchiveApi](/recipe_modules/archive/api.py#8)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [ArchiveApi](/recipe_modules/archive/api.py#8)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 Provides steps to manipulate archive files (tar, zip, etc.).
 
@@ -225,7 +225,7 @@
   Package object.
 ### *recipe_modules* / [assertions](/recipe_modules/assertions)
 
-#### **class [AssertionsApi](/recipe_modules/assertions/api.py#56)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [AssertionsApi](/recipe_modules/assertions/api.py#56)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 Provides access to the assertion methods of the python unittest module.
 
@@ -283,7 +283,7 @@
 `build_pb2.Build` and returns a link title.
 If it returns `None`, the link is not reported. Default link title is build id.
 
-#### **class [BuildbucketApi](/recipe_modules/buildbucket/api.py#29)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [BuildbucketApi](/recipe_modules/buildbucket/api.py#29)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 A module for interacting with buildbucket.
 
@@ -664,7 +664,7 @@
 
 API for interacting with cas client.
 
-#### **class [CasApi](/recipe_modules/cas/api.py#13)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [CasApi](/recipe_modules/cas/api.py#13)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 A module for interacting with cas client.
 
@@ -706,7 +706,7 @@
 Depends on 'cipd' binary available in PATH:
 https://godoc.org/go.chromium.org/luci/cipd/client/cmd/cipd
 
-#### **class [CIPDApi](/recipe_modules/cipd/api.py#202)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [CIPDApi](/recipe_modules/cipd/api.py#202)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 CIPDApi provides basic support for CIPD.
 
@@ -974,7 +974,7 @@
 Returns the CIPDApi.Pin instance.
 ### *recipe_modules* / [commit\_position](/recipe_modules/commit_position)
 
-#### **class [CommitPositionApi](/recipe_modules/commit_position/api.py#10)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [CommitPositionApi](/recipe_modules/commit_position/api.py#10)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 Recipe module providing commit position parsing and formatting.
 
@@ -1014,7 +1014,7 @@
   api.step("cat subdir/foo", ['cat', './foo'])
 ```
 
-#### **class [ContextApi](/recipe_modules/context/api.py#78)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [ContextApi](/recipe_modules/context/api.py#78)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/recipe_modules/context/api.py#108)(self, cwd=None, env_prefixes=None, env_suffixes=None, env=None, infra_steps=None, luciexe=None, realm=None):**
 
@@ -1133,7 +1133,7 @@
 
 [DEPS](/recipe_modules/cq/__init__.py#7): [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
 
-#### **class [CQApi](/recipe_modules/cq/api.py#14)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [CQApi](/recipe_modules/cq/api.py#14)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 This module provides recipe API of LUCI CQ, aka pre-commit testing system.
 
@@ -1263,7 +1263,7 @@
 
 File manipulation (read/write/delete/glob) methods.
 
-#### **class [FileApi](/recipe_modules/file/api.py#83)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [FileApi](/recipe_modules/file/api.py#83)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [compute\_hash](/recipe_modules/file/api.py#182)(self, name, paths, base_path, test_data=''):**
 
@@ -1653,7 +1653,7 @@
 
 Implements in-recipe concurrency via green threads.
 
-#### **class [FuturesApi](/recipe_modules/futures/api.py#42)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [FuturesApi](/recipe_modules/futures/api.py#42)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 Provides access to the Recipe concurrency primitives.
 
@@ -1802,7 +1802,7 @@
 another repo. It is not recommended to use this, and it will be removed in the
 near future.
 
-#### **class [GeneratorScriptApi](/recipe_modules/generator_script/api.py#16)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [GeneratorScriptApi](/recipe_modules/generator_script/api.py#16)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [\_\_call\_\_](/recipe_modules/generator_script/api.py#44)(self, path_to_script, \*args):**
 
@@ -1843,7 +1843,7 @@
 
 [DEPS](/recipe_modules/isolated/__init__.py#1): [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step)
 
-#### **class [IsolatedApi](/recipe_modules/isolated/api.py#15)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [IsolatedApi](/recipe_modules/isolated/api.py#15)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 API for interacting with isolated.
 
@@ -1902,7 +1902,7 @@
 
 Methods for producing and consuming JSON.
 
-#### **class [JsonApi](/recipe_modules/json/api.py#95)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [JsonApi](/recipe_modules/json/api.py#95)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &emsp; **@staticmethod**<br>&mdash; **def [dumps](/recipe_modules/json/api.py#96)(\*args, \*\*kwargs):**
 
@@ -1943,7 +1943,7 @@
 
 [DEPS](/recipe_modules/led/__init__.py#5): [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [json](#recipe_modules-json), [path](#recipe_modules-path), [proto](#recipe_modules-proto), [step](#recipe_modules-step)
 
-#### **class [LedApi](/recipe_modules/led/api.py#15)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [LedApi](/recipe_modules/led/api.py#15)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 Interface to the led tool.
 
@@ -2011,7 +2011,7 @@
 build (using the Merge Step feature from luciexe protocol). This is the
 replacement for allow_subannotation feature in the legacy annotate mode.
 
-#### **class [LegacyAnnotationApi](/recipe_modules/legacy_annotation/api.py#24)([RecipeApiPlain](/recipe_engine/recipe_api.py#725)):**
+#### **class [LegacyAnnotationApi](/recipe_modules/legacy_annotation/api.py#24)([RecipeApiPlain](/recipe_engine/recipe_api.py#729)):**
 
 &mdash; **def [\_\_call\_\_](/recipe_modules/legacy_annotation/api.py#28)(self, name, cmd, timeout=None, step_test_data=None, cost=_ResourceCost()):**
 
@@ -2026,7 +2026,7 @@
 
 API for specifying Milo behavior.
 
-#### **class [MiloApi](/recipe_modules/milo/api.py#17)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [MiloApi](/recipe_modules/milo/api.py#17)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 A module for interacting with Milo.
 
@@ -2077,7 +2077,7 @@
 `depot_tools/infra_paths` module). Refer to those modules for additional
 documentation.
 
-#### **class [PathApi](/recipe_modules/path/api.py#206)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [PathApi](/recipe_modules/path/api.py#206)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [\_\_getitem\_\_](/recipe_modules/path/api.py#438)(self, name):**
 
@@ -2243,7 +2243,7 @@
 
 Mockable system platform identity functions.
 
-#### **class [PlatformApi](/recipe_modules/platform/api.py#24)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [PlatformApi](/recipe_modules/platform/api.py#24)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 Provides host-platform-detection properties.
 
@@ -2328,7 +2328,7 @@
 intentionally no API to write property values (lest they become a kind of
 random-access global variable).
 
-#### **class [PropertiesApi](/recipe_modules/properties/api.py#28)([RecipeApiPlain](/recipe_engine/recipe_api.py#725), collections.Mapping):**
+#### **class [PropertiesApi](/recipe_modules/properties/api.py#28)([RecipeApiPlain](/recipe_engine/recipe_api.py#729), collections.Mapping):**
 
 PropertiesApi implements all the standard Mapping functions, so you
 can use it like a read-only dict.
@@ -2355,7 +2355,7 @@
 Methods for producing and consuming protobuf data to/from steps and the
 filesystem.
 
-#### **class [ProtoApi](/recipe_modules/proto/api.py#75)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [ProtoApi](/recipe_modules/proto/api.py#75)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &emsp; **@staticmethod**<br>&mdash; **def [decode](/recipe_modules/proto/api.py#151)(data, msg_class, codec, \*\*decoding_kwargs):**
 
@@ -2432,7 +2432,7 @@
 correctly for bots (e.g. ensuring that python is working on Windows, passing the
 unbuffered flag, etc.)
 
-#### **class [PythonApi](/recipe_modules/python/api.py#17)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [PythonApi](/recipe_modules/python/api.py#17)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [\_\_call\_\_](/recipe_modules/python/api.py#18)(self, name, script, args=None, unbuffered=True, venv=None, \*\*kwargs):**
 
@@ -2516,7 +2516,7 @@
       api.random.shuffle(my_list)
       # my_list is now random!
 
-#### **class [RandomApi](/recipe_modules/random/api.py#31)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [RandomApi](/recipe_modules/random/api.py#31)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [\_\_getattr\_\_](/recipe_modules/random/api.py#38)(self, name):**
 
@@ -2527,7 +2527,7 @@
 
 Provides objects for reading and writing raw data to and from steps.
 
-#### **class [RawIOApi](/recipe_modules/raw_io/api.py#297)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [RawIOApi](/recipe_modules/raw_io/api.py#297)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &emsp; **@[returns\_placeholder](/recipe_engine/util.py#151)**<br>&emsp; **@staticmethod**<br>&mdash; **def [input](/recipe_modules/raw_io/api.py#298)(data, suffix='', name=None):**
 
@@ -2622,7 +2622,7 @@
 Requires `rdb` command in `$PATH`:
 https://godoc.org/go.chromium.org/luci/resultdb/cmd/rdb
 
-#### **class [ResultDBAPI](/recipe_modules/resultdb/api.py#19)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [ResultDBAPI](/recipe_modules/resultdb/api.py#19)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 A module for interacting with ResultDB.
 
@@ -2741,7 +2741,7 @@
     file name with a relative path. The value must start with "//".
 ### *recipe_modules* / [runtime](/recipe_modules/runtime)
 
-#### **class [RuntimeApi](/recipe_modules/runtime/api.py#8)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [RuntimeApi](/recipe_modules/runtime/api.py#8)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 This module assists in experimenting with production recipes.
 
@@ -2770,7 +2770,7 @@
 RPCExplorer available at
   https://luci-scheduler.appspot.com/rpcexplorer/services/scheduler.Scheduler
 
-#### **class [SchedulerApi](/recipe_modules/scheduler/api.py#26)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [SchedulerApi](/recipe_modules/scheduler/api.py#26)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 A module for interacting with LUCI Scheduler service.
 
@@ -2826,7 +2826,7 @@
 
 Depends on luci-auth to be in PATH.
 
-#### **class [ServiceAccountApi](/recipe_modules/service_account/api.py#16)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [ServiceAccountApi](/recipe_modules/service_account/api.py#16)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [default](/recipe_modules/service_account/api.py#57)(self):**
 
@@ -2852,7 +2852,7 @@
 Step is the primary API for running steps (external programs, scripts,
 etc.).
 
-#### **class [StepApi](/recipe_modules/step/api.py#26)([RecipeApiPlain](/recipe_engine/recipe_api.py#725)):**
+#### **class [StepApi](/recipe_modules/step/api.py#26)([RecipeApiPlain](/recipe_engine/recipe_api.py#729)):**
 
 &emsp; **@property**<br>&mdash; **def [InfraFailure](/recipe_modules/step/api.py#138)(self):**
 
@@ -3159,7 +3159,7 @@
 
 [DEPS](/recipe_modules/swarming/__init__.py#8): [buildbucket](#recipe_modules-buildbucket), [cipd](#recipe_modules-cipd), [context](#recipe_modules-context), [isolated](#recipe_modules-isolated), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [raw\_io](#recipe_modules-raw_io), [runtime](#recipe_modules-runtime), [step](#recipe_modules-step)
 
-#### **class [SwarmingApi](/recipe_modules/swarming/api.py#1161)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [SwarmingApi](/recipe_modules/swarming/api.py#1161)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 API for interacting with swarming.
 
@@ -3261,7 +3261,7 @@
 
 Allows mockable access to the current time.
 
-#### **class [TimeApi](/recipe_modules/time/api.py#12)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [TimeApi](/recipe_modules/time/api.py#12)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [ms\_since\_epoch](/recipe_modules/time/api.py#49)(self):**
 
@@ -3289,7 +3289,7 @@
 
 API for Tricium analyzers to use.
 
-#### **class [TriciumApi](/recipe_modules/tricium/api.py#15)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [TriciumApi](/recipe_modules/tricium/api.py#15)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 TriciumApi provides basic support for Tricium.
 
@@ -3311,7 +3311,7 @@
 
 Methods for interacting with HTTP(s) URLs.
 
-#### **class [UrlApi](/recipe_modules/url/api.py#15)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [UrlApi](/recipe_modules/url/api.py#15)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [get\_file](/recipe_modules/url/api.py#131)(self, url, path, step_name=None, headers=None, transient_retry=True, strip_prefix=None, timeout=None):**
 
@@ -3416,7 +3416,7 @@
 
 Allows test-repeatable access to a random UUID.
 
-#### **class [UuidApi](/recipe_modules/uuid/api.py#11)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [UuidApi](/recipe_modules/uuid/api.py#11)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &mdash; **def [random](/recipe_modules/uuid/api.py#20)(self):**
 
@@ -3425,7 +3425,7 @@
 
 Thin API for parsing semver strings into comparable object.
 
-#### **class [VersionApi](/recipe_modules/version/api.py#12)([RecipeApi](/recipe_engine/recipe_api.py#873)):**
+#### **class [VersionApi](/recipe_modules/version/api.py#12)([RecipeApi](/recipe_engine/recipe_api.py#877)):**
 
 &emsp; **@staticmethod**<br>&mdash; **def [parse](/recipe_modules/version/api.py#14)(version):**
 
@@ -3441,7 +3441,7 @@
 
 Allows recipe modules to issue warnings in simulation test.
 
-#### **class [WarningApi](/recipe_modules/warning/api.py#12)([RecipeApiPlain](/recipe_engine/recipe_api.py#725)):**
+#### **class [WarningApi](/recipe_modules/warning/api.py#12)([RecipeApiPlain](/recipe_engine/recipe_api.py#729)):**
 
 &emsp; **@recipe_api.escape_all_warnings**<br>&mdash; **def [issue](/recipe_modules/warning/api.py#15)(self, name):**
 
diff --git a/recipe_engine/internal/engine.py b/recipe_engine/internal/engine.py
index 9155289..3f3c01c 100644
--- a/recipe_engine/internal/engine.py
+++ b/recipe_engine/internal/engine.py
@@ -208,6 +208,10 @@
     """
     self._clients['paths']._initialize_with_recipe_api(root_api)
 
+  def record_import_warning(self, warning, importer):
+    """Records an import warning."""
+    self._clients['warning'].record_import_warning(warning, importer)
+
   def close_non_parent_step(self):
     """Closes the tip of the _step_stack if it's not a parent nesting step."""
     try:
diff --git a/recipe_engine/internal/recipe_deps.py b/recipe_engine/internal/recipe_deps.py
index d70b23a..e026fbb 100644
--- a/recipe_engine/internal/recipe_deps.py
+++ b/recipe_engine/internal/recipe_deps.py
@@ -62,10 +62,8 @@
 from .exceptions import UnknownRecipeModule
 from .simple_cfg import SimpleRecipesCfg, RECIPES_CFG_LOCATION_REL
 from .test.test_util import filesystem_safe
-from .warn.definition import (
-  parse_warning_definitions,
-  RECIPE_WARNING_DEFINITIONS_REL,
-)
+from .warn.definition import (parse_warning_definitions,
+                              RECIPE_WARNING_DEFINITIONS_REL)
 
 
 LOG = logging.getLogger(__name__)
@@ -408,6 +406,19 @@
     """
     return parse_deps_spec(self.repo.name, self.do_import().DEPS)
 
+  @cached_property
+  def warnings(self):
+    """Returns a tuple of warnings issued against this recipe module."""
+    return tuple(self.do_import().WARNINGS)
+
+  @cached_property
+  def _cumulative_import_warnings(self):
+    """Returns all import warnings as a tuple that this module and its
+    dependent modules hit. Each element of the tuple is a tuple of
+    (fully-qualified warning name, importer RecipeModule).
+    """
+    return tuple(_collect_import_warnings(self))
+
   def do_import(self):
     """Imports the raw recipe module (i.e. python module).
 
@@ -675,6 +686,8 @@
       test_data=test_data.get_module_test_data(None))
     resolved_deps = _resolve(
       self.repo.recipe_deps, self.normalized_DEPS, 'API', engine, test_data)
+    for _, (warning, importer) in enumerate(_collect_import_warnings(self)):
+      engine.record_import_warning(warning, importer)
     api.__dict__.update({
       local_name: resolved_dep
       for local_name, resolved_dep in resolved_deps.iteritems()
@@ -801,6 +814,24 @@
   return deps
 
 
+def _collect_import_warnings(root):
+  """Traverses the dependency tree from root and collects all import warnings.
+
+  Returns a set of (fully-qualified warning name, importing recipe or
+  recipe module) tuple.
+  """
+  ret = set()
+  recipe_deps = root.repo.recipe_deps
+  for _, (repo_name, module_name) in root.normalized_DEPS.iteritems():
+    module = recipe_deps.repos[repo_name].modules[module_name]
+    for warning in module.warnings:
+      if '/' not in warning:
+        warning = '/'.join((repo_name, warning))
+      ret.add((warning, root))
+    ret.update(module._cumulative_import_warnings)
+  return ret
+
+
 def _instantiate_test_api(imported_module, resolved_deps):
   """Instantiates the RecipeTestApi class from the given imported recipe module.
 
diff --git a/recipe_engine/internal/recipe_module_importer.py b/recipe_engine/internal/recipe_module_importer.py
index 0a9d885..4591f72 100644
--- a/recipe_engine/internal/recipe_module_importer.py
+++ b/recipe_engine/internal/recipe_module_importer.py
@@ -181,6 +181,7 @@
         module, or None if no config.py exists.
       * `DEPS`: Sets a default DEPS value of `()` so that other code in the
         engine can assume that there's ALWAYS a DEPS object for a module.
+      * `WARNINGS`: A list of warnings issued against this recipe module.
       * `DISABLE_STRICT_COVERAGE`: Sets a default value of False.
 
     Args:
@@ -196,6 +197,7 @@
     mod.REPO_ROOT = Path(RepoBasePath(repo_name, repo_root))
     mod.CONFIG_CTX = getattr(mod, 'CONFIG_CTX', None)
     mod.DEPS = getattr(mod, 'DEPS', ())
+    mod.WARNINGS = getattr(mod, 'WARNINGS', ())
 
     # TODO(iannucci, probably): remove DISABLE_STRICT_COVERAGE (crbug/693058).
     mod.DISABLE_STRICT_COVERAGE = getattr(mod, 'DISABLE_STRICT_COVERAGE', False)
diff --git a/recipe_engine/recipe_api.py b/recipe_engine/recipe_api.py
index bf05de0..391544d 100644
--- a/recipe_engine/recipe_api.py
+++ b/recipe_engine/recipe_api.py
@@ -310,6 +310,10 @@
         [frame_tup[0] for frame_tup in inspect.stack()],
     )
 
+  def record_import_warning(self, name, importer):
+    """Records import warning during DEPS resolution."""
+    self._recorder.record_import_warning(name, importer)
+
   def resolve_warning(self, name, issuer_file):
     """Returns the fully-qualified warning name for the given warning.