Repository: sqlfluff/sqlfluff

Issue:
fix succeeds even though lint fails with PRS error
## Expected Behaviour

I assume fix is basically lint + fix (probably many do if they fix in a pre-commit, for example). If that assumption is reasonable, then `fix -f` should fail whenever `lint` would fail.

## Observed Behaviour

It's possible that `fix -f` succeeds when fix doesn't find anything to do, even though there are errors that would cause `lint` to fail. 

## Steps to Reproduce

Edit: since https://github.com/sqlfluff/sqlfluff/pull/2438 the `INTEGER` is detected. Replace it with any non-existing pseudo type to reproduce this issue again.

`lint_placeholders.sql`:
```sql
SELECT my_col
FROM my_schema.my_table
WHERE processdate = CAST(
        event_date AS INTEGER
    )
    AND eventtype = &{event_type}
```

```
$ sqlfluff lint lint_placeholders.sql
== [lint_placeholders.sql] FAIL
L:   4 | P:  70 |  PRS | Line 4, Position 70: Found unparsable section: ' AS
| INTEGER'
All Finished 📜 🎉!

$ echo $?
65
```

That is as expected, because `INTEGER` is not listed as a keyword in Hive dialect at the moment.

```
$ sqlfluff fix -f lint_placeholders.sql
==== finding fixable violations ====
==== no fixable linting violations found ====
All Finished 📜 🎉!

$ echo $?
0
```

That is unexpected. I was expecting this to also fail and complain about the PRS error.

## Dialect

Hive, but this issue doesn't seem to be dialect specific per se.

## Version

sqlfluff, version 0.9.1

Python 3.7.5

(not using dbt)

## Configuration

`.sqlfluff`:

```ini
[sqlfluff]
dialect = hive
templater = placeholder

[sqlfluff:templater:placeholder]
param_style = ampersand
event_type='my_event'
```

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff) Report
> Merging [#2546](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff) (a85e2f7) into [main](https://codecov.io/gh/sqlfluff/sqlfluff/commit/0102303098ad547a1b918df3f69e0e224c3b8a10?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff) (0102303) will **decrease** coverage by `0.01%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546/graphs/tree.svg?width=650&height=150&src=pr&token=maM8kLgCmn&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff)](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff)

```diff
@@             Coverage Diff             @@
##              main    #2546      +/-   ##
===========================================
- Coverage   100.00%   99.98%   -0.02%     
===========================================
  Files          163      163              
  Lines        11768    11773       +5     
===========================================
+ Hits         11768    11771       +3     
- Misses           0        2       +2     
```


| [Impacted Files](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff) | Coverage Δ | |
|---|---|---|
| [src/sqlfluff/cli/commands.py](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff#diff-c3JjL3NxbGZsdWZmL2NsaS9jb21tYW5kcy5weQ==) | `99.40% <100.00%> (-0.60%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff). Last update [0102303...a85e2f7](https://codecov.io/gh/sqlfluff/sqlfluff/pull/2546?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sqlfluff).

barrywhart: I'm putting this back in draft mode because I "discovered" the existing `--ignore templating,parsing` command-line option. I think we should use that, rather than adding a new `--FIX-EVEN-UNPARSABLE` option.

@tunetheweb, @alanmcruickshank 
tunetheweb: > I'm putting this back in draft mode because I "discovered" the existing `--ignore templating,parsing` command-line option. I think we should use that, rather than adding a new `--FIX-EVEN-UNPARSABLE` option.

That’s a slightly different thing though. It basically means ignore them from linting. Your own means “fix what you can even if linting errors, whether ignored or not”.


barrywhart: >That’s a slightly different thing though. It basically means ignore them from linting.
>Your own means “fix what you can even if linting errors, whether ignored or not”.

Are you sure? Look at [these messages](https://github.com/sqlfluff/sqlfluff/blob/main/src/sqlfluff/cli/commands.py#L672L685) printed in `fix` when there are templater errors.  Specifically, this one:
>Use '--ignore templating' to attempt to fix anyway.

🤯 

barrywhart: @tunetheweb: Oh yes, you're right. `--ignore` is almost identical to `noqa`, but applied globally. 👍 
barrywhart: @tunetheweb: Okay, I think I've addressed all (almost all?) of your feedback. Ready for another review. I removed the `#pragma: no cover` from the `click.echo()` after changing the code around it, so I think it'll be covered by existing tests. We'll know for sure once the build finishes. 🍿 
barrywhart: >A function name almost as long as your branch names 😁
I was trying to avoid it, but it's hard to explain in a few words. 😛  Next time, `magic_code_xxx123()`. 🎉
barrywhart: @tunetheweb: Whee! Onward and upward. 🚀 

Patch:
diff --git a/docs/source/production.rst b/docs/source/production.rst
index fb4fdc364..1641002fa 100644
--- a/docs/source/production.rst
+++ b/docs/source/production.rst
@@ -106,6 +106,20 @@ SQLFluff comes with two `pre-commit`_ hooks:
 * sqlfluff-lint: returns linting errors.
 * sqlfluff-fix: attempts to fix rule violations.
 
+.. warning::
+   For safety reasons, ``sqlfluff-fix`` by default will not make any fixes in
+   files that had templating or parse errors, even if those errors were ignored
+   using ``noqa`` or `--ignore``.
+
+   Although it is not advised, you *can* tell SQLFluff to try and fix
+   these files by overriding the ``fix_even_unparsable`` setting
+   in ``.sqlfluff`` config file or using the ``sqlfluff fix --FIX-EVEN-UNPARSABLE``
+   command line option.
+
+   *Overriding this behavior may break your SQL. If you use this override,
+   always be sure to review any fixes applied to files with templating or parse
+   errors to verify they are okay.*
+
 You should create a file named `.pre-commit-config.yaml`
 at the root of your git project, which should look
 like this:
diff --git a/src/sqlfluff/api/simple.py b/src/sqlfluff/api/simple.py
index bce9148ff..145fa4350 100644
--- a/src/sqlfluff/api/simple.py
+++ b/src/sqlfluff/api/simple.py
@@ -97,6 +97,7 @@ def fix(
     rules: Optional[List[str]] = None,
     exclude_rules: Optional[List[str]] = None,
     config_path: Optional[str] = None,
+    fix_even_unparsable: Optional[bool] = None,
 ) -> str:
     """Fix a SQL string.
 
@@ -123,8 +124,18 @@ def fix(
     linter = Linter(config=cfg)
 
     result = linter.lint_string_wrapped(sql, fix=True)
-    fixed_string = result.paths[0].files[0].fix_string()[0]
-    return fixed_string
+    if fix_even_unparsable is None:
+        fix_even_unparsable = cfg.get("fix_even_unparsable")
+    should_fix = True
+    if not fix_even_unparsable:
+        # If fix_even_unparsable wasn't set, check for templating or parse
+        # errors and suppress fixing if there were any.
+        _, num_filtered_errors = result.count_tmp_prs_errors()
+        if num_filtered_errors > 0:
+            should_fix = False
+    if should_fix:
+        sql = result.paths[0].files[0].fix_string()[0]
+    return sql
 
 
 def parse(
diff --git a/src/sqlfluff/cli/commands.py b/src/sqlfluff/cli/commands.py
index b3f16409c..f92f71dd3 100644
--- a/src/sqlfluff/cli/commands.py
+++ b/src/sqlfluff/cli/commands.py
@@ -44,7 +44,6 @@ from sqlfluff.core import (
     Linter,
     FluffConfig,
     SQLLintError,
-    SQLParseError,
     SQLTemplaterError,
     SQLFluffUserError,
     dialect_selector,
@@ -56,6 +55,7 @@ from sqlfluff.core.config import progress_bar_configuration
 from sqlfluff.core.enums import FormatType, Color
 from sqlfluff.core.linter import ParsedString
 from sqlfluff.core.plugin.host import get_plugin_manager
+from sqlfluff.core.linter import LintingResult
 
 
 class RedWarningsFilter(logging.Filter):
@@ -263,9 +263,10 @@ def core_options(f: Callable) -> Callable:
         help=(
             "Ignore particular families of errors so that they don't cause a failed "
             "run. For example `--ignore parsing` would mean that any parsing errors "
-            "are ignored and don't influence the success or fail of a run. Multiple "
-            "options are possible if comma separated e.g. "
-            "`--ignore parsing,templating`."
+            "are ignored and don't influence the success or fail of a run. "
+            "`--ignore` behaves somewhat like `noqa` comments, except it "
+            "applies globally. Multiple options are possible if comma separated: "
+            "e.g. `--ignore parsing,templating`."
         ),
     )(f)
     f = click.option(
@@ -575,6 +576,30 @@ def lint(
         sys.exit(0)
 
 
+def _handle_files_with_tmp_or_prs_errors(lint_result: LintingResult) -> int:
+    """Discard lint fixes for files with templating or parse errors.
+
+    Returns 1 if there are any files with templating or parse errors after
+    filtering, else 0. (Intended as a process exit code.)
+    """
+    total_errors, num_filtered_errors = lint_result.count_tmp_prs_errors()
+    lint_result.discard_fixes_for_lint_errors_in_files_with_tmp_or_prs_errors()
+    if total_errors:
+        click.echo(
+            colorize(f"  [{total_errors} templating/parsing errors found]", Color.red)
+        )
+        if num_filtered_errors < total_errors:
+            color = Color.red if num_filtered_errors else Color.green
+            click.echo(
+                colorize(
+                    f"  [{num_filtered_errors} templating/parsing errors "
+                    f'remaining after "ignore"]',
+                    color,
+                )
+            )
+    return 1 if num_filtered_errors else 0
+
+
 def do_fixes(lnt, result, formatter=None, **kwargs):
     """Actually do the fixes."""
     click.echo("Persisting Changes...")
@@ -619,6 +644,18 @@ def do_fixes(lnt, result, formatter=None, **kwargs):
     is_flag=True,
     help="Disables progress bars.",
 )
+@click.option(
+    "--FIX-EVEN-UNPARSABLE",
+    is_flag=True,
+    help=(
+        "Enables fixing of files that have templating or parse errors. "
+        "Note that the similar-sounding '--ignore' or 'noqa' features merely "
+        "prevent errors from being *displayed*. For safety reasons, the 'fix'"
+        "command will not make any fixes in files that have templating or parse "
+        "errors unless '--FIX-EVEN-UNPARSABLE' is enabled on the command line"
+        "or in the .sqlfluff config file."
+    ),
+)
 @click.argument("paths", nargs=-1, type=click.Path(allow_dash=True))
 def fix(
     force: bool,
@@ -643,6 +680,7 @@ def fix(
     fixing_stdin = ("-",) == paths
 
     config = get_config(extra_config_path, ignore_local_config, **kwargs)
+    fix_even_unparsable = config.get("fix_even_unparsable")
     lnt, formatter = get_linter_and_formatter(config, silent=fixing_stdin)
 
     verbose = config.get("verbose")
@@ -662,6 +700,8 @@ def fix(
         result = lnt.lint_string_wrapped(stdin, fname="stdin", fix=True)
         templater_error = result.num_violations(types=SQLTemplaterError) > 0
         unfixable_error = result.num_violations(types=SQLLintError, fixable=False) > 0
+        if not fix_even_unparsable:
+            exit_code = _handle_files_with_tmp_or_prs_errors(result)
 
         if result.num_violations(types=SQLLintError, fixable=True) > 0:
             stdout = result.paths[0].files[0].fix_string()[0]
@@ -678,16 +718,17 @@ def fix(
             )
             click.echo(
                 colorize(
-                    "Use '--ignore templating' to attempt to fix anyway.",
+                    "Use --fix-even-unparsable' to attempt to fix the SQL anyway.",
                     Color.red,
                 ),
                 err=True,
             )
+
         if unfixable_error:
             click.echo(colorize("Unfixable violations detected.", Color.red), err=True)
 
         click.echo(stdout, nl=False)
-        sys.exit(1 if templater_error or unfixable_error else 0)
+        sys.exit(1 if templater_error or unfixable_error else exit_code)
 
     # Lint the paths (not with the fix argument at this stage), outputting as we go.
     click.echo("==== finding fixable violations ====")
@@ -708,6 +749,9 @@ def fix(
         )
         sys.exit(1)
 
+    if not fix_even_unparsable:
+        exit_code = _handle_files_with_tmp_or_prs_errors(result)
+
     # NB: We filter to linting violations here, because they're
     # the only ones which can be potentially fixed.
     if result.num_violations(types=SQLLintError, fixable=True) > 0:
@@ -763,15 +807,9 @@ def fix(
             "  [{} unfixable linting violations found]",
             1,
         ),
-        (
-            dict(types=SQLTemplaterError),
-            "  [{} templating errors found]",
-            1,
-        ),
-        (dict(types=SQLParseError), "  [{} parsing errors found]", 0),
     ]
     for num_violations_kwargs, message_format, error_level in error_types:
-        num_violations = result.num_violations(**num_violations_kwargs)  # type: ignore
+        num_violations = result.num_violations(**num_violations_kwargs)
         if num_violations > 0:
             click.echo(message_format.format(num_violations))
             exit_code = max(exit_code, error_level)
diff --git a/src/sqlfluff/core/default_config.cfg b/src/sqlfluff/core/default_config.cfg
index 7cdae4ad7..f45e3aae7 100644
--- a/src/sqlfluff/core/default_config.cfg
+++ b/src/sqlfluff/core/default_config.cfg
@@ -27,6 +27,9 @@ disable_noqa = False
 # Comma separated list of file extensions to lint
 # NB: This config will only apply in the root folder
 sql_file_exts = .sql,.sql.j2,.dml,.ddl
+# Allow fix to run on files, even if they contain parsing errors
+# Note altering this is NOT RECOMMENDED as can corrupt SQL
+fix_even_unparsable = False
 
 [sqlfluff:indentation]
 # See https://docs.sqlfluff.com/en/stable/indentation.html
diff --git a/src/sqlfluff/core/linter/linting_result.py b/src/sqlfluff/core/linter/linting_result.py
index 277087d99..f600d0569 100644
--- a/src/sqlfluff/core/linter/linting_result.py
+++ b/src/sqlfluff/core/linter/linting_result.py
@@ -7,12 +7,16 @@ from typing import (
     List,
     Optional,
     overload,
+    Tuple,
 )
 from typing_extensions import Literal
 
 
 from sqlfluff.core.errors import (
     CheckTuple,
+    SQLLintError,
+    SQLParseError,
+    SQLTemplaterError,
 )
 
 from sqlfluff.core.timing import TimingSummary
@@ -181,3 +185,36 @@ class LintingResult:
                 "path."
             )
         return self.paths[0].tree
+
+    TMP_PRS_ERROR_TYPES = (SQLTemplaterError, SQLParseError)
+
+    def count_tmp_prs_errors(self) -> Tuple[int, int]:
+        """Count templating or parse errors before and after filtering."""
+        total_errors = self.num_violations(
+            types=self.TMP_PRS_ERROR_TYPES, filter_ignore=False
+        )
+        num_filtered_errors = 0
+        for linted_dir in self.paths:
+            for linted_file in linted_dir.files:
+                num_filtered_errors += linted_file.num_violations(
+                    types=self.TMP_PRS_ERROR_TYPES
+                )
+        return total_errors, num_filtered_errors
+
+    def discard_fixes_for_lint_errors_in_files_with_tmp_or_prs_errors(self) -> None:
+        """Discard lint fixes for files with templating or parse errors."""
+        total_errors = self.num_violations(
+            types=self.TMP_PRS_ERROR_TYPES, filter_ignore=False
+        )
+        if total_errors:
+            for linted_dir in self.paths:
+                for linted_file in linted_dir.files:
+                    num_errors = linted_file.num_violations(
+                        types=self.TMP_PRS_ERROR_TYPES, filter_ignore=False
+                    )
+                    if num_errors:
+                        # File has errors. Discard all the SQLLintError fixes:
+                        # they are potentially unsafe.
+                        for violation in linted_file.violations:
+                            if isinstance(violation, SQLLintError):
+                                violation.fixes = []


Test patch:
diff --git a/test/api/simple_test.py b/test/api/simple_test.py
index f797c2a13..248035149 100644
--- a/test/api/simple_test.py
+++ b/test/api/simple_test.py
@@ -177,6 +177,31 @@ def test__api__fix_string_specific_exclude():
     assert result == "SELECT *, 1, blah AS foo FROM mytable\n"
 
 
+def test__api__fix_string_unparsable():
+    """Test behavior with parse errors."""
+    bad_query = """SELECT my_col
+FROM my_schema.my_table
+where processdate ! 3"""
+    result = sqlfluff.fix(bad_query, rules=["L010"])
+    # Check fix result: should be unchanged because of the parse error.
+    assert result == bad_query
+
+
+def test__api__fix_string_unparsable_fix_even_unparsable():
+    """Test behavior with parse errors."""
+    bad_query = """SELECT my_col
+FROM my_schema.my_table
+where processdate ! 3"""
+    result = sqlfluff.fix(bad_query, rules=["L010"], fix_even_unparsable=True)
+    # Check fix result: should be fixed because we overrode fix_even_unparsable.
+    assert (
+        result
+        == """SELECT my_col
+FROM my_schema.my_table
+WHERE processdate ! 3"""
+    )
+
+
 def test__api__parse_string():
     """Basic checking of parse functionality."""
     parsed = sqlfluff.parse(my_bad_query)
diff --git a/test/cli/commands_test.py b/test/cli/commands_test.py
index 9224914bc..668beaaca 100644
--- a/test/cli/commands_test.py
+++ b/test/cli/commands_test.py
@@ -1,10 +1,12 @@
 """The Test file for CLI (General)."""
 
 import configparser
-import tempfile
+import json
 import os
+import pathlib
 import shutil
-import json
+import tempfile
+import textwrap
 from unittest.mock import MagicMock, patch
 
 import yaml
@@ -509,6 +511,165 @@ def test__cli__command__fix(rule, fname):
         generic_roundtrip_test(test_file, rule)
 
 
+@pytest.mark.parametrize(
+    "sql,fix_args,fixed,exit_code",
+    [
+        (
+            # - One lint error: "where" is lower case
+            # - Not fixable because of parse error, hence error exit
+            """
+            SELECT my_col
+            FROM my_schema.my_table
+            where processdate ! 3
+            """,
+            ["--force", "--fixed-suffix", "FIXED", "--rules", "L010"],
+            None,
+            1,
+        ),
+        (
+            # - One lint error: "where" is lower case
+            # - Not fixable because of templater error, hence error exit
+            """
+            SELECT my_col
+            FROM my_schema.my_table
+            where processdate {{ condition }}
+            """,
+            ["--force", "--fixed-suffix", "FIXED", "--rules", "L010"],
+            None,
+            1,
+        ),
+        (
+            # - One lint error: "where" is lower case
+            # - Not fixable because of parse error (even though "noqa"), hence
+            #   error exit
+            """
+            SELECT my_col
+            FROM my_schema.my_table
+            where processdate ! 3  -- noqa: PRS
+            """,
+            ["--force", "--fixed-suffix", "FIXED", "--rules", "L010"],
+            None,
+            1,
+        ),
+        (
+            # - No lint errors
+            # - Parse error not suppressed, hence error exit
+            """
+            SELECT my_col
+            FROM my_schema.my_table
+            WHERE processdate ! 3
+            """,
+            ["--force", "--fixed-suffix", "FIXED", "--rules", "L010"],
+            None,
+            1,
+        ),
+        (
+            # - No lint errors
+            # - Parse error suppressed, hence success exit
+            """
+            SELECT my_col
+            FROM my_schema.my_table
+            WHERE processdate ! 3  --noqa: PRS
+            """,
+            ["--force", "--fixed-suffix", "FIXED", "--rules", "L010"],
+            None,
+            0,
+        ),
+        (
+            # - One lint error: "where" is lower case
+            # - Parse error not suppressed
+            # - "--FIX-EVEN-UNPARSABLE", hence fix anyway & success exit
+            """
+            SELECT my_col
+            FROM my_schema.my_table
+            where processdate ! 3
+            """,
+            [
+                "--force",
+                "--fixed-suffix",
+                "FIXED",
+                "--rules",
+                "L010",
+                "--FIX-EVEN-UNPARSABLE",
+            ],
+            """
+            SELECT my_col
+            FROM my_schema.my_table
+            WHERE processdate ! 3
+            """,
+            0,
+        ),
+        (
+            # Two files:
+            # File #1:
+            #   - One lint error: "where" is lower case
+            #   - Not fixable because of parse error
+            # File #2:
+            #   - One lint error: "where" is lower case
+            #   - No parse error, thus fixable
+            # Should fix the second file but not the first, and exit with an
+            # error.
+            [
+                """
+                SELECT my_col
+                FROM my_schema.my_table
+                where processdate ! 3
+                """,
+                """SELECT my_col
+                FROM my_schema.my_table
+                where processdate != 3""",
+            ],
+            ["--force", "--fixed-suffix", "FIXED", "--rules", "L010"],
+            [
+                None,
+                """SELECT my_col
+                FROM my_schema.my_table
+                WHERE processdate != 3""",
+            ],
+            1,
+        ),
+    ],
+    ids=[
+        "1_lint_error_1_unsuppressed_parse_error",
+        "1_lint_error_1_unsuppressed_templating_error",
+        "1_lint_error_1_suppressed_parse_error",
+        "0_lint_errors_1_unsuppressed_parse_error",
+        "0_lint_errors_1_suppressed_parse_error",
+        "1_lint_error_1_unsuppressed_parse_error_FIX_EVEN_UNPARSABLE",
+        "2_files_with_lint_errors_1_unsuppressed_parse_error",
+    ],
+)
+def test__cli__fix_error_handling_behavior(sql, fix_args, fixed, exit_code, tmpdir):
+    """Tests how "fix" behaves wrt parse errors, exit code, etc."""
+    if not isinstance(sql, list):
+        sql = [sql]
+    if not isinstance(fixed, list):
+        fixed = [fixed]
+    assert len(sql) == len(fixed)
+    tmp_path = pathlib.Path(str(tmpdir))
+    for idx, this_sql in enumerate(sql):
+        filepath = tmp_path / f"testing{idx+1}.sql"
+        filepath.write_text(textwrap.dedent(this_sql))
+    with tmpdir.as_cwd():
+        with pytest.raises(SystemExit) as e:
+            fix(
+                fix_args
+                + [
+                    "-f",
+                ]
+            )
+        assert exit_code == e.value.code
+    for idx, this_fixed in enumerate(fixed):
+        fixed_path = tmp_path / f"testing{idx+1}FIXED.sql"
+        if this_fixed is not None:
+            assert textwrap.dedent(this_fixed) == fixed_path.read_text()
+        else:
+            # A None value indicates "sqlfluff fix" should have skipped any
+            # fixes for this file. To confirm this, we verify that the output
+            # file WAS NOT EVEN CREATED.
+            assert not fixed_path.is_file()
+
+
 # Test case disabled because there isn't a good example of where to test this.
 # This *should* test the case where a rule DOES have a proposed fix, but for
 # some reason when we try to apply it, there's a failure.