Repository: pylint-dev/pylint

Issue:
[unknown-option-value] False negative after `--disable=all`
### Bug description
The option `--enable` does not give an error when an unknown error code is used. It should give an error.

What I was trying is to enable just a group of warnings. I tried `W1` and `W1*`. No error but also no warning. But the code does have some W1203 problems in it.

### Configuration

_No response_

### Command used

```shell
pylint --disable=all --enable=foobar bandas.py
```


### Pylint output

```python
--------------------------------------------------------------------                                                                                                                               Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)
```


### Expected behavior

Give an error.

### Pylint version

```shell
pylint 2.17.7
astroid 2.15.8
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0]
```


### OS / Environment

Debian 12 arm64

### Additional dependencies

_No response_


Discussion:
Reproduced @ 51c28bc922dfb8da42e680ed7190eb942889df01

```sh
pylint --enable=missnig-docstring pylint/checkers/utils.py
************* Module Command line
Command line:1:0: W0012: Unknown option value for '--enable', expected a valid pylint message and got 'missnig-docstring' (unknown-option-value)

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)
```
```
pylint --disable=all --enable=missnig-docstring pylint/checkers/utils.py
No files to lint: exiting.
```
As I also ran into this issue I started debugging it to check for a fix.

The reason seems to be that `--disable=all` also disables the `unknown-option-value` messages.
Thus when running

```
pylint --disable=all --enable=unknown-option-value,missnig-docstring pylint/checkers/utils.py
************* Module Command line
Command line:1:0: W0012: Unknown option value for '--enable', expected a valid pylint message and got 'missnig-docstring' (unknown-option-value)

------------------------------------
Your code has been rated at 10.00/10
```

the results is again as expected.

While I understand this behavior for `--disable=all`, I think it is not very intuitive from a user perspective. I did not expect to silent pylint error messages, most people would assume it only silents linting errors.

Would it make sense to  have internal pylint errors not being affected by `--disable=all`?
Yep, exactly! This issue is ready for a contributor to tackle.



Patch:
diff --git a/doc/whatsnew/fragments/9403.bugfix b/doc/whatsnew/fragments/9403.bugfix
new file mode 100644
index 0000000000..478f5978fe
--- /dev/null
+++ b/doc/whatsnew/fragments/9403.bugfix
@@ -0,0 +1,3 @@
+`--enable` with `--disable=all` now produces an error, when an unknown msg code is used. Internal `pylint` messages are no longer affected by `--disable=all`.
+
+Closes #9403
diff --git a/pylint/lint/message_state_handler.py b/pylint/lint/message_state_handler.py
index 2ddd7d4db3..5c4498af96 100644
--- a/pylint/lint/message_state_handler.py
+++ b/pylint/lint/message_state_handler.py
@@ -18,7 +18,7 @@
 )
 from pylint.interfaces import HIGH
 from pylint.message import MessageDefinition
-from pylint.typing import ManagedMessage
+from pylint.typing import ManagedMessage, MessageDefinitionTuple
 from pylint.utils.pragma_parser import (
     OPTION_PO,
     InvalidPragmaError,
@@ -37,6 +37,11 @@ class _MessageStateHandler:
 
     def __init__(self, linter: PyLinter) -> None:
         self.linter = linter
+        self.default_enabled_messages: dict[str, MessageDefinitionTuple] = {
+            k: v
+            for k, v in self.linter.msgs.items()
+            if len(v) == 3 or v[3].get("default_enabled", True)
+        }
         self._msgs_state: dict[str, bool] = {}
         self._options_methods = {
             "enable": self.enable,
@@ -84,6 +89,14 @@ def _get_messages_to_set(
                 message_definitions.extend(
                     self._get_messages_to_set(_msgid, enable, ignore_unknown)
                 )
+            if not enable:
+                # "all" should not disable pylint's own warnings
+                message_definitions = list(
+                    filter(
+                        lambda m: m.msgid not in self.default_enabled_messages,
+                        message_definitions,
+                    )
+                )
             return message_definitions
 
         # msgid is a category?
diff --git a/pylint/lint/run.py b/pylint/lint/run.py
index 1a8d594a04..2bbbb337b9 100644
--- a/pylint/lint/run.py
+++ b/pylint/lint/run.py
@@ -175,9 +175,13 @@ def __init__(
                 sys.exit(code)
             return
 
-        # Display help if there are no files to lint or no checks enabled
-        if not args or len(linter.config.disable) == len(
-            linter.msgs_store._messages_definitions
+        # Display help if there are no files to lint or only internal checks enabled (`--disable=all`)
+        disable_all_msg_set = set(
+            msg.symbol for msg in linter.msgs_store.messages
+        ) - set(msg[1] for msg in linter.default_enabled_messages.values())
+        if not args or (
+            len(linter.config.enable) == 0
+            and set(linter.config.disable) == disable_all_msg_set
         ):
             print("No files to lint: exiting.")
             sys.exit(32)


Test patch:
diff --git a/tests/test_self.py b/tests/test_self.py
index 1c72bc95f8..821d051ba2 100644
--- a/tests/test_self.py
+++ b/tests/test_self.py
@@ -219,6 +219,26 @@ def test_disable_all(self) -> None:
         self._runtest([UNNECESSARY_LAMBDA, "--disable=all"], out=out, code=32)
         assert "No files to lint: exiting." in out.getvalue().strip()
 
+    def test_disable_all_enable_invalid(self) -> None:
+        # Reproduces issue #9403. If disable=all is used no error was raised for invalid messages unless
+        # unknown-option-value was manually enabled.
+        out = StringIO()
+        self._runtest(
+            # Enable one valid message to not run into "No files to lint: exiting."
+            [
+                UNNECESSARY_LAMBDA,
+                "--disable=all",
+                "--enable=import-error",
+                "--enable=foo",
+            ],
+            out=out,
+            code=0,
+        )
+        assert (
+            "W0012: Unknown option value for '--enable', expected a valid pylint message and got 'foo'"
+            in out.getvalue().strip()
+        )
+
     def test_output_with_verbose(self) -> None:
         out = StringIO()
         self._runtest([UNNECESSARY_LAMBDA, "--verbose"], out=out, code=4)