Repository: sqlfluff/sqlfluff

Issue:
Running `lint` on an empty file fails with critical Exception
### Search before asking

- [X] I searched the [issues](https://github.com/sqlfluff/sqlfluff/issues) and found no similar issues.


### What Happened

This is a bit of an odd one. When running `sqlfluff lint` on an empty file it fails with (Python) exception.

While trying to lint empty file is probably not the main use-case for SQLFluff I still consider this somewhat relevant, when applying SQLFluff in a dynamic code base. 

### Expected Behaviour

I'm not entirely sure what the correct result is. Feasible option are

- Passing
- Raise some kind of lint error (but not a critical exception)

My personal take is that lint should pass, which (I think) is similar behaviour to other linters.

### Observed Behaviour

`LT01` and `LT12` with an critical Exception

```
CRITICAL   [LT01] Applying rule LT01 to 'stdin' threw an Exception: ReflowSequence has empty elements.
CRITICAL   [LT12] Applying rule LT12 to 'stdin' threw an Exception: tuple index out of range
```


### How to reproduce

```sh
cat /dev/null | sqlfluff lint --dialect ansi -
```

### Dialect

ansi

### Version

latest main branch

```
git rev-parse HEAD
d19de0ecd16d298f9e3bfb91da122734c40c01e5
```

### Configuration

default

### Are you willing to work on and submit a PR to address the issue?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/sqlfluff/sqlfluff/blob/main/CODE_OF_CONDUCT.md)

Running `lint` on an empty file fails with critical Exception
### Search before asking

- [X] I searched the [issues](https://github.com/sqlfluff/sqlfluff/issues) and found no similar issues.


### What Happened

This is a bit of an odd one. When running `sqlfluff lint` on an empty file it fails with (Python) exception.

While trying to lint empty file is probably not the main use-case for SQLFluff I still consider this somewhat relevant, when applying SQLFluff in a dynamic code base. 

### Expected Behaviour

I'm not entirely sure what the correct result is. Feasible option are

- Passing
- Raise some kind of lint error (but not a critical exception)

My personal take is that lint should pass, which (I think) is similar behaviour to other linters.

### Observed Behaviour

`LT01` and `LT12` with an critical Exception

```
CRITICAL   [LT01] Applying rule LT01 to 'stdin' threw an Exception: ReflowSequence has empty elements.
CRITICAL   [LT12] Applying rule LT12 to 'stdin' threw an Exception: tuple index out of range
```


### How to reproduce

```sh
cat /dev/null | sqlfluff lint --dialect ansi -
```

### Dialect

ansi

### Version

latest main branch

```
git rev-parse HEAD
d19de0ecd16d298f9e3bfb91da122734c40c01e5
```

### Configuration

default

### Are you willing to work on and submit a PR to address the issue?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/sqlfluff/sqlfluff/blob/main/CODE_OF_CONDUCT.md)



Discussion:
I'll pick up this one - I've been in the "empty" file code recently and might have influenced this outcome.
I'll pick up this one - I've been in the "empty" file code recently and might have influenced this outcome.

Patch:
diff --git a/src/sqlfluff/rules/layout/LT12.py b/src/sqlfluff/rules/layout/LT12.py
--- a/src/sqlfluff/rules/layout/LT12.py
+++ b/src/sqlfluff/rules/layout/LT12.py
@@ -124,6 +124,10 @@ def _eval(self, context: RuleContext) -> Optional[LintResult]:
         # We only care about the final segment of the parse tree.
         parent_stack, segment = get_last_segment(FunctionalContext(context).segment)
         self.logger.debug("Found last segment as: %s", segment)
+        if not segment:
+            # NOTE: Edge case. If the file is totally empty, we won't find a final
+            # segment. In this case return without error.
+            return None
 
         trailing_newlines = Segments(*get_trailing_newlines(context.segment))
         trailing_literal_newlines = trailing_newlines
diff --git a/src/sqlfluff/utils/reflow/sequence.py b/src/sqlfluff/utils/reflow/sequence.py
--- a/src/sqlfluff/utils/reflow/sequence.py
+++ b/src/sqlfluff/utils/reflow/sequence.py
@@ -104,7 +104,10 @@ def get_raw(self) -> str:
 
     @staticmethod
     def _validate_reflow_sequence(elements: ReflowSequenceType):
-        assert elements, "ReflowSequence has empty elements."
+        # An empty set of elements _is_ allowed as an edge case.
+        if not elements:
+            # Return early if so
+            return None
         # Check odds and evens
         OddType = elements[0].__class__
         EvenType = ReflowPoint if OddType is ReflowBlock else ReflowBlock


Test patch:
diff --git a/test/cli/commands_test.py b/test/cli/commands_test.py
--- a/test/cli/commands_test.py
+++ b/test/cli/commands_test.py
@@ -251,6 +251,14 @@ def test__cli__command_lint_stdin(command):
     invoke_assert_code(args=[lint, ("--dialect=ansi",) + command], cli_input=sql)
 
 
+def test__cli__command_lint_empty_stdin():
+    """Check linting an empty file raises no exceptions.
+
+    https://github.com/sqlfluff/sqlfluff/issues/4807
+    """
+    invoke_assert_code(args=[lint, ("-d", "ansi", "-")], cli_input="")
+
+
 def test__cli__command_render_stdin():
     """Check render on a simple script using stdin."""
     with open("test/fixtures/cli/passing_a.sql") as test_file: