Flag single-line `//` comments that sit inside a QML object body (`Item { }`, `Rectangle { }`, etc.) but NOT inside a JavaScript body (`function () { }`, `onClicked: { }`, `() => { }`). QML object bodies hold declarative property bindings — labelling them with inline `//` notes is t
(
lines: list[str], path: Path, fence_mask: list[bool]
)
| 1420 | |
| 1421 | |
| 1422 | def find_qml_inline_comment_violations( |
| 1423 | lines: list[str], path: Path, fence_mask: list[bool] |
| 1424 | ) -> list[Violation]: |
| 1425 | """Flag single-line `//` comments that sit inside a QML object body |
| 1426 | (`Item { }`, `Rectangle { }`, etc.) but NOT inside a JavaScript body |
| 1427 | (`function () { }`, `onClicked: { }`, `() => { }`). |
| 1428 | |
| 1429 | QML object bodies hold declarative property bindings — labelling them |
| 1430 | with inline `//` notes is the AI-narration smell CLAUDE.md bans. JS |
| 1431 | function bodies are imperative code and follow the same one-line |
| 1432 | section-header rule as `.cpp` files; flagging those would double-tax |
| 1433 | the multi-line-comment rule, so they're left alone here. |
| 1434 | |
| 1435 | Banner shapes (`//`, `//---`, `//===`, sandwich runs) are NOT flagged — |
| 1436 | they're handled by the multi-line / banner rules. Tooling pragmas |
| 1437 | (`// clang-format off/on`, `// NOLINT`, `// code-verify off/on`) are |
| 1438 | directives, not prose, and skipped.""" |
| 1439 | if path.suffix != ".qml": |
| 1440 | return [] |
| 1441 | |
| 1442 | violations: list[Violation] = [] |
| 1443 | n = len(lines) |
| 1444 | |
| 1445 | # Stack of body kinds — "qml" for an object body, "js" for a JS body. |
| 1446 | # We push when a line opens a body and pop on the matching close. The |
| 1447 | # top of the stack tells us where the current line lives. |
| 1448 | body_stack: list[str] = [] |
| 1449 | |
| 1450 | for i, line in enumerate(lines): |
| 1451 | if fence_mask[i]: |
| 1452 | continue |
| 1453 | |
| 1454 | stripped = line.strip() |
| 1455 | |
| 1456 | # Skip comment-only lines for stack maintenance — they don't open |
| 1457 | # or close bodies. |
| 1458 | if ( |
| 1459 | stripped.startswith("//") |
| 1460 | or stripped.startswith("/*") |
| 1461 | or stripped.startswith("*") |
| 1462 | ): |
| 1463 | payload = _comment_payload(line) |
| 1464 | if payload is not None and not _is_tooling_pragma(line): |
| 1465 | p = payload.strip() |
| 1466 | # Skip blank `//` lines and `//---`/`//===` decorator rules — |
| 1467 | # those are banner pieces handled by the multi-line rule. |
| 1468 | is_decorator = (not p) or set(p) <= {"-", "=", "*"} |
| 1469 | if not is_decorator and body_stack and body_stack[-1] == "qml": |
| 1470 | # Lookahead: only flag a SINGLE-line `//` block. If the |
| 1471 | # next or prev non-blank line is also a `//` comment, |
| 1472 | # the multi-line-comment rule covers it instead. |
| 1473 | nxt = i + 1 |
| 1474 | while nxt < n and lines[nxt].strip() == "": |
| 1475 | nxt += 1 |
| 1476 | next_is_comment = ( |
| 1477 | nxt < n |
| 1478 | and lines[nxt].lstrip().startswith("//") |
| 1479 | and not _is_tooling_pragma(lines[nxt]) |
no test coverage detected