Repository: mesonbuild/meson

Issue:
Deprecation warnings are reversed
**Describe the bug**
When I use `python3` module with min_version where it was not deprecated yet, I get deprecation warning and a “feature too new” warning:

```
DEPRECATION: Project targeting '>= 0.47' but tried to use feature deprecated since '0.48.0': python3 module
Build targets in project: 0
WARNING: Project specifies a minimum meson_version '>= 0.47' but uses features which were added in newer versions:
 * 0.48.0: {'python3 module'}
WARNING: Deprecated features used:
 * 0.48.0: {'python3 module'}
```

When I bump the min_version to 0.48 both warnings disappear.

**To Reproduce**
```
project('almanah', 'c', meson_version: '>= 0.47')

python3 = import('python3')
python = python3.find_python().path()
```

**Expected behavior**
* Do not show deprecation warning with min_version < 48
* Show deprecation warning with min_version ≥ 48
* Never show “feature too new” warning.

**system parameters**
* native build
* NixOS
* Python 3.7.7
* Meson 0.54.1



Discussion:
There's two issues here:
1) don't show deprecation warnings if the feature is not deprecated in the minimum version
2) meson shows both a deprecated warning and a too new warning for *the exact same thing*

1) is a feature request
2) is a regression

@nirbheek, do you think that 2) is worth fixing before 0.54.2? If so I'll look into it tomorrow.
@dcbaker yeah, sounds like something we should fix. Incorrect warnings are bad; causes a culture of people ignoring warnings.

Resolving patch:
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -18,6 +18,7 @@
 from . import mparser, mesonlib, mlog
 from . import environment, dependencies
 
+import abc
 import os, copy, re
 import collections.abc
 from functools import wraps
@@ -212,12 +213,11 @@ def wrapped(*wrapped_args, **wrapped_kwargs):
             return f(*wrapped_args, **wrapped_kwargs)
         return wrapped
 
-class FeatureCheckBase:
+class FeatureCheckBase(metaclass=abc.ABCMeta):
     "Base class for feature version checks"
 
-    # Class variable, shared across all instances
-    #
-    # Format: {subproject: {feature_version: set(feature_names)}}
+    # In python 3.6 we can just forward declare this, but in 3.5 we can't
+    # This will be overwritten by the subclasses by necessity
     feature_registry = {}  # type: T.ClassVar[T.Dict[str, T.Dict[str, T.Set[str]]]]
 
     def __init__(self, feature_name: str, version: str):
@@ -231,13 +231,18 @@ def get_target_version(subproject: str) -> str:
             return ''
         return mesonlib.project_meson_versions[subproject]
 
+    @staticmethod
+    @abc.abstractmethod
+    def check_version(target_version: str, feature_Version: str) -> bool:
+        pass
+
     def use(self, subproject: str) -> None:
         tv = self.get_target_version(subproject)
         # No target version
         if tv == '':
             return
         # Target version is new enough
-        if mesonlib.version_compare_condition_with_min(tv, self.feature_version):
+        if self.check_version(tv, self.feature_version):
             return
         # Feature is too new for target version, register it
         if subproject not in self.feature_registry:
@@ -283,6 +288,15 @@ def wrapped(*wrapped_args, **wrapped_kwargs):
 class FeatureNew(FeatureCheckBase):
     """Checks for new features"""
 
+    # Class variable, shared across all instances
+    #
+    # Format: {subproject: {feature_version: set(feature_names)}}
+    feature_registry = {}  # type: T.ClassVar[T.Dict[str, T.Dict[str, T.Set[str]]]]
+
+    @staticmethod
+    def check_version(target_version: str, feature_version: str) -> bool:
+        return mesonlib.version_compare_condition_with_min(target_version, feature_version)
+
     @staticmethod
     def get_warning_str_prefix(tv: str) -> str:
         return 'Project specifies a minimum meson_version \'{}\' but uses features which were added in newer versions:'.format(tv)
@@ -294,6 +308,16 @@ def log_usage_warning(self, tv: str) -> None:
 class FeatureDeprecated(FeatureCheckBase):
     """Checks for deprecated features"""
 
+    # Class variable, shared across all instances
+    #
+    # Format: {subproject: {feature_version: set(feature_names)}}
+    feature_registry = {}  # type: T.ClassVar[T.Dict[str, T.Dict[str, T.Set[str]]]]
+
+    @staticmethod
+    def check_version(target_version: str, feature_version: str) -> bool:
+        # For deprecatoin checks we need to return the inverse of FeatureNew checks
+        return not mesonlib.version_compare_condition_with_min(target_version, feature_version)
+
     @staticmethod
     def get_warning_str_prefix(tv: str) -> str:
         return 'Deprecated features used:'


Test patch: