From 0d4dbd64b69bf201cee28307252c300411cb9ed0 Mon Sep 17 00:00:00 2001 From: David Harris Date: Thu, 1 Oct 2026 10:34:55 -0700 Subject: [PATCH] tags.rb: report normative tags in admonitions as errors Admonitions are non-normative, so a tag inside one, or an anchor on the line just above one (which tags the admonition itself), is an error. With --failure-level=WARN, as the norm-tag builds use, this fails the build. The admonition cases in test-ch2.adoc move to tests/tags/admonition.adoc, which must fail and report each bad tag, and normative-rules.md now says admonitions cannot hold normative rules. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: David Harris --- Makefile | 15 +++++- converters/tags.rb | 14 ++++++ normative-rules.md | 9 ++-- .../expected/test-ch2-norm-tags.json | 30 ++---------- tests/norm-rule/expected/test-norm-rules.html | 39 +-------------- tests/norm-rule/expected/test-norm-rules.json | 49 ------------------- tests/norm-rule/test-ch2.adoc | 49 +------------------ tests/tags/admonition.adoc | 27 ++++++++++ 8 files changed, 67 insertions(+), 165 deletions(-) create mode 100644 tests/tags/admonition.adoc diff --git a/Makefile b/Makefile index e534a31..dd533d4 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,9 @@ NORM_RULE_HTML_OUTPUT_FNAME := test-norm-rules.html # Tag extraction test files DUPLICATE_TEST_ADOC_INPUT_FNAME := duplicate.adoc DUPLICATE_NORM_TAGS_OUTPUT_FNAME := duplicate-tags.json +ADMONITION_TEST_ADOC_INPUT_FNAME := admonition.adoc +ADMONITION_NORM_TAGS_OUTPUT_FNAME := admonition-admonition-tags.json +ADMONITION_ERRORS_FNAME := admonition-errors.txt # Built output files BUILT_TEST_CH1_HTML_FNAME := $(BUILD_DIR)/$(TEST_CH1_HTML_FNAME) @@ -57,6 +60,7 @@ BUILT_TEST_CH2_HTML_FNAME := $(BUILD_DIR)/$(TEST_CH2_HTML_FNAME) BUILT_TEST_CH1_NORM_TAGS_FNAME := $(BUILD_DIR)/$(TEST_CH1_NORM_TAGS_OUTPUT_FNAME) BUILT_TEST_CH2_NORM_TAGS_FNAME := $(BUILD_DIR)/$(TEST_CH2_NORM_TAGS_OUTPUT_FNAME) BUILT_DUPLICATE_NORM_TAGS_FNAME := $(BUILD_DIR)/$(DUPLICATE_NORM_TAGS_OUTPUT_FNAME) +BUILT_ADMONITION_NORM_TAGS_FNAME := $(BUILD_DIR)/$(ADMONITION_NORM_TAGS_OUTPUT_FNAME) BUILT_NORM_RULES_JSON := $(BUILD_DIR)/$(NORM_RULE_JSON_OUTPUT_FNAME) BUILT_NORM_RULES_HTML := $(BUILD_DIR)/$(NORM_RULE_HTML_OUTPUT_FNAME) @@ -151,7 +155,7 @@ test: build-tests compare-tests test-adoc2html test-shared-utils test-text-to-ht # Build tests .PHONY: build-tests build-test-tags build-test-norm-rules-json build-test-norm-rules-html build-tests: build-test-tags build-test-norm-rules-json build-test-norm-rules-html -build-test-tags: $(BUILT_TEST_NORM_TAGS_FNAMES) $(BUILT_DUPLICATE_NORM_TAGS_FNAME) +build-test-tags: $(BUILT_TEST_NORM_TAGS_FNAMES) $(BUILT_DUPLICATE_NORM_TAGS_FNAME) $(BUILT_ADMONITION_NORM_TAGS_FNAME) build-test-norm-rules-json: $(BUILT_NORM_RULES_JSON) build-test-norm-rules-html: $(BUILT_NORM_RULES_HTML) @@ -258,6 +262,15 @@ $(BUILT_DUPLICATE_NORM_TAGS_FNAME): $(TAGS_TESTS_DIR)/$(DUPLICATE_TEST_ADOC_INPU $(DOCKER_CMD) $(DOCKER_QUOTE) $(ASCIIDOCTOR_TAGS) $(OPTIONS) -a tags-match-prefix='duplicate:' -a tags-output-suffix='-duplicate-tags.json' $< || touch $(BUILT_DUPLICATE_NORM_TAGS_FNAME) $(DOCKER_QUOTE) $(WORKDIR_TEARDOWN) +# Build tags with admonition adoc input. +# Tags in or on an admonition are errors, so Asciidoctor must fail and report each "bad" tag. +$(BUILT_ADMONITION_NORM_TAGS_FNAME): $(TAGS_TESTS_DIR)/$(ADMONITION_TEST_ADOC_INPUT_FNAME) $(CONVERTERS_DIR)/$(TAGS_BACKEND) + $(WORKDIR_SETUP) + $(DOCKER_CMD) $(DOCKER_QUOTE) ! $(ASCIIDOCTOR_TAGS) $(OPTIONS) -a tags-match-prefix='admonition:' -a tags-output-suffix='-admonition-tags.json' $< 2> $(ADMONITION_ERRORS_FNAME) $(DOCKER_QUOTE) + test "$$(grep -c "^asciidoctor: ERROR: Tag 'admonition:bad-.*' is in an admonition" $@.workdir/$(ADMONITION_ERRORS_FNAME))" -eq 5 + ! grep -q "admonition:ok-" $@.workdir/$(ADMONITION_ERRORS_FNAME) + $(WORKDIR_TEARDOWN) + # Build normative rules with JSON output format $(BUILT_NORM_RULES_JSON): $(BUILT_TEST_NORM_TAGS_FNAMES) $(CREATE_NORM_RULE_TOOL) $(WORKDIR_SETUP) diff --git a/converters/tags.rb b/converters/tags.rb index 664c1cb..a5d6de3 100644 --- a/converters/tags.rb +++ b/converters/tags.rb @@ -90,6 +90,7 @@ def convert(node, transform = node.node_name, opts = nil) if node.id.start_with?(@prefix) node.document.logger.error "Duplicate tag name '#{node.id}'" unless @tag_map[node.id].nil? node.document.logger.error "Tag '#{node.id}' content should be a String but it is #{content.class}" unless content.is_a?(String) + node.document.logger.error "Tag '#{node.id}' is in an admonition, which is non-normative" if in_admonition?(node) @tag_map[node.id] = content.strip() @section_stack.last["tags"] << node.id @@ -108,6 +109,19 @@ def convert(node, transform = node.node_name, opts = nil) private + # Return true if the node is an admonition (e.g. a tag on the line above + # `[NOTE]`) or is inside one. Inline nodes reach their block via `parent`. + # + # node: AbstractNode + # returns: Boolean + def in_admonition?(node) + until node.nil? || node.context == :document + return true if node.context == :admonition + node = node.parent + end + false + end + # Return the text content of a node. Adapted from `text-converter.rb` # in the docs: https://docs.asciidoctor.org/asciidoctor/latest/convert/custom/ # diff --git a/normative-rules.md b/normative-rules.md index 4db3d15..2464eea 100644 --- a/normative-rules.md +++ b/normative-rules.md @@ -125,7 +125,7 @@ If you'd like to see detailed AsciiDoc examples of tagging cases, see https://gi This also includes text followed by a list (ordered, unordered, description) since there has to be a blank line between the text the list. > * Must have text next to the 2nd hash symbol (i.e., can't have newline after `[# * Can't put inside admonitions such as [NOTE] (see #4 below for solution). + > * Can't put inside admonitions such as [NOTE] (see #4 below). > * Can't have `.` in anchor-name (replace with hyphen) 3. Tagging description lists @@ -146,7 +146,6 @@ If you'd like to see detailed AsciiDoc examples of tagging cases, see https://gi > `Bananas::`
> `Generally yellow in color` -4. Tagging admonitions (e.g. `[NOTE]`): -* Can tag entire admonition by putting ``[[anchor-name]]`` before `[NOTE]` -* Can also tag individual paragraphs in admonition using `[[All Normative Rules
-

94 Normative Rules

+

87 Normative Rules

- + @@ -158,41 +158,6 @@

94 Normative Rules

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/norm-rule/expected/test-norm-rules.json b/tests/norm-rule/expected/test-norm-rules.json index 24d7d0b..702292e 100644 --- a/tests/norm-rule/expected/test-norm-rules.json +++ b/tests/norm-rule/expected/test-norm-rules.json @@ -1,54 +1,5 @@ { "normative_rules": [ - { - "name": "admonition:single-paragraph-note", - "chapter_name": "Chapter 2", - "text": "Single paragraph note\nthat spans lines.", - "tag_filename": "/build/test-ch2-norm-tags.json", - "stds_doc_url": "test-ch2.html" - }, - { - "name": "admonition:no-anchors-in-notes:entire-note", - "chapter_name": "Chapter 2", - "text": "Paragraph A\n\nParagraph B\n\nParagraph C", - "tag_filename": "/build/test-ch2-norm-tags.json", - "stds_doc_url": "test-ch2.html" - }, - { - "name": "admonition:anchors-in-notes:note1", - "chapter_name": "Chapter 2", - "text": "Paragraph 0", - "tag_filename": "/build/test-ch2-norm-tags.json", - "stds_doc_url": "test-ch2.html" - }, - { - "name": "admonition:anchors-in-notes:note3", - "chapter_name": "Chapter 2", - "text": "Paragraph 2", - "tag_filename": "/build/test-ch2-norm-tags.json", - "stds_doc_url": "test-ch2.html" - }, - { - "name": "admonition:anchors-in-notes:entire-note", - "chapter_name": "Chapter 2", - "text": "Paragraph 0\n\nParagraph 1\n\nParagraph 2", - "tag_filename": "/build/test-ch2-norm-tags.json", - "stds_doc_url": "test-ch2.html" - }, - { - "name": "admonition:only-anchors-in-notes:note1", - "chapter_name": "Chapter 2", - "text": "Paragraph X", - "tag_filename": "/build/test-ch2-norm-tags.json", - "stds_doc_url": "test-ch2.html" - }, - { - "name": "admonition:only-anchors-in-notes:note3", - "chapter_name": "Chapter 2", - "text": "Paragraph Z", - "tag_filename": "/build/test-ch2-norm-tags.json", - "stds_doc_url": "test-ch2.html" - }, { "name": "ROSE_COLORS", "chapter_name": "Chapter 2", diff --git a/tests/norm-rule/test-ch2.adoc b/tests/norm-rule/test-ch2.adoc index 831fdda..cc5028a 100644 --- a/tests/norm-rule/test-ch2.adoc +++ b/tests/norm-rule/test-ch2.adoc @@ -3,52 +3,7 @@ == Chapter 2 -=== Chapter 2.1 - Tagging Admonitions - -// PASSES -NOTE: [#norm:admonition:single-paragraph-note]#Single paragraph note -that spans lines.# - -// PASSES - Tag contains entire list -[[norm:admonition:no-anchors-in-notes:entire-note]] -[NOTE] -==== -Paragraph A - -Paragraph B - -Paragraph C -==== - -// PASSES - Tag contains entire list -[[norm:admonition:anchors-in-notes:entire-note]] -[NOTE] -==== -// PASSES - Tag contains paragraph -[[norm:admonition:anchors-in-notes:note1]] -Paragraph 0 - -Paragraph 1 - -// PASSES - Tag contains paragraph -[[norm:admonition:anchors-in-notes:note3]] -Paragraph 2 -==== - -[NOTE] -==== -// PASSES - Tag contains paragraph -[[norm:admonition:only-anchors-in-notes:note1]] -Paragraph X - -Paragraph Y - -// PASSES - Tag contains paragraph -[[norm:admonition:only-anchors-in-notes:note3]] -Paragraph Z -==== - -=== Chapter 2.2 - Implementation-Defined Behaviors +=== Chapter 2.1 - Implementation-Defined Behaviors [#norm:ROSE_COLORS]#You can use red or yellow roses. The tags backend removes some adoc formating so I don't see *bold* text but I can see ^superscript^ as long as I use the inline anchor and not the paragraph anchor.# @@ -80,7 +35,7 @@ Here's a normative rule for mock CSR field "Y". [[norm:mock-ext-dep-A-on-B]] Here's a normative rule for mock extension dependency of extension A on extension B. -=== Chapter 2.3 - CSR Field Types +=== Chapter 2.2 - CSR Field Types [#norm:foo_abc_warl_enum]#An implementation may support any or all of the following values for the 4-bit `foo.ABC` field: When `foo.GHI` is 0, legal values are 0, 4, 15. When `foo.GHI` is non-zero, legal values are 1, 2, 3.# diff --git a/tests/tags/admonition.adoc b/tests/tags/admonition.adoc new file mode 100644 index 0000000..721dc32 --- /dev/null +++ b/tests/tags/admonition.adoc @@ -0,0 +1,27 @@ +// Used to test that the tags backend reports an error for every tag in or on an admonition, +// since admonitions are non-normative. Each tag below must be reported. + +== Chapter 1 + +[#admonition:ok-inline]#Normative text outside any admonition.# + +NOTE: [#admonition:bad-paragraph-note]#Inline tag in a paragraph admonition.# + +[[admonition:bad-entire-note]] +[NOTE] +==== +An anchor on the line above an admonition tags the admonition itself. +==== + +[WARNING] +==== +[[admonition:bad-paragraph-in-block]] +A tagged paragraph in a delimited admonition of another type. + +* [#admonition:bad-list-item-in-block]#A tagged list item in an admonition.# +==== + +TIP: An inline anchor [[admonition:bad-inline-anchor]] in a paragraph admonition. + +[[admonition:ok-paragraph]] +A tagged paragraph outside any admonition.
Chapter Chapter 2: 21 Normative RulesChapter Chapter 2: 14 Normative Rules
NameTextLocation
admonition:single-paragraph-noteSingle paragraph note
that spans lines.
norm:admonition:single-paragraph-note
admonition:no-anchors-in-notes:entire-noteParagraph A

Paragraph B

Paragraph C
norm:admonition:no-anchors-in-notes:entire-note
admonition:anchors-in-notes:note1Paragraph 0norm:admonition:anchors-in-notes:note1
admonition:anchors-in-notes:note3Paragraph 2norm:admonition:anchors-in-notes:note3
admonition:anchors-in-notes:entire-noteParagraph 0

Paragraph 1

Paragraph 2
norm:admonition:anchors-in-notes:entire-note
admonition:only-anchors-in-notes:note1Paragraph Xnorm:admonition:only-anchors-in-notes:note1
admonition:only-anchors-in-notes:note3Paragraph Znorm:admonition:only-anchors-in-notes:note3
ROSE_COLORS You can use red or yellow roses. The tags backend removes some adoc formating so I don't see bold text but I can see superscript as long as I use the inline anchor and not the paragraph anchor.