Devicetree
 help / color / mirror / Atom feed
* [PATCH 0/6] dtc: dt-check-style: Cleanup plus new checks
@ 2026-08-29 19:53 Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 1/6] dtc: dt-check-style: Sort rule directory by name Krzysztof Kozlowski
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-29 19:53 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Saravana Kannan
  Cc: devicetree, linux-kernel, Krzysztof Kozlowski, sashiko-bot

Minor cleanup, more checks for node and property naming, improved
checking for whitespaces/tabs in code.

Best regards,
Krzysztof

---
Krzysztof Kozlowski (6):
      dtc: dt-check-style: Sort rule directory by name
      dtc: dt-check-style: Sort rule functions by name
      dtc: dt-check-style: Process property names with a question mark
      dtc: dt-check-style: Add test for node and property name styles
      dtc: dt-check-style: Check for indentation also in continued lines of DTS
      dtc: dt-check-style: Check for indentation also in continued lines of bindings example

 scripts/dtc/dt-check-style                         | 740 +++++++++++----------
 .../dtc/dt-style-selftest/bad/dts-node-name.dts    |  60 ++
 .../dt-style-selftest/bad/dts-property-name.dts    |  28 +
 .../dt-style-selftest/bad/yaml-mixed-indent.yaml   |   2 +
 .../dtc/dt-style-selftest/bad/yaml-node-name.yaml  |  54 ++
 .../dt-style-selftest/bad/yaml-property-name.yaml  |  46 ++
 .../expected/dts-mixed-indent.dts.txt              |   1 +
 .../expected/dts-node-name.dts.txt                 |  13 +
 .../expected/dts-property-name.dts.txt             |  14 +
 .../expected/yaml-mixed-indent.yaml.txt            |   1 +
 .../expected/yaml-node-name.yaml.txt               |   7 +
 .../expected/yaml-property-name.yaml.txt           |  14 +
 12 files changed, 629 insertions(+), 351 deletions(-)
---
base-commit: a5795bb908d1b8b125a59bbfe9f27f93929dc647
change-id: 20260826-dts-style-checker-sort-349052778b22

Best regards,
--  
Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/6] dtc: dt-check-style: Sort rule directory by name
  2026-08-29 19:53 [PATCH 0/6] dtc: dt-check-style: Cleanup plus new checks Krzysztof Kozlowski
@ 2026-08-29 19:53 ` Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 2/6] dtc: dt-check-style: Sort rule functions " Krzysztof Kozlowski
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-29 19:53 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Saravana Kannan
  Cc: devicetree, linux-kernel, Krzysztof Kozlowski

Sort the array with all the rules, so managing this will be at bit
easier and simultaneous addons of new rules less conflict-prone.  No
functional changes.

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 scripts/dtc/dt-check-style | 95 +++++++++++++++++++++++-----------------------
 1 file changed, 48 insertions(+), 47 deletions(-)

diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
index 487e76b85105..49b5c28563eb 100755
--- a/scripts/dtc/dt-check-style
+++ b/scripts/dtc/dt-check-style
@@ -1036,22 +1036,7 @@ RULES = [
     # 'relaxed' is the default; rules in this group must produce zero
     # output on a clean kernel tree (post the small prep-cleanup
     # commit at the head of this series).
-    Rule('trailing-whitespace', 'relaxed',
-         'no trailing whitespace on any line',
-         check_trailing_whitespace),
-    # See also check_redundant_whitespace_strict() and check_value_whitespace()
-    Rule('redundant-whitespace', 'relaxed',
-         'no redundant whitespace within code',
-         check_redundant_whitespace),
-    Rule('tab-in-yaml', 'relaxed',
-         'YAML (also DTS examples) may not contain tab characters',
-         check_tab_in_yaml_example, applies_to=('yaml',)),
-    Rule('mixed-indent-chars', 'relaxed',
-         'indent must not mix tabs and spaces',
-         check_mixed_indent_chars, applies_to=('dts', 'dtsi', 'dtso')),
-    Rule('unclosed-block-comment', 'relaxed',
-         'every /* block comment must close with */',
-         check_unclosed_block_comment),
+    # Rules are sorted here by group (relaxed, strict, stricter) and name
 
     # DTS files always use tabs; this is not negotiable per kernel
     # coding style (.dts files are real source). Relaxed mode.
@@ -1059,19 +1044,26 @@ RULES = [
          'DTS files: 1 tab per nesting level',
          check_indent_unit_dts,
          applies_to=('dts', 'dtsi', 'dtso')),
+    Rule('mixed-indent-chars', 'relaxed',
+         'indent must not mix tabs and spaces',
+         check_mixed_indent_chars, applies_to=('dts', 'dtsi', 'dtso')),
+    # See also check_redundant_whitespace_strict() and check_value_whitespace()
+    Rule('redundant-whitespace', 'relaxed',
+         'no redundant whitespace within code',
+         check_redundant_whitespace),
+    Rule('tab-in-yaml', 'relaxed',
+         'YAML (also DTS examples) may not contain tab characters',
+         check_tab_in_yaml_example, applies_to=('yaml',)),
+    Rule('trailing-whitespace', 'relaxed',
+         'no trailing whitespace on any line',
+         check_trailing_whitespace),
+    Rule('unclosed-block-comment', 'relaxed',
+         'every /* block comment must close with */',
+         check_unclosed_block_comment),
 
     # 'strict' rules are opt-in (e.g. for new submissions via
     # checkpatch.pl in a follow-up series). They flag many existing
     # files and can be promoted to relaxed once those are cleaned up.
-    Rule('indent-unit', 'strict',
-         'YAML: 2 or 4 spaces per level',
-         check_indent_unit_relaxed, applies_to=('yaml',)),
-    Rule('indent-unit-strict', 'strict',
-         'YAML: must be 4 spaces per level',
-         check_indent_unit_strict, applies_to=('yaml',)),
-    Rule('indent-consistent', 'strict',
-         'every line indented at depth * unit',
-         check_indent_consistent),
     Rule('blank-lines', 'strict',
          'no consecutive blanks; no blanks at node body edges',
          check_blank_lines),
@@ -1081,37 +1073,43 @@ RULES = [
     Rule('child-name-order', 'strict',
          'unaddressed siblings must be in natural-sort name order',
          check_child_name_order),
-    Rule('property-order', 'strict',
-         'canonical bucket + pairing + natural-sort order of properties',
-         check_property_order),
-    Rule('required-blank-lines', 'strict',
-         'blank line before child nodes and before "status"',
-         check_required_blank_lines),
+    Rule('continuation-alignment', 'strict',
+         'multi-line property continuations align under <, " or the value under <',
+         check_continuation_alignment),
     Rule('hex-case', 'strict',
          'hex literals must be lowercase',
          check_hex_case),
-    Rule('unit-address-format', 'strict',
-         'unit addresses must be lowercase hex without leading zeros',
-         check_unit_address_format),
-    # See also check_redundant_whitespace() and check_value_whitespace()
-    Rule('redundant-whitespace-strict', 'strict',
-         'no redundant whitespace within code',
-         check_redundant_whitespace_strict),
-    Rule('value-whitespace', 'strict',
-         'no whitespace directly inside <...> brackets',
-         check_value_whitespace),
-    Rule('node-close-alone', 'strict',
-         'closing brace must be on its own line',
-         check_node_close_alone),
+    Rule('indent-consistent', 'strict',
+         'every line indented at depth * unit',
+         check_indent_consistent),
+    Rule('indent-unit', 'strict',
+         'YAML: 2 or 4 spaces per level',
+         check_indent_unit_relaxed, applies_to=('yaml',)),
+    Rule('indent-unit-strict', 'strict',
+         'YAML: must be 4 spaces per level',
+         check_indent_unit_strict, applies_to=('yaml',)),
     Rule('line-length', 'strict',
          'lines must not exceed 80 columns',
          check_line_length, applies_to=('yaml',)),
     Rule('line-length-dts', 'stricter',
          'lines must not exceed 80 columns',
          check_line_length, applies_to=('dts', 'dtsi', 'dtso')),
-    Rule('continuation-alignment', 'strict',
-         'multi-line property continuations align under <, " or the value under <',
-         check_continuation_alignment),
+    Rule('node-close-alone', 'strict',
+         'closing brace must be on its own line',
+         check_node_close_alone),
+    Rule('property-order', 'strict',
+         'canonical bucket + pairing + natural-sort order of properties',
+         check_property_order),
+    # See also check_redundant_whitespace() and check_value_whitespace()
+    Rule('redundant-whitespace-strict', 'strict',
+         'no redundant whitespace within code',
+         check_redundant_whitespace_strict),
+    Rule('required-blank-lines', 'strict',
+         'blank line before child nodes and before "status"',
+         check_required_blank_lines),
+    Rule('unit-address-format', 'strict',
+         'unit addresses must be lowercase hex without leading zeros',
+         check_unit_address_format),
     Rule('unused-labels', 'strict',
          'every label must be &-referenced in the same example/file '
          '(skipped for .dtsi/.dtso since labels there are exported)',
@@ -1120,6 +1118,9 @@ RULES = [
          'every label must be &-referenced in the same example/file '
          '(skipped for .dtsi/.dtso since labels there are exported)',
          check_unused_labels, applies_to=('dts',)),
+    Rule('value-whitespace', 'strict',
+         'no whitespace directly inside <...> brackets',
+         check_value_whitespace),
 ]
 
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/6] dtc: dt-check-style: Sort rule functions by name
  2026-08-29 19:53 [PATCH 0/6] dtc: dt-check-style: Cleanup plus new checks Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 1/6] dtc: dt-check-style: Sort rule directory by name Krzysztof Kozlowski
@ 2026-08-29 19:53 ` Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 3/6] dtc: dt-check-style: Process property names with a question mark Krzysztof Kozlowski
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-29 19:53 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Saravana Kannan
  Cc: devicetree, linux-kernel, Krzysztof Kozlowski

Sort all the functions implementing style rules by name, so managing
this will be at bit easier and simultaneous addons of new rules less
conflict-prone.  No functional changes, except adding underscore to
_detect_indent_unit() to match other helpers used by the rules.

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 scripts/dtc/dt-check-style | 607 +++++++++++++++++++++++----------------------
 1 file changed, 304 insertions(+), 303 deletions(-)

diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
index 49b5c28563eb..5385a03f377e 100755
--- a/scripts/dtc/dt-check-style
+++ b/scripts/dtc/dt-check-style
@@ -343,88 +343,8 @@ class Rule:
 
 # --- individual rule check functions --------------------------------------
 
-def check_trailing_whitespace(ctx):
-    for dl in ctx.lines:
-        if dl.raw != dl.raw.rstrip():
-            yield (dl.lineno, 'trailing whitespace')
 
-
-def _check_redundant_whitespace(dl):
-    if dl.linetype in (LineType.COMMENT, LineType.COMMENT_BODY,
-                        LineType.COMMENT_END, LineType.COMMENT_START,
-                        LineType.PREPROCESSOR):
-        return
-    if re.search(r'(\s\s+|\t)\{', dl.code):
-        yield (dl.lineno, 'extra whitespace before {')
-    if re.search(r':(\s\s+|\t)', dl.code):
-        yield (dl.lineno, 'extra whitespace after :')
-    if re.search(r'\s+;', dl.code):
-        yield (dl.lineno, 'extra whitespace before ;')
-
-
-def check_redundant_whitespace(ctx):
-    """No whitespace between brackets or other code elements.
-    See also check_value_whitespace() for more checks."""
-    for dl in ctx.lines:
-        yield from _check_redundant_whitespace(dl)
-        for cont in dl.continuations:
-            yield from _check_redundant_whitespace(cont)
-
-
-def check_redundant_whitespace_strict(ctx):
-    """No whitespace between brackets or other code elements.
-    See also check_value_whitespace() for more checks."""
-    for dl in ctx.lines:
-        if dl.linetype in (LineType.COMMENT, LineType.COMMENT_BODY,
-                           LineType.COMMENT_END, LineType.COMMENT_START,
-                           LineType.PREPROCESSOR):
-            continue
-        if re.search(r'(\s\s+|\t)=', dl.code):
-            yield (dl.lineno, 'extra whitespace before =')
-        if re.search(r'=(\s\s+|\t)', dl.code):
-            yield (dl.lineno, 'extra whitespace after =')
-
-
-def check_tab_in_yaml_example(ctx):
-    """Reject literal tabs in DTS lines when input is YAML.
-
-    For YAML examples, indent and content must use spaces. Tabs inside
-    a #define value are tolerated (those are CPP macros, not DTS).
-    For .dts files, this rule does not apply -- tabs are required.
-    """
-    if ctx.file_type != 'yaml':
-        return
-    for dl in ctx.lines:
-        if dl.linetype == LineType.PREPROCESSOR:
-            continue
-        if dl.linetype == LineType.BLANK:
-            continue
-        if '\t' in dl.raw:
-            yield (dl.lineno, 'tab character not allowed in DTS example')
-
-
-def check_mixed_indent_chars(ctx):
-    """Indent must be all-tabs, except for aligning indentation (comments
-    or continued lines)."""
-    for dl in ctx.lines:
-        if not dl.indent_str:
-            continue
-        if dl.linetype == LineType.PREPROCESSOR:
-            continue
-        if re.search(r' \t', dl.indent_str):
-            yield (dl.lineno, 'mixed tabs and spaces in indent')
-        if dl.indent_str.count(' ') > 7:
-            yield (dl.lineno, 'too many space characters in indent (more than 7)')
-        for cont in dl.continuations:
-            if not cont.indent_str:
-                continue
-            if cont.linetype == LineType.PREPROCESSOR:
-                continue
-            if re.search(r' \t', cont.indent_str):
-                yield (cont.lineno, 'mixed tabs and spaces in indent')
-
-
-def detect_indent_unit(ctx):
+def _detect_indent_unit(ctx):
     """Find the indent unit used at depth 1 in this block.
 
     Returns tuple of string (one of: '  ' (2 spaces), '    ' (4 spaces),
@@ -450,85 +370,42 @@ def detect_indent_unit(ctx):
     return (None, None)
 
 
-def check_indent_unit_relaxed(ctx):
-    """YAML examples: 2 or 4 spaces. Never tabs or other widths."""
-    (unit, lineno) = detect_indent_unit(ctx)
-    if unit is None:
-        return
-    if unit not in ('  ', '    '):
-        yield (lineno, 'indent unit must be 2 or 4 spaces, got %r' % unit)
+def _display_col(text):
+    """Visual column width of text, with tabs expanded to the next
+    8-column stop, matching how printf and most editors render a
+    line and the kernel-wide line length convention."""
+    col = 0
+    for ch in text:
+        if ch == '\t':
+            col = (col // 8 + 1) * 8
+        else:
+            col += 1
+    return col
 
 
-def check_indent_unit_dts(ctx):
-    """DTS files: 1 tab per level. Always required."""
-    (unit, lineno) = detect_indent_unit(ctx)
-    if unit is None:
-        return
-    if unit != '\t':
-        yield (lineno, 'indent unit must be 1 tab in DTS, got %r' % unit)
+def _natural_sort_key(s):
+    """Split a string into a tuple of (kind, value) pairs that compares
+    numeric runs as ints, so 'foo10' sorts after 'foo2'."""
+    parts = []
+    for part in re.split(r'(\d+)', s):
+        if part.isdigit():
+            parts.append((0, int(part)))
+        else:
+            parts.append((1, part))
+    return tuple(parts)
 
 
-def check_indent_unit_strict(ctx):
-    """YAML: must be exactly 4 spaces. DTS: 1 tab (same as relaxed)."""
-    (unit, lineno) = detect_indent_unit(ctx)
-    if unit is None:
-        return
-    if ctx.file_type == 'yaml':
-        if unit != '    ':
-            yield (lineno, 'indent unit must be 4 spaces in strict mode, '
-                   'got %r' % unit)
-
-
-def check_indent_consistent(ctx):
-    """All indented lines must be a multiple of the detected unit."""
-    (unit, lineno) = detect_indent_unit(ctx)
-    if unit is None:
-        return
-    if ctx.file_type == 'yaml':
-        if unit not in ('  ', '    '):
-            return  # let check_indent_unit_* report this
-    else:
-        if unit != '\t':
-            return
-
-    for dl in ctx.lines:
-        if dl.linetype in (LineType.BLANK, LineType.PREPROCESSOR):
-            continue
-        if dl.linetype == LineType.CONTINUATION:
-            continue   # continuations align to <, not to indent unit
-        if dl.linetype in (LineType.COMMENT_BODY, LineType.COMMENT_END):
-            continue
-        if not dl.indent_str:
-            continue
-        # The indent must be 'unit' repeated dl.depth times, exactly.
-        # NODE_CLOSE lines have depth equal to the post-decrement value,
-        # which matches the indent expected.
-        expected = unit * dl.depth
-        if dl.indent_str != expected:
-            yield (dl.lineno,
-                   'indent mismatch (expected depth %d * %r)' %
-                   (dl.depth, unit))
-
-
-def check_blank_lines(ctx):
-    """No two consecutive blank lines, no leading/trailing blank lines
-    in any node body."""
-    lines = ctx.lines
-    # Consecutive blanks
-    for i in range(1, len(lines)):
-        if lines[i].linetype == LineType.BLANK and \
-                lines[i - 1].linetype == LineType.BLANK:
-            yield (lines[i].lineno, 'consecutive blank lines')
-    # Blank right after { or right before }
-    for i, dl in enumerate(lines):
-        if dl.linetype != LineType.BLANK:
-            continue
-        prev = lines[i - 1] if i > 0 else None
-        nxt = lines[i + 1] if i + 1 < len(lines) else None
-        if prev is not None and prev.linetype == LineType.NODE_OPEN:
-            yield (dl.lineno, 'blank line at start of node body')
-        if nxt is not None and nxt.linetype == LineType.NODE_CLOSE:
-            yield (dl.lineno, 'blank line at end of node body')
+def _strip_strings_and_comments(text):
+    """Remove string literals and /* */ + // comments from a single
+    line, replacing them with empty strings. Used so syntactic checks
+    (whitespace, hex case, etc.) don't false-positive on contents of
+    quoted strings or comments. An unclosed /* on the line is treated
+    as a comment running to end of line."""
+    text = re.sub(r'"(?:[^"\\]|\\.)*"', '""', text)
+    text = re.sub(r'/\*.*?\*/', '', text)
+    text = re.sub(r'/\*.*$', '', text)
+    text = re.sub(r'//.*$', '', text)
+    return text
 
 
 def _walk_bodies(lines):
@@ -559,16 +436,25 @@ def _walk_bodies(lines):
         yield body_stack.pop()
 
 
-def _natural_sort_key(s):
-    """Split a string into a tuple of (kind, value) pairs that compares
-    numeric runs as ints, so 'foo10' sorts after 'foo2'."""
-    parts = []
-    for part in re.split(r'(\d+)', s):
-        if part.isdigit():
-            parts.append((0, int(part)))
-        else:
-            parts.append((1, part))
-    return tuple(parts)
+def check_blank_lines(ctx):
+    """No two consecutive blank lines, no leading/trailing blank lines
+    in any node body."""
+    lines = ctx.lines
+    # Consecutive blanks
+    for i in range(1, len(lines)):
+        if lines[i].linetype == LineType.BLANK and \
+                lines[i - 1].linetype == LineType.BLANK:
+            yield (lines[i].lineno, 'consecutive blank lines')
+    # Blank right after { or right before }
+    for i, dl in enumerate(lines):
+        if dl.linetype != LineType.BLANK:
+            continue
+        prev = lines[i - 1] if i > 0 else None
+        nxt = lines[i + 1] if i + 1 < len(lines) else None
+        if prev is not None and prev.linetype == LineType.NODE_OPEN:
+            yield (dl.lineno, 'blank line at start of node body')
+        if nxt is not None and nxt.linetype == LineType.NODE_CLOSE:
+            yield (dl.lineno, 'blank line at end of node body')
 
 
 def check_child_address_order(ctx):
@@ -624,6 +510,172 @@ def check_child_name_order(ctx):
                        'child node %r out of name order' % dl.node_name)
 
 
+def check_continuation_alignment(ctx):
+    """A multi-line property's continuation lines must align their
+    first non-whitespace character to the display column of:
+    1. the first '<' or '"' after the '=' in the leading line, if continuation is with '<' or '"'
+    2. the first value, if the continuation is still the same phandle.
+    Display columns are used so tab-indented .dts files (where a continuation
+    aligns with tabs plus spaces) are compared correctly."""
+    for dl in ctx.lines:
+        if dl.linetype != LineType.PROPERTY:
+            continue
+        if not dl.continuations:
+            continue
+        eq = dl.raw.find('=')
+        if eq < 0:
+            continue
+        # First '<' or '"' after '=', but ignore comments and strip trailing
+        # whitespace (e.g. remaining after removing the comment)
+        rest = _strip_strings_and_comments(dl.raw[eq + 1:]).rstrip()
+        m = re.search(r'\s*([<"])', rest)
+        if not m:
+            continue
+        dl_value_complete = rest.endswith('",') or rest.endswith('>,')
+        target_col = _display_col(_strip_strings_and_comments(dl.raw[:eq + 1 + m.start(1)]))
+        for cont in dl.continuations:
+            target_offset = 0
+            err_msg_explanation = 'to < or "'
+            if not dl_value_complete:
+                target_offset = 1
+                err_msg_explanation = 'to the value under <'
+            if _display_col(cont.indent_str) != target_col + target_offset:
+                yield (cont.lineno,
+                       'continuation should align to column %d '
+                       '(%s)' % (target_col + target_offset + 1, err_msg_explanation))
+            # Align to the value within <> or "" of continuation (so the previous line)
+            dl_value_complete = cont.stripped.endswith('",') or cont.stripped.endswith('>,')
+
+
+def check_hex_case(ctx):
+    """Hex literals (0xN) must use lowercase digits and prefix."""
+    for dl in ctx.lines:
+        if dl.linetype in (LineType.BLANK, LineType.COMMENT,
+                           LineType.COMMENT_START, LineType.COMMENT_BODY,
+                           LineType.COMMENT_END, LineType.PREPROCESSOR):
+            continue
+        for m in re.finditer(r'\b0[xX][0-9a-fA-F]+\b', dl.code):
+            lit = m.group(0)
+            if any(c.isupper() for c in lit[2:]) or lit[1] == 'X':
+                yield (dl.lineno,
+                       'hex literal %r must be lowercase' % lit)
+
+
+def check_indent_consistent(ctx):
+    """All indented lines must be a multiple of the detected unit."""
+    (unit, lineno) = _detect_indent_unit(ctx)
+    if unit is None:
+        return
+    if ctx.file_type == 'yaml':
+        if unit not in ('  ', '    '):
+            return  # let check_indent_unit_* report this
+    else:
+        if unit != '\t':
+            return
+
+    for dl in ctx.lines:
+        if dl.linetype in (LineType.BLANK, LineType.PREPROCESSOR):
+            continue
+        if dl.linetype == LineType.CONTINUATION:
+            continue   # continuations align to <, not to indent unit
+        if dl.linetype in (LineType.COMMENT_BODY, LineType.COMMENT_END):
+            continue
+        if not dl.indent_str:
+            continue
+        # The indent must be 'unit' repeated dl.depth times, exactly.
+        # NODE_CLOSE lines have depth equal to the post-decrement value,
+        # which matches the indent expected.
+        expected = unit * dl.depth
+        if dl.indent_str != expected:
+            yield (dl.lineno,
+                   'indent mismatch (expected depth %d * %r)' %
+                   (dl.depth, unit))
+
+
+def check_indent_unit_dts(ctx):
+    """DTS files: 1 tab per level. Always required."""
+    (unit, lineno) = _detect_indent_unit(ctx)
+    if unit is None:
+        return
+    if unit != '\t':
+        yield (lineno, 'indent unit must be 1 tab in DTS, got %r' % unit)
+
+
+def check_indent_unit_relaxed(ctx):
+    """YAML examples: 2 or 4 spaces. Never tabs or other widths."""
+    (unit, lineno) = _detect_indent_unit(ctx)
+    if unit is None:
+        return
+    if unit not in ('  ', '    '):
+        yield (lineno, 'indent unit must be 2 or 4 spaces, got %r' % unit)
+
+
+def check_indent_unit_strict(ctx):
+    """YAML: must be exactly 4 spaces. DTS: 1 tab (same as relaxed)."""
+    (unit, lineno) = _detect_indent_unit(ctx)
+    if unit is None:
+        return
+    if ctx.file_type == 'yaml':
+        if unit != '    ':
+            yield (lineno, 'indent unit must be 4 spaces in strict mode, '
+                   'got %r' % unit)
+
+
+def check_line_length(ctx):
+    """Lines must not exceed 80 columns; tabs count as 8 (see
+    _display_col)."""
+    for dl in ctx.lines:
+        if dl.linetype == LineType.BLANK:
+            continue
+        cols = _display_col(dl.raw)
+        if cols > 80:
+            yield (dl.lineno,
+                   'line exceeds 80 columns (%d)' % cols)
+
+
+def check_mixed_indent_chars(ctx):
+    """Indent must be all-tabs, except for aligning indentation (comments
+    or continued lines)."""
+    for dl in ctx.lines:
+        if not dl.indent_str:
+            continue
+        if dl.linetype == LineType.PREPROCESSOR:
+            continue
+        if re.search(r' \t', dl.indent_str):
+            yield (dl.lineno, 'mixed tabs and spaces in indent')
+        if dl.indent_str.count(' ') > 7:
+            yield (dl.lineno, 'too many space characters in indent (more than 7)')
+        for cont in dl.continuations:
+            if not cont.indent_str:
+                continue
+            if cont.linetype == LineType.PREPROCESSOR:
+                continue
+            if re.search(r' \t', cont.indent_str):
+                yield (cont.lineno, 'mixed tabs and spaces in indent')
+
+
+def check_node_close_alone(ctx):
+    """The closing '};' of a node must be on its own line. The
+    classifier accepts a canonical "}" or "};" as NODE_CLOSE; a line
+    that is all closures (e.g. "}; };") is still NODE_CLOSE for depth
+    tracking but is flagged here via dl.closures. Any other line that
+    still contains '};' (in code, not in strings or comments) is
+    mixing a node close with something else."""
+    for dl in ctx.lines:
+        if dl.linetype == LineType.NODE_CLOSE:
+            if dl.closures > 1:
+                yield (dl.lineno,
+                       'closing brace must be on its own line')
+            continue
+        if dl.linetype in (LineType.BLANK, LineType.COMMENT,
+                           LineType.COMMENT_START, LineType.COMMENT_BODY,
+                           LineType.COMMENT_END, LineType.PREPROCESSOR):
+            continue
+        if '};' in dl.code:
+            yield (dl.lineno,
+                   'closing brace must be on its own line')
+
+
 def _property_bucket(name):
     """Return the canonical bucket index for a property:
        0 device_type
@@ -769,17 +821,40 @@ def check_property_order(ctx):
                        (p.prop_name, prev.prop_name))
 
 
-def _strip_strings_and_comments(text):
-    """Remove string literals and /* */ + // comments from a single
-    line, replacing them with empty strings. Used so syntactic checks
-    (whitespace, hex case, etc.) don't false-positive on contents of
-    quoted strings or comments. An unclosed /* on the line is treated
-    as a comment running to end of line."""
-    text = re.sub(r'"(?:[^"\\]|\\.)*"', '""', text)
-    text = re.sub(r'/\*.*?\*/', '', text)
-    text = re.sub(r'/\*.*$', '', text)
-    text = re.sub(r'//.*$', '', text)
-    return text
+def _check_redundant_whitespace(dl):
+    if dl.linetype in (LineType.COMMENT, LineType.COMMENT_BODY,
+                        LineType.COMMENT_END, LineType.COMMENT_START,
+                        LineType.PREPROCESSOR):
+        return
+    if re.search(r'(\s\s+|\t)\{', dl.code):
+        yield (dl.lineno, 'extra whitespace before {')
+    if re.search(r':(\s\s+|\t)', dl.code):
+        yield (dl.lineno, 'extra whitespace after :')
+    if re.search(r'\s+;', dl.code):
+        yield (dl.lineno, 'extra whitespace before ;')
+
+
+def check_redundant_whitespace(ctx):
+    """No whitespace between brackets or other code elements.
+    See also check_value_whitespace() for more checks."""
+    for dl in ctx.lines:
+        yield from _check_redundant_whitespace(dl)
+        for cont in dl.continuations:
+            yield from _check_redundant_whitespace(cont)
+
+
+def check_redundant_whitespace_strict(ctx):
+    """No whitespace between brackets or other code elements.
+    See also check_value_whitespace() for more checks."""
+    for dl in ctx.lines:
+        if dl.linetype in (LineType.COMMENT, LineType.COMMENT_BODY,
+                           LineType.COMMENT_END, LineType.COMMENT_START,
+                           LineType.PREPROCESSOR):
+            continue
+        if re.search(r'(\s\s+|\t)=', dl.code):
+            yield (dl.lineno, 'extra whitespace before =')
+        if re.search(r'=(\s\s+|\t)', dl.code):
+            yield (dl.lineno, 'extra whitespace after =')
 
 
 def check_required_blank_lines(ctx):
@@ -841,18 +916,49 @@ def check_required_blank_lines(ctx):
             between_blanks = 0
 
 
-def check_hex_case(ctx):
-    """Hex literals (0xN) must use lowercase digits and prefix."""
+def check_tab_in_yaml_example(ctx):
+    """Reject literal tabs in DTS lines when input is YAML.
+
+    For YAML examples, indent and content must use spaces. Tabs inside
+    a #define value are tolerated (those are CPP macros, not DTS).
+    For .dts files, this rule does not apply -- tabs are required.
+    """
+    if ctx.file_type != 'yaml':
+        return
     for dl in ctx.lines:
-        if dl.linetype in (LineType.BLANK, LineType.COMMENT,
-                           LineType.COMMENT_START, LineType.COMMENT_BODY,
-                           LineType.COMMENT_END, LineType.PREPROCESSOR):
+        if dl.linetype == LineType.PREPROCESSOR:
             continue
-        for m in re.finditer(r'\b0[xX][0-9a-fA-F]+\b', dl.code):
-            lit = m.group(0)
-            if any(c.isupper() for c in lit[2:]) or lit[1] == 'X':
-                yield (dl.lineno,
-                       'hex literal %r must be lowercase' % lit)
+        if dl.linetype == LineType.BLANK:
+            continue
+        if '\t' in dl.raw:
+            yield (dl.lineno, 'tab character not allowed in DTS example')
+
+
+def check_trailing_whitespace(ctx):
+    for dl in ctx.lines:
+        if dl.raw != dl.raw.rstrip():
+            yield (dl.lineno, 'trailing whitespace')
+
+
+def check_unclosed_block_comment(ctx):
+    """Every /* must have a matching */ in the same block. Catches both
+    a comment opened on its own line (COMMENT_START) and a tail comment
+    opened on a PROPERTY or other code line (where in_block_comment is
+    set by _split_code so the next line becomes COMMENT_BODY without a
+    preceding COMMENT_START)."""
+    open_lineno = None
+    for dl in ctx.lines:
+        if dl.linetype == LineType.COMMENT_START:
+            open_lineno = dl.lineno
+        elif dl.linetype == LineType.COMMENT_END:
+            open_lineno = None
+        elif dl.linetype == LineType.COMMENT_BODY and open_lineno is None:
+            # Block was opened by a /* tail on a code line; report at
+            # the first orphan body line since the originating line is
+            # already classified as something else.
+            open_lineno = dl.lineno
+    if open_lineno is not None:
+        yield (open_lineno, 'unclosed /* block comment')
 
 
 def check_unit_address_format(ctx):
@@ -886,6 +992,17 @@ def check_unit_address_format(ctx):
                 break
 
 
+def check_unused_labels(ctx):
+    """Labels defined but never referenced are clutter."""
+    defined, referenced = collect_labels_and_refs(ctx.text)
+    for label in sorted(defined - referenced):
+        # Find the line where this label is defined for line-number
+        # reporting.
+        m = re.search(r'(?m)^.*\b' + re.escape(label) + r'\s*:', ctx.text)
+        lineno = ctx.text[:m.start()].count('\n') + 1 if m else 1
+        yield (lineno, 'label %r defined but never &-referenced' % label)
+
+
 def check_value_whitespace(ctx):
     """A <...> cell list must have no whitespace directly after '<'
     or directly before '>'. Continuation lines are joined onto the
@@ -914,122 +1031,6 @@ def check_value_whitespace(ctx):
                 break
 
 
-def check_node_close_alone(ctx):
-    """The closing '};' of a node must be on its own line. The
-    classifier accepts a canonical "}" or "};" as NODE_CLOSE; a line
-    that is all closures (e.g. "}; };") is still NODE_CLOSE for depth
-    tracking but is flagged here via dl.closures. Any other line that
-    still contains '};' (in code, not in strings or comments) is
-    mixing a node close with something else."""
-    for dl in ctx.lines:
-        if dl.linetype == LineType.NODE_CLOSE:
-            if dl.closures > 1:
-                yield (dl.lineno,
-                       'closing brace must be on its own line')
-            continue
-        if dl.linetype in (LineType.BLANK, LineType.COMMENT,
-                           LineType.COMMENT_START, LineType.COMMENT_BODY,
-                           LineType.COMMENT_END, LineType.PREPROCESSOR):
-            continue
-        if '};' in dl.code:
-            yield (dl.lineno,
-                   'closing brace must be on its own line')
-
-
-def _display_col(text):
-    """Visual column width of text, with tabs expanded to the next
-    8-column stop, matching how printf and most editors render a
-    line and the kernel-wide line length convention."""
-    col = 0
-    for ch in text:
-        if ch == '\t':
-            col = (col // 8 + 1) * 8
-        else:
-            col += 1
-    return col
-
-
-def check_line_length(ctx):
-    """Lines must not exceed 80 columns; tabs count as 8 (see
-    _display_col)."""
-    for dl in ctx.lines:
-        if dl.linetype == LineType.BLANK:
-            continue
-        cols = _display_col(dl.raw)
-        if cols > 80:
-            yield (dl.lineno,
-                   'line exceeds 80 columns (%d)' % cols)
-
-
-def check_continuation_alignment(ctx):
-    """A multi-line property's continuation lines must align their
-    first non-whitespace character to the display column of:
-    1. the first '<' or '"' after the '=' in the leading line, if continuation is with '<' or '"'
-    2. the first value, if the continuation is still the same phandle.
-    Display columns are used so tab-indented .dts files (where a continuation
-    aligns with tabs plus spaces) are compared correctly."""
-    for dl in ctx.lines:
-        if dl.linetype != LineType.PROPERTY:
-            continue
-        if not dl.continuations:
-            continue
-        eq = dl.raw.find('=')
-        if eq < 0:
-            continue
-        # First '<' or '"' after '=', but ignore comments and strip trailing
-        # whitespace (e.g. remaining after removing the comment)
-        rest = _strip_strings_and_comments(dl.raw[eq + 1:]).rstrip()
-        m = re.search(r'\s*([<"])', rest)
-        if not m:
-            continue
-        dl_value_complete = rest.endswith('",') or rest.endswith('>,')
-        target_col = _display_col(_strip_strings_and_comments(dl.raw[:eq + 1 + m.start(1)]))
-        for cont in dl.continuations:
-            target_offset = 0
-            err_msg_explanation = 'to < or "'
-            if not dl_value_complete:
-                target_offset = 1
-                err_msg_explanation = 'to the value under <'
-            if _display_col(cont.indent_str) != target_col + target_offset:
-                yield (cont.lineno,
-                       'continuation should align to column %d '
-                       '(%s)' % (target_col + target_offset + 1, err_msg_explanation))
-            # Align to the value within <> or "" of continuation (so the previous line)
-            dl_value_complete = cont.stripped.endswith('",') or cont.stripped.endswith('>,')
-
-
-def check_unclosed_block_comment(ctx):
-    """Every /* must have a matching */ in the same block. Catches both
-    a comment opened on its own line (COMMENT_START) and a tail comment
-    opened on a PROPERTY or other code line (where in_block_comment is
-    set by _split_code so the next line becomes COMMENT_BODY without a
-    preceding COMMENT_START)."""
-    open_lineno = None
-    for dl in ctx.lines:
-        if dl.linetype == LineType.COMMENT_START:
-            open_lineno = dl.lineno
-        elif dl.linetype == LineType.COMMENT_END:
-            open_lineno = None
-        elif dl.linetype == LineType.COMMENT_BODY and open_lineno is None:
-            # Block was opened by a /* tail on a code line; report at
-            # the first orphan body line since the originating line is
-            # already classified as something else.
-            open_lineno = dl.lineno
-    if open_lineno is not None:
-        yield (open_lineno, 'unclosed /* block comment')
-
-
-def check_unused_labels(ctx):
-    """Labels defined but never referenced are clutter."""
-    defined, referenced = collect_labels_and_refs(ctx.text)
-    for label in sorted(defined - referenced):
-        # Find the line where this label is defined for line-number
-        # reporting.
-        m = re.search(r'(?m)^.*\b' + re.escape(label) + r'\s*:', ctx.text)
-        lineno = ctx.text[:m.start()].count('\n') + 1 if m else 1
-        yield (lineno, 'label %r defined but never &-referenced' % label)
-
-
 # --- registry --------------------------------------------------------------
 
 RULES = [

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/6] dtc: dt-check-style: Process property names with a question mark
  2026-08-29 19:53 [PATCH 0/6] dtc: dt-check-style: Cleanup plus new checks Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 1/6] dtc: dt-check-style: Sort rule directory by name Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 2/6] dtc: dt-check-style: Sort rule functions " Krzysztof Kozlowski
@ 2026-08-29 19:53 ` Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 4/6] dtc: dt-check-style: Add test for node and property name styles Krzysztof Kozlowski
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-29 19:53 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Saravana Kannan
  Cc: devicetree, linux-kernel, Krzysztof Kozlowski

Devicetree spec allows question mark '?' in property names and such code
is also accepted by DTC, thus catch such properties as a valid code,
even if discouraged.  Otherwise certain rules checking for property
names would ignore that line.

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 scripts/dtc/dt-check-style | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
index 5385a03f377e..7c3ee1432dc6 100755
--- a/scripts/dtc/dt-check-style
+++ b/scripts/dtc/dt-check-style
@@ -280,7 +280,7 @@ def parse_node_header(dl):
 
 
 def parse_property_name(dl):
-    m = re.match(r'^([a-zA-Z0-9#][a-zA-Z0-9,._+#-]*)\s*[=;]', dl.stripped)
+    m = re.match(r'^([a-zA-Z0-9#][a-zA-Z0-9,._+?#-]*)\s*[=;]', dl.stripped)
     if m:
         dl.prop_name = m.group(1)
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/6] dtc: dt-check-style: Add test for node and property name styles
  2026-08-29 19:53 [PATCH 0/6] dtc: dt-check-style: Cleanup plus new checks Krzysztof Kozlowski
                   ` (2 preceding siblings ...)
  2026-08-29 19:53 ` [PATCH 3/6] dtc: dt-check-style: Process property names with a question mark Krzysztof Kozlowski
@ 2026-08-29 19:53 ` Krzysztof Kozlowski
  2026-08-30 19:59   ` Rob Herring
  2026-08-29 19:53 ` [PATCH 5/6] dtc: dt-check-style: Check for indentation also in continued lines of DTS Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 6/6] dtc: dt-check-style: Check for indentation also in continued lines of bindings example Krzysztof Kozlowski
  5 siblings, 1 reply; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-29 19:53 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Saravana Kannan
  Cc: devicetree, linux-kernel, Krzysztof Kozlowski

Common style violation is usage of underscores in node or property
names, so add rules for following recommended names.  There is plenty of
legacy approach, so add the rules only for 'strict' mode of checking.

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 scripts/dtc/dt-check-style                         | 31 +++++++++++
 .../dtc/dt-style-selftest/bad/dts-node-name.dts    | 60 ++++++++++++++++++++++
 .../dt-style-selftest/bad/dts-property-name.dts    | 28 ++++++++++
 .../dtc/dt-style-selftest/bad/yaml-node-name.yaml  | 54 +++++++++++++++++++
 .../dt-style-selftest/bad/yaml-property-name.yaml  | 46 +++++++++++++++++
 .../expected/dts-node-name.dts.txt                 | 13 +++++
 .../expected/dts-property-name.dts.txt             | 14 +++++
 .../expected/yaml-node-name.yaml.txt               |  7 +++
 .../expected/yaml-property-name.yaml.txt           | 14 +++++
 9 files changed, 267 insertions(+)

diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
index 7c3ee1432dc6..16586d0029ff 100755
--- a/scripts/dtc/dt-check-style
+++ b/scripts/dtc/dt-check-style
@@ -676,6 +676,31 @@ def check_node_close_alone(ctx):
                    'closing brace must be on its own line')
 
 
+def check_node_name(ctx):
+    """Only recommended characters are used in node names."""
+    for dl in ctx.lines:
+        if not dl.node_name in (None, '/'):
+            if not re.match(r'[0-9a-z][0-9a-z-]*(?<!-)$', dl.node_name):
+                yield (dl.lineno, f'node name "{dl.node_name}" is using discouraged style')
+
+
+def check_property_name(ctx):
+    """Only recommended characters are used in property names."""
+    for dl in ctx.lines:
+        if dl.prop_name:
+            exceptions = ['cpu_off', 'cpu_on', 'cpu_suspend',
+                          'device_type', 'dr_mode',
+                          'mmc-hs200-1_2v', 'mmc-hs200-1_8v', 'mmc-hs400-1_2v', 'mmc-hs400-1_8v',
+                          'opp-avg-kBps', 'opp-peak-kBps', 'phy_type']
+
+            if re.match(r'\#([0-9a-z][0-9a-z-]*(?<!-),)?[a-z-]+-cells$', dl.prop_name):
+                continue
+            if dl.prop_name in exceptions:
+                continue
+            if not re.match(r'([0-9a-z][0-9a-z-]*(?<!-),)?[0-9a-z][0-9a-z-]*(?<!-)$', dl.prop_name):
+                yield (dl.lineno, f'property name "{dl.prop_name}" is using discouraged style')
+
+
 def _property_bucket(name):
     """Return the canonical bucket index for a property:
        0 device_type
@@ -1098,6 +1123,12 @@ RULES = [
     Rule('node-close-alone', 'strict',
          'closing brace must be on its own line',
          check_node_close_alone),
+    Rule('node-name', 'strict',
+         'node names use only recommended characters (see DTS Coding Style)',
+         check_node_name),
+    Rule('property-name', 'strict',
+         'property names use only recommended characters (see DTS Coding Style)',
+         check_property_name),
     Rule('property-order', 'strict',
          'canonical bucket + pairing + natural-sort order of properties',
          check_property_order),
diff --git a/scripts/dtc/dt-style-selftest/bad/dts-node-name.dts b/scripts/dtc/dt-style-selftest/bad/dts-node-name.dts
new file mode 100644
index 000000000000..b897cbd74dd6
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/dts-node-name.dts
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	foo_bar {
+		compatible = "example,test-node-name";
+	};
+
+	foo,bar {
+		compatible = "example,test-node-name";
+	};
+
+	foo.bar {
+		compatible = "example,test-node-name";
+	};
+
+	foo+bar {
+		compatible = "example,test-node-name";
+	};
+
+	foo-bar- {
+		compatible = "example,test-node-name";
+	};
+
+	soc@0 {
+		compatible = "simple-bus";
+		ranges = <0 0 0 0xc0000000>;
+
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		foo_bar@1000 {
+			compatible = "example,test-node-name";
+			reg = <0x1000 0x100>;
+		};
+
+		foo,bar@2000 {
+			compatible = "example,test-node-name";
+			reg = <0x2000 0x100>;
+		};
+
+		foo.bar@3000 {
+			compatible = "example,test-node-name";
+			reg = <0x3000 0x100>;
+		};
+
+		foo+bar@4000 {
+			compatible = "example,test-node-name";
+			reg = <0x4000 0x100>;
+		};
+
+		foo-bar-@5000 {
+			compatible = "example,test-node-name";
+			reg = <0x5000 0x100>;
+		};
+	};
+};
diff --git a/scripts/dtc/dt-style-selftest/bad/dts-property-name.dts b/scripts/dtc/dt-style-selftest/bad/dts-property-name.dts
new file mode 100644
index 000000000000..110e33a524dd
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/dts-property-name.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	foo {
+		compatible = "example,test-property-name";
+
+		property#foo;
+		property+foo;
+		property-foo;
+		property-foo-;
+		property.foo;
+		property?foo;
+		property_foo;
+
+		vendor,property#foo;
+		vendor,property+foo;
+		vendor,property,foo;
+		vendor,property-foo;
+		vendor,property-foo-;
+		vendor,property.foo;
+		vendor,property?foo;
+		vendor,property_foo;
+	};
+};
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-node-name.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-node-name.yaml
new file mode 100644
index 000000000000..03373cae9a66
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-node-name.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-node-name.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with incorrect node names
+
+maintainers:
+  - Rob Herring <robh@kernel.org>
+
+properties:
+  compatible:
+    const: example,test-node-name
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    foo_bar@1000 {
+        compatible = "example,test-node-name";
+        reg = <0x1000 0x100>;
+    };
+
+    foo,bar@2000 {
+        compatible = "example,test-node-name";
+        reg = <0x2000 0x100>;
+    };
+
+    foo.bar@3000 {
+        compatible = "example,test-node-name";
+        reg = <0x3000 0x100>;
+    };
+
+    foo+bar@4000 {
+        compatible = "example,test-node-name";
+        reg = <0x4000 0x100>;
+    };
+
+    foo-bar-@5000 {
+        compatible = "example,test-node-name";
+        reg = <0x5000 0x100>;
+    };
+
+    // TODO handle also "-foo-bar" which is against spec but accepted by DTC
+
+    foo_bar {
+        compatible = "example,test-node-name";
+    };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-property-name.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-property-name.yaml
new file mode 100644
index 000000000000..08e0d1eb152a
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-property-name.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-property-name.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with incorrect property names
+
+maintainers:
+  - Rob Herring <robh@kernel.org>
+
+properties:
+  compatible:
+    const: example,test-property-name
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    foo@1000 {
+        compatible = "example,test-property-name";
+        reg = <0x1000 0x100>;
+
+        property#foo;
+        property+foo;
+        property-foo;
+        property-foo-;
+        property.foo;
+        property?foo;
+        property_foo;
+
+        vendor,property#foo;
+        vendor,property+foo;
+        vendor,property,foo;
+        vendor,property-foo;
+        vendor,property-foo-;
+        vendor,property.foo;
+        vendor,property?foo;
+        vendor,property_foo;
+    };
diff --git a/scripts/dtc/dt-style-selftest/expected/dts-node-name.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-node-name.dts.txt
new file mode 100644
index 000000000000..bd8d7e21e243
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/dts-node-name.dts.txt
@@ -0,0 +1,13 @@
+# mode=strict
+bad/dts-node-name.dts:8: [node-name] node name "foo_bar" is using discouraged style
+bad/dts-node-name.dts:12: [child-name-order] child node 'foo,bar' out of name order
+bad/dts-node-name.dts:12: [node-name] node name "foo,bar" is using discouraged style
+bad/dts-node-name.dts:16: [node-name] node name "foo.bar" is using discouraged style
+bad/dts-node-name.dts:20: [child-name-order] child node 'foo+bar' out of name order
+bad/dts-node-name.dts:20: [node-name] node name "foo+bar" is using discouraged style
+bad/dts-node-name.dts:24: [node-name] node name "foo-bar-" is using discouraged style
+bad/dts-node-name.dts:35: [node-name] node name "foo_bar" is using discouraged style
+bad/dts-node-name.dts:40: [node-name] node name "foo,bar" is using discouraged style
+bad/dts-node-name.dts:45: [node-name] node name "foo.bar" is using discouraged style
+bad/dts-node-name.dts:50: [node-name] node name "foo+bar" is using discouraged style
+bad/dts-node-name.dts:55: [node-name] node name "foo-bar-" is using discouraged style
diff --git a/scripts/dtc/dt-style-selftest/expected/dts-property-name.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-property-name.dts.txt
new file mode 100644
index 000000000000..f447743383da
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/dts-property-name.dts.txt
@@ -0,0 +1,14 @@
+# mode=strict
+bad/dts-property-name.dts:11: [property-name] property name "property#foo" is using discouraged style
+bad/dts-property-name.dts:12: [property-name] property name "property+foo" is using discouraged style
+bad/dts-property-name.dts:14: [property-name] property name "property-foo-" is using discouraged style
+bad/dts-property-name.dts:15: [property-name] property name "property.foo" is using discouraged style
+bad/dts-property-name.dts:16: [property-name] property name "property?foo" is using discouraged style
+bad/dts-property-name.dts:17: [property-name] property name "property_foo" is using discouraged style
+bad/dts-property-name.dts:19: [property-name] property name "vendor,property#foo" is using discouraged style
+bad/dts-property-name.dts:20: [property-name] property name "vendor,property+foo" is using discouraged style
+bad/dts-property-name.dts:21: [property-name] property name "vendor,property,foo" is using discouraged style
+bad/dts-property-name.dts:23: [property-name] property name "vendor,property-foo-" is using discouraged style
+bad/dts-property-name.dts:24: [property-name] property name "vendor,property.foo" is using discouraged style
+bad/dts-property-name.dts:25: [property-name] property name "vendor,property?foo" is using discouraged style
+bad/dts-property-name.dts:26: [property-name] property name "vendor,property_foo" is using discouraged style
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-node-name.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-node-name.yaml.txt
new file mode 100644
index 000000000000..d9cf749e35c2
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-node-name.yaml.txt
@@ -0,0 +1,7 @@
+# mode=strict
+bad/yaml-node-name.yaml:25: example 0 [node-name] node name "foo_bar" is using discouraged style
+bad/yaml-node-name.yaml:30: example 0 [node-name] node name "foo,bar" is using discouraged style
+bad/yaml-node-name.yaml:35: example 0 [node-name] node name "foo.bar" is using discouraged style
+bad/yaml-node-name.yaml:40: example 0 [node-name] node name "foo+bar" is using discouraged style
+bad/yaml-node-name.yaml:45: example 0 [node-name] node name "foo-bar-" is using discouraged style
+bad/yaml-node-name.yaml:52: example 0 [node-name] node name "foo_bar" is using discouraged style
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-property-name.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-property-name.yaml.txt
new file mode 100644
index 000000000000..6aa3a004532b
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-property-name.yaml.txt
@@ -0,0 +1,14 @@
+# mode=strict
+bad/yaml-property-name.yaml:30: example 0 [property-name] property name "property#foo" is using discouraged style
+bad/yaml-property-name.yaml:31: example 0 [property-name] property name "property+foo" is using discouraged style
+bad/yaml-property-name.yaml:33: example 0 [property-name] property name "property-foo-" is using discouraged style
+bad/yaml-property-name.yaml:34: example 0 [property-name] property name "property.foo" is using discouraged style
+bad/yaml-property-name.yaml:35: example 0 [property-name] property name "property?foo" is using discouraged style
+bad/yaml-property-name.yaml:36: example 0 [property-name] property name "property_foo" is using discouraged style
+bad/yaml-property-name.yaml:38: example 0 [property-name] property name "vendor,property#foo" is using discouraged style
+bad/yaml-property-name.yaml:39: example 0 [property-name] property name "vendor,property+foo" is using discouraged style
+bad/yaml-property-name.yaml:40: example 0 [property-name] property name "vendor,property,foo" is using discouraged style
+bad/yaml-property-name.yaml:42: example 0 [property-name] property name "vendor,property-foo-" is using discouraged style
+bad/yaml-property-name.yaml:43: example 0 [property-name] property name "vendor,property.foo" is using discouraged style
+bad/yaml-property-name.yaml:44: example 0 [property-name] property name "vendor,property?foo" is using discouraged style
+bad/yaml-property-name.yaml:45: example 0 [property-name] property name "vendor,property_foo" is using discouraged style

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/6] dtc: dt-check-style: Check for indentation also in continued lines of DTS
  2026-08-29 19:53 [PATCH 0/6] dtc: dt-check-style: Cleanup plus new checks Krzysztof Kozlowski
                   ` (3 preceding siblings ...)
  2026-08-29 19:53 ` [PATCH 4/6] dtc: dt-check-style: Add test for node and property name styles Krzysztof Kozlowski
@ 2026-08-29 19:53 ` Krzysztof Kozlowski
  2026-08-29 19:53 ` [PATCH 6/6] dtc: dt-check-style: Check for indentation also in continued lines of bindings example Krzysztof Kozlowski
  5 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-29 19:53 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Saravana Kannan
  Cc: devicetree, linux-kernel, sashiko-bot, Krzysztof Kozlowski

Add missing code in check_mixed_indent_chars() for DTS to apply the same
rule of too many spaces for indentation also for continued lines
(iterating over all DtsLines is not enough).

Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/all/20260709180118.6935B1F000E9@smtp.kernel.org/
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 scripts/dtc/dt-check-style                                      | 2 ++
 scripts/dtc/dt-style-selftest/expected/dts-mixed-indent.dts.txt | 1 +
 2 files changed, 3 insertions(+)

diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
index 16586d0029ff..dc5a8ee23d68 100755
--- a/scripts/dtc/dt-check-style
+++ b/scripts/dtc/dt-check-style
@@ -652,6 +652,8 @@ def check_mixed_indent_chars(ctx):
                 continue
             if re.search(r' \t', cont.indent_str):
                 yield (cont.lineno, 'mixed tabs and spaces in indent')
+            if cont.indent_str.count(' ') > 7:
+                yield (cont.lineno, 'too many space characters in indent (more than 7)')
 
 
 def check_node_close_alone(ctx):
diff --git a/scripts/dtc/dt-style-selftest/expected/dts-mixed-indent.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-mixed-indent.dts.txt
index 93146cfb51c7..1706390f5664 100644
--- a/scripts/dtc/dt-style-selftest/expected/dts-mixed-indent.dts.txt
+++ b/scripts/dtc/dt-style-selftest/expected/dts-mixed-indent.dts.txt
@@ -6,4 +6,5 @@ bad/dts-mixed-indent.dts:12: [mixed-indent-chars] mixed tabs and spaces in inden
 bad/dts-mixed-indent.dts:13: [indent-consistent] indent mismatch (expected depth 2 * '\t')
 bad/dts-mixed-indent.dts:13: [mixed-indent-chars] mixed tabs and spaces in indent
 bad/dts-mixed-indent.dts:16: [mixed-indent-chars] mixed tabs and spaces in indent
+bad/dts-mixed-indent.dts:18: [mixed-indent-chars] too many space characters in indent (more than 7)
 bad/dts-mixed-indent.dts:19: [indent-consistent] indent mismatch (expected depth 2 * '\t')

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/6] dtc: dt-check-style: Check for indentation also in continued lines of bindings example
  2026-08-29 19:53 [PATCH 0/6] dtc: dt-check-style: Cleanup plus new checks Krzysztof Kozlowski
                   ` (4 preceding siblings ...)
  2026-08-29 19:53 ` [PATCH 5/6] dtc: dt-check-style: Check for indentation also in continued lines of DTS Krzysztof Kozlowski
@ 2026-08-29 19:53 ` Krzysztof Kozlowski
  5 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-29 19:53 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Saravana Kannan
  Cc: devicetree, linux-kernel, Krzysztof Kozlowski

Add missing code in check_tab_in_yaml_example() for example in the
bindings to apply the same rule of discouraged tab character also for
continued lines (iterating over all DtsLines is not enough).

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 scripts/dtc/dt-check-style                                        | 3 +++
 scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml          | 2 ++
 scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt | 1 +
 3 files changed, 6 insertions(+)

diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
index dc5a8ee23d68..0ebbe658b893 100755
--- a/scripts/dtc/dt-check-style
+++ b/scripts/dtc/dt-check-style
@@ -959,6 +959,9 @@ def check_tab_in_yaml_example(ctx):
             continue
         if '\t' in dl.raw:
             yield (dl.lineno, 'tab character not allowed in DTS example')
+        for cont in dl.continuations:
+            if '\t' in cont.raw:
+                yield (cont.lineno, 'tab character not allowed in DTS example')
 
 
 def check_trailing_whitespace(ctx):
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml
index c25a0d1a999e..4f76188b5e23 100644
--- a/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml
@@ -26,4 +26,6 @@ examples:
     device@1000 {
     	   compatible = "example,test-mixed";
         reg = <0x1000 0x100>;
+        clocks = <1>,
+    	           <2>;
     };
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt
index bc3fc3cf00cc..442f21f90e83 100644
--- a/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt
@@ -1,2 +1,3 @@
 # mode=relaxed
 bad/yaml-mixed-indent.yaml:27: example 0 [tab-in-yaml] tab character not allowed in DTS example
+bad/yaml-mixed-indent.yaml:30: example 0 [tab-in-yaml] tab character not allowed in DTS example

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/6] dtc: dt-check-style: Add test for node and property name styles
  2026-08-29 19:53 ` [PATCH 4/6] dtc: dt-check-style: Add test for node and property name styles Krzysztof Kozlowski
@ 2026-08-30 19:59   ` Rob Herring
  2026-08-30 20:57     ` Krzysztof Kozlowski
  0 siblings, 1 reply; 10+ messages in thread
From: Rob Herring @ 2026-08-30 19:59 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Krzysztof Kozlowski, Conor Dooley, Saravana Kannan, devicetree,
	linux-kernel

On Sat, Aug 29, 2026 at 09:53:20PM +0200, Krzysztof Kozlowski wrote:
> Common style violation is usage of underscores in node or property
> names, so add rules for following recommended names.  There is plenty of
> legacy approach, so add the rules only for 'strict' mode of checking.

We already have this in dtc, do we need it here too?

Rob

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/6] dtc: dt-check-style: Add test for node and property name styles
  2026-08-30 19:59   ` Rob Herring
@ 2026-08-30 20:57     ` Krzysztof Kozlowski
  2026-08-31 17:58       ` Rob Herring
  0 siblings, 1 reply; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-30 20:57 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski
  Cc: Krzysztof Kozlowski, Conor Dooley, Saravana Kannan, devicetree,
	linux-kernel

On 30/08/2026 21:59, Rob Herring wrote:
> On Sat, Aug 29, 2026 at 09:53:20PM +0200, Krzysztof Kozlowski wrote:
>> Common style violation is usage of underscores in node or property
>> names, so add rules for following recommended names.  There is plenty of
>> legacy approach, so add the rules only for 'strict' mode of checking.
> 
> We already have this in dtc, do we need it here too?

Not sure really. DTC has it in W=2, so initially I thought no one ever
looks there, but duplicating maybe is not a good idea. I don't mind
skipping this, next patches in this set should still apply cleanly.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/6] dtc: dt-check-style: Add test for node and property name styles
  2026-08-30 20:57     ` Krzysztof Kozlowski
@ 2026-08-31 17:58       ` Rob Herring
  0 siblings, 0 replies; 10+ messages in thread
From: Rob Herring @ 2026-08-31 17:58 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Krzysztof Kozlowski, Krzysztof Kozlowski, Conor Dooley,
	Saravana Kannan, devicetree, linux-kernel

On Sun, Aug 30, 2026 at 10:57:23PM +0200, Krzysztof Kozlowski wrote:
> On 30/08/2026 21:59, Rob Herring wrote:
> > On Sat, Aug 29, 2026 at 09:53:20PM +0200, Krzysztof Kozlowski wrote:
> >> Common style violation is usage of underscores in node or property
> >> names, so add rules for following recommended names.  There is plenty of
> >> legacy approach, so add the rules only for 'strict' mode of checking.
> > 
> > We already have this in dtc, do we need it here too?
> 
> Not sure really. DTC has it in W=2, so initially I thought no one ever
> looks there, but duplicating maybe is not a good idea. I don't mind
> skipping this, next patches in this set should still apply cleanly.

Well, I guess this is a bit more exhaustive in what it allows. 

Series applied.

Rob

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-08-31 17:58 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-29 19:53 [PATCH 0/6] dtc: dt-check-style: Cleanup plus new checks Krzysztof Kozlowski
2026-08-29 19:53 ` [PATCH 1/6] dtc: dt-check-style: Sort rule directory by name Krzysztof Kozlowski
2026-08-29 19:53 ` [PATCH 2/6] dtc: dt-check-style: Sort rule functions " Krzysztof Kozlowski
2026-08-29 19:53 ` [PATCH 3/6] dtc: dt-check-style: Process property names with a question mark Krzysztof Kozlowski
2026-08-29 19:53 ` [PATCH 4/6] dtc: dt-check-style: Add test for node and property name styles Krzysztof Kozlowski
2026-08-30 19:59   ` Rob Herring
2026-08-30 20:57     ` Krzysztof Kozlowski
2026-08-31 17:58       ` Rob Herring
2026-08-29 19:53 ` [PATCH 5/6] dtc: dt-check-style: Check for indentation also in continued lines of DTS Krzysztof Kozlowski
2026-08-29 19:53 ` [PATCH 6/6] dtc: dt-check-style: Check for indentation also in continued lines of bindings example Krzysztof Kozlowski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox