stricter checks for RST headlines (#17089)

This commit is contained in:
Andrey Makarov 2021-02-20 20:01:45 +03:00 • committed by GitHub
commit d1fec552d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 72 additions and 17 deletions

View file

@ -142,6 +142,7 @@ template declareClosures =
of mwUnknownSubstitution: k = warnUnknownSubstitutionX of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported of mwUnsupportedLanguage: k = warnLanguageXNotSupported
of mwUnsupportedField: k = warnFieldXNotSupported of mwUnsupportedField: k = warnFieldXNotSupported
of mwRstStyle: k = warnRstStyle
{.gcsafe.}: {.gcsafe.}:
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg) globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)

View file

@ -44,8 +44,10 @@ type
warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated", warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX", warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX",
warnLanguageXNotSupported = "LanguageXNotSupported", warnFieldXNotSupported = "FieldXNotSupported", warnLanguageXNotSupported = "LanguageXNotSupported",
warnCommentXIgnored = "CommentXIgnored", warnTypelessParam = "TypelessParam", warnFieldXNotSupported = "FieldXNotSupported",
warnRstStyle = "warnRstStyle", warnCommentXIgnored = "CommentXIgnored",
warnTypelessParam = "TypelessParam",
warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap", warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap",
warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport", warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport",
warnInheritFromException = "InheritFromException", warnEachIdentIsTuple = "EachIdentIsTuple", warnInheritFromException = "InheritFromException", warnEachIdentIsTuple = "EachIdentIsTuple",
@ -83,7 +85,7 @@ const
errGridTableNotImplemented: "grid table is not implemented", errGridTableNotImplemented: "grid table is not implemented",
errMarkdownIllformedTable: "illformed delimiter row of a markdown table", errMarkdownIllformedTable: "illformed delimiter row of a markdown table",
errGeneralParseError: "general parse error", errGeneralParseError: "general parse error",
errNewSectionExpected: "new section expected", errNewSectionExpected: "new section expected $1",
errInvalidDirectiveX: "invalid directive: '$1'", errInvalidDirectiveX: "invalid directive: '$1'",
errFootnoteMismatch: "number of footnotes and their references don't match: $1", errFootnoteMismatch: "number of footnotes and their references don't match: $1",
errProveInit: "Cannot prove that '$1' is initialized.", # deadcode errProveInit: "Cannot prove that '$1' is initialized.", # deadcode
@ -101,6 +103,7 @@ const
warnUnknownSubstitutionX: "unknown substitution '$1'", warnUnknownSubstitutionX: "unknown substitution '$1'",
warnLanguageXNotSupported: "language '$1' not supported", warnLanguageXNotSupported: "language '$1' not supported",
warnFieldXNotSupported: "field '$1' not supported", warnFieldXNotSupported: "field '$1' not supported",
warnRstStyle: "RST style: $1",
warnCommentXIgnored: "comment '$1' ignored", warnCommentXIgnored: "comment '$1' ignored",
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",

View file

@ -261,6 +261,10 @@ proc mainCommand*(graph: ModuleGraph) =
if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions: if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
commandBuildIndex(conf, $conf.outDir) commandBuildIndex(conf, $conf.outDir)
of cmdRst2html: of cmdRst2html:
# XXX: why are warnings disabled by default for rst2html and rst2tex?
for warn in [warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnRstStyle]:
conf.setNoteDefaults(warn, true)
conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218 conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218
when defined(leanCompiler): when defined(leanCompiler):
quit "compiler wasn't built with documentation generator" quit "compiler wasn't built with documentation generator"
@ -268,6 +272,10 @@ proc mainCommand*(graph: ModuleGraph) =
loadConfigs(DocConfig, cache, conf, graph.idgen) loadConfigs(DocConfig, cache, conf, graph.idgen)
commandRst2Html(cache, conf) commandRst2Html(cache, conf)
of cmdRst2tex: of cmdRst2tex:
for warn in [warnRedefinitionOfLabel, warnUnknownSubstitutionX,
warnLanguageXNotSupported,
warnFieldXNotSupported, warnRstStyle]:
conf.setNoteDefaults(warn, true)
when defined(leanCompiler): when defined(leanCompiler):
quit "compiler wasn't built with documentation generator" quit "compiler wasn't built with documentation generator"
else: else:

View file

@ -77,7 +77,7 @@ the garbage collector with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSwee
Soft real-time support Soft real-time support
--------------------- ----------------------
To enable real-time support, the symbol `useRealtimeGC`:idx: needs to be To enable real-time support, the symbol `useRealtimeGC`:idx: needs to be
defined via ``--define:useRealtimeGC`` (you can put this into your config defined via ``--define:useRealtimeGC`` (you can put this into your config

View file

@ -183,7 +183,7 @@ Usually we just track the result of an expression: probably this should apply fo
Also related to tracking initialization of expressions/fields. Also related to tracking initialization of expressions/fields.
unstructured control flow rules unstructured control flow rules
------------------------- -------------------------------
Unstructured control flow keywords as ``return``, ``break``, ``continue``, ``raise`` mean that we jump from a branch out. Unstructured control flow keywords as ``return``, ``break``, ``continue``, ``raise`` mean that we jump from a branch out.
This means that if there is code after the finishing of the branch, it would be ran if one hasn't hit the direct parent branch of those: so it is similar to an ``else``. In those cases we should use the reverse nilabilities for the local to the condition expressions. E.g. This means that if there is code after the finishing of the branch, it would be ran if one hasn't hit the direct parent branch of those: so it is similar to an ``else``. In those cases we should use the reverse nilabilities for the local to the condition expressions. E.g.

View file

@ -154,7 +154,7 @@ but does nothing else. Here is an example of such a tree representation:
Custom Semantic Checking Custom Semantic Checking
----------------------- ------------------------
The first thing that a macro should do with its arguments is to check The first thing that a macro should do with its arguments is to check
if the argument is in the correct form. Not every type of wrong input if the argument is in the correct form. Not every type of wrong input

View file

@ -165,14 +165,15 @@ type
meExpected = "'$1' expected", meExpected = "'$1' expected",
meGridTableNotImplemented = "grid table is not implemented", meGridTableNotImplemented = "grid table is not implemented",
meMarkdownIllformedTable = "illformed delimiter row of a Markdown table", meMarkdownIllformedTable = "illformed delimiter row of a Markdown table",
meNewSectionExpected = "new section expected", meNewSectionExpected = "new section expected $1",
meGeneralParseError = "general parse error", meGeneralParseError = "general parse error",
meInvalidDirective = "invalid directive: '$1'", meInvalidDirective = "invalid directive: '$1'",
meFootnoteMismatch = "mismatch in number of footnotes and their refs: $1", meFootnoteMismatch = "mismatch in number of footnotes and their refs: $1",
mwRedefinitionOfLabel = "redefinition of label '$1'", mwRedefinitionOfLabel = "redefinition of label '$1'",
mwUnknownSubstitution = "unknown substitution '$1'", mwUnknownSubstitution = "unknown substitution '$1'",
mwUnsupportedLanguage = "language '$1' not supported", mwUnsupportedLanguage = "language '$1' not supported",
mwUnsupportedField = "field '$1' not supported" mwUnsupportedField = "field '$1' not supported",
mwRstStyle = "RST style: $1"
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind, MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
arg: string) {.closure, gcsafe.} ## what to do in case of an error arg: string) {.closure, gcsafe.} ## what to do in case of an error
@ -1420,16 +1421,42 @@ proc tokenAfterNewline(p: RstParser): int =
else: inc result else: inc result
proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool = proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool =
## check that underline/overline length is enough for the heading.
## No support for Unicode.
if p.tok[adornmentIdx].symbol in ["::", "..", "|"]:
return false
var headlineLen = 0 var headlineLen = 0
if p.idx < adornmentIdx: # underline var failure = ""
if p.idx < adornmentIdx: # check for underline
if p.idx > 0:
headlineLen = currentTok(p).col - p.tok[adornmentIdx].col
if headlineLen > 0:
rstMessage(p, mwRstStyle, "indentation of heading text allowed" &
" only for overline titles")
for i in p.idx ..< adornmentIdx-1: # adornmentIdx-1 is a linebreak for i in p.idx ..< adornmentIdx-1: # adornmentIdx-1 is a linebreak
headlineLen += p.tok[i].symbol.len headlineLen += p.tok[i].symbol.len
else: # overline result = p.tok[adornmentIdx].symbol.len >= headlineLen and headlineLen != 0
if not result:
failure = "(underline '" & p.tok[adornmentIdx].symbol & "' is too short)"
else: # p.idx == adornmentIdx, at overline. Check overline and underline
var i = p.idx + 2 var i = p.idx + 2
headlineLen = p.tok[i].col - p.tok[adornmentIdx].col
while p.tok[i].kind notin {tkEof, tkIndent}: while p.tok[i].kind notin {tkEof, tkIndent}:
headlineLen += p.tok[i].symbol.len headlineLen += p.tok[i].symbol.len
inc i inc i
return p.tok[adornmentIdx].symbol.len >= headlineLen result = p.tok[adornmentIdx].symbol.len >= headlineLen and
headlineLen != 0
if result:
result = result and p.tok[i].kind == tkIndent and
p.tok[i+1].kind == tkAdornment and
p.tok[i+1].symbol == p.tok[adornmentIdx].symbol
if not result:
failure = "(underline '" & p.tok[i+1].symbol & "' does not match " &
"overline '" & p.tok[adornmentIdx].symbol & "')"
else:
failure = "(overline '" & p.tok[adornmentIdx].symbol & "' is too short)"
if not result:
rstMessage(p, meNewSectionExpected, failure)
proc isLineBlock(p: RstParser): bool = proc isLineBlock(p: RstParser): bool =
var j = tokenAfterNewline(p) var j = tokenAfterNewline(p)
@ -1941,7 +1968,7 @@ proc parseSection(p: var RstParser, result: PRstNode) =
of rnLineBlock: a = parseLineBlock(p) of rnLineBlock: a = parseLineBlock(p)
of rnDirective: a = parseDotDot(p) of rnDirective: a = parseDotDot(p)
of rnEnumList: a = parseEnumList(p) of rnEnumList: a = parseEnumList(p)
of rnLeaf: rstMessage(p, meNewSectionExpected) of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)")
of rnParagraph: discard of rnParagraph: discard
of rnDefList: a = parseDefinitionList(p) of rnDefList: a = parseDefinitionList(p)
of rnFieldList: of rnFieldList:

View file

@ -101,7 +101,7 @@
## Another message ## Another message
## ##
## Passing Channels Safely ## Passing Channels Safely
## ---------------------- ## -----------------------
## Note that when passing objects to procedures on another thread by pointer ## Note that when passing objects to procedures on another thread by pointer
## (for example through a thread's argument), objects created using the default ## (for example through a thread's argument), objects created using the default
## allocator will use thread-local, GC-managed memory. Thus it is generally ## allocator will use thread-local, GC-managed memory. Thus it is generally

View file

@ -261,8 +261,8 @@ Wrong chapter
------------ ------------
""" """
expect(EParseError):
let output4 = rstToHtml(input4, {roSupportMarkdown}, defaultConfig()) let output4 = rstToHtml(input4, {roSupportMarkdown}, defaultConfig())
doAssert "Wrong chapter" in output4 and "<h1" notin output4
let input5 = """ let input5 = """
Check that punctuation after adornment and indent are not detected as adornment. Check that punctuation after adornment and indent are not detected as adornment.
@ -281,6 +281,22 @@ Some chapter
let output6 = rstToHtml(input6, {roSupportMarkdown}, defaultConfig()) let output6 = rstToHtml(input6, {roSupportMarkdown}, defaultConfig())
doAssert "<h1 id=\"some-chapter\">Some chapter</h1>" in output6 doAssert "<h1 id=\"some-chapter\">Some chapter</h1>" in output6
# check that overline and underline match
let input7 = dedent """
------------
Some chapter
-----------
"""
expect(EParseError):
let output7 = rstToHtml(input7, {roSupportMarkdown}, defaultConfig())
let input8 = dedent """
-----------
Overflow
-----------
"""
expect(EParseError):
let output8 = rstToHtml(input8, {roSupportMarkdown}, defaultConfig())
test "RST links": test "RST links":
let input1 = """ let input1 = """
@ -308,8 +324,8 @@ This is too short to be a transition:
context2 context2
""" """
expect(EParseError):
let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig())
doAssert "<hr" notin output2
test "RST literal block": test "RST literal block":
let input1 = """ let input1 = """