diff --git a/docs/rules.rst b/docs/rules.rst
index dab310d..24318c7 100644
--- a/docs/rules.rst
+++ b/docs/rules.rst
@@ -59,6 +59,11 @@ empty-lines
.. automodule:: yamllint.rules.empty_lines
+empty-values
+------------
+
+.. automodule:: yamllint.rules.empty_values
+
hyphens
-------
diff --git a/tests/rules/test_empty_values.py b/tests/rules/test_empty_values.py
new file mode 100644
index 0000000..1cc4f3e
--- /dev/null
+++ b/tests/rules/test_empty_values.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2017 Greg Dubicki
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from tests.common import RuleTestCase
+
+
+class EmptyValuesTestCase(RuleTestCase):
+ rule_id = 'empty-values'
+
+ def test_disabled_globally(self):
+ conf = 'empty-values: disable'
+ self.check('---\n'
+ 'foo:\n', conf)
+
+ self.check('---\n'
+ 'foo:\n'
+ ' bar:\n', conf)
+
+ def test_disabled_forbid_in_block_mappings(self):
+ conf = 'empty-values: {forbid-in-block-mappings: false}'
+ self.check('---\n'
+ 'foo:\n', conf)
+
+ self.check('---\n'
+ 'foo:\n'
+ 'bar: aaa\n', conf)
+
+ def test_single_line(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'implicitly-null:\n', conf, problem1=(2, 17))
+
+ self.check('---\n'
+ 'implicitly-null:with-colons:in-key:\n', conf,
+ problem1=(2, 36))
+
+ self.check('---\n'
+ 'implicitly-null:with-colons:in-key2:\n', conf,
+ problem1=(2, 37))
+
+ def test_enabled_all_lines(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'foo:\n'
+ 'bar:\n'
+ 'foobar:\n', conf, problem1=(2, 5),
+ problem2=(3, 5), problem3=(4, 8))
+
+ def test_enabled_explicit_end_of_document(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'foo:\n'
+ '...\n', conf, problem1=(2, 5))
+
+ def test_enabled_not_end_of_document(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'foo:\n'
+ 'bar:\n'
+ ' aaa\n', conf, problem1=(2, 5))
+
+ def test_enabled_different_level(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'foo:\n'
+ ' bar:\n'
+ 'aaa: bbb\n', conf, problem1=(3, 6))
+
+ def test_enabled_empty_flow_mapping(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'foo: {a:}\n', conf)
+
+ def test_enabled_empty_block_sequence(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'foo:\n'
+ ' -\n', conf)
+
+ def test_enabled_not_empty_or_explicit_null(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'foo:\n'
+ ' bar:\n'
+ ' aaa\n', conf)
+
+ self.check('---\n'
+ 'explicitly-null: null\n', conf)
+
+ self.check('---\n'
+ 'explicitly-null:with-colons:in-key: null\n', conf)
+
+ self.check('---\n'
+ 'false-null: nulL\n', conf)
+
+ self.check('---\n'
+ 'empty-string: \'\'\n', conf)
+
+ self.check('---\n'
+ 'nullable-boolean: false\n', conf)
+
+ self.check('---\n'
+ 'nullable-int: 0\n', conf)
+
+ self.check('---\n'
+ 'First occurrence: &anchor Foo\n'
+ 'Second occurrence: *anchor\n', conf)
+
+ def test_enabled_various_explicit_null(self):
+ conf = 'empty-values: {forbid-in-block-mappings: true}\n'
+ self.check('---\n'
+ 'null-key1: {?: val}\n', conf)
+
+ self.check('---\n'
+ 'null-alias: ~\n', conf)
+
+ self.check('---\n'
+ 'null-key2: {? !!null "": val}\n', conf)
diff --git a/yamllint/conf/default.yaml b/yamllint/conf/default.yaml
index 57cff64..8542d03 100644
--- a/yamllint/conf/default.yaml
+++ b/yamllint/conf/default.yaml
@@ -32,6 +32,8 @@ rules:
max: 2
max-start: 0
max-end: 0
+ empty-values:
+ forbid-in-block-mappings: false
hyphens:
max-spaces-after: 1
indentation:
diff --git a/yamllint/rules/__init__.py b/yamllint/rules/__init__.py
index 83dca76..189ec69 100644
--- a/yamllint/rules/__init__.py
+++ b/yamllint/rules/__init__.py
@@ -24,6 +24,7 @@ from yamllint.rules import (
document_end,
document_start,
empty_lines,
+ empty_values,
hyphens,
indentation,
key_duplicates,
@@ -45,6 +46,7 @@ _RULES = {
document_end.ID: document_end,
document_start.ID: document_start,
empty_lines.ID: empty_lines,
+ empty_values.ID: empty_values,
hyphens.ID: hyphens,
indentation.ID: indentation,
key_duplicates.ID: key_duplicates,
diff --git a/yamllint/rules/empty_values.py b/yamllint/rules/empty_values.py
new file mode 100644
index 0000000..2c1991e
--- /dev/null
+++ b/yamllint/rules/empty_values.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2017 Greg Dubicki
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+"""
+Use this rule to prevent nodes with empty content, that implicitly result in
+``null`` values.
+
+.. rubric:: Options
+
+* Use ``forbid-in-block-mappings`` to prevent implicit empty values in block
+ mappings.
+
+.. rubric:: Examples
+
+#. With ``empty-values: {forbid-in-block-mappings: true}``
+ the following code snippets would **PASS**:
+ ::
+
+ some-mapping:
+ sub-element: correctly indented
+
+ ::
+
+ explicitly-null: null
+
+ the following code snippets would **FAIL**:
+ ::
+
+ some-mapping:
+ sub-element: incorrectly indented
+
+ ::
+
+ implicitly-null:
+
+"""
+
+import yaml
+
+from yamllint.linter import LintProblem
+
+
+ID = 'empty-values'
+TYPE = 'token'
+CONF = {'forbid-in-block-mappings': bool}
+
+
+def check(conf, token, prev, next, nextnext, context):
+
+ if conf['forbid-in-block-mappings']:
+ if isinstance(token, yaml.ValueToken) and isinstance(next, (
+ yaml.KeyToken, yaml.BlockEndToken,
+ yaml.StreamEndToken, yaml.DocumentEndToken)):
+ yield LintProblem(token.start_mark.line + 1,
+ token.end_mark.column + 1,
+ 'empty value in block mapping')