mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Cite
synced 2024-12-18 01:30:32 +00:00
Rollback all, then redo all, when fixing out-of-order tags; not one-by-one
Imagine the following wikitext: <ref name=r/> <references> <ref name=r>A</ref> <ref name=r>B</ref> </references> This is simple. Cite would see these as the following operations, in order: 1. Use only: <ref name=r/> 2. References block 3. Define only: <ref name=r>A</ref> 4. Define only: <ref name=r>B</ref> <ref name=r> is defined twice with different content and we get an error message. Now, imagine the following wikitext: <ref name=r/> {{#tag:references| <ref name=r>A</ref> <ref name=r>B</ref> }} Cite would see these as the following operations, in order: 1. Use only: <ref name=r/> 2. Use and define: <ref name=r>A</ref> 3. Use and define: <ref name=r>B</ref> 4. References block When the 'references' block appears, Cite notices that the tag has parsed content, and deduces that it was called with #tag. We need to undo the last operations to update internal bookkeeping, as the last two 'ref' tags do not actually represent ref usages, as we assumed, but only definitions. 5. Undo: <ref name=r> reused 6. Define only: <ref name=r>B</ref> 7. Undo: <ref name=r> defined (Right now, it appears to Cite that <ref name=r> was never defined!) 8. Define only: <ref name=r>A</ref> Thus we get no errors, although we should. This patch changes the order of the rollback operations: 5. Undo: <ref name=r> reused 6. Undo: <ref name=r> defined 7. Define only: <ref name=r>A</ref> 8. Define only: <ref name=r>B</ref> Aha! <ref name=r> is defined twice with different content! We get an error correctly. Bug: T124227 Change-Id: I61766c4104856323987cca9a5e4ff85a76b3618b
This commit is contained in:
parent
f42e769174
commit
e116699480
|
@ -604,30 +604,39 @@ class Cite {
|
||||||
if ( strval( $str ) !== '' ) {
|
if ( strval( $str ) !== '' ) {
|
||||||
$this->mReferencesGroup = $group;
|
$this->mReferencesGroup = $group;
|
||||||
|
|
||||||
# Detect whether we were sent already rendered <ref>s
|
# Detect whether we were sent already rendered <ref>s.
|
||||||
# Mostly a side effect of using #tag to call references
|
# Mostly a side effect of using #tag to call references.
|
||||||
|
# The following assumes that the parsed <ref>s sent within
|
||||||
|
# the <references> block were the most recent calls to
|
||||||
|
# <ref>. This assumption is true for all known use cases,
|
||||||
|
# but not strictly enforced by the parser. It is possible
|
||||||
|
# that some unusual combination of #tag, <references> and
|
||||||
|
# conditional parser functions could be created that would
|
||||||
|
# lead to malformed references here.
|
||||||
$count = substr_count( $str, Parser::MARKER_PREFIX . "-ref-" );
|
$count = substr_count( $str, Parser::MARKER_PREFIX . "-ref-" );
|
||||||
|
$redoStack = array();
|
||||||
|
|
||||||
|
# Undo effects of calling <ref> while unaware of containing <references>
|
||||||
for ( $i = 1; $i <= $count; $i++ ) {
|
for ( $i = 1; $i <= $count; $i++ ) {
|
||||||
if ( !$this->mRefCallStack ) {
|
if ( !$this->mRefCallStack ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
# The following assumes that the parsed <ref>s sent within
|
|
||||||
# the <references> block were the most recent calls to
|
|
||||||
# <ref>. This assumption is true for all known use cases,
|
|
||||||
# but not strictly enforced by the parser. It is possible
|
|
||||||
# that some unusual combination of #tag, <references> and
|
|
||||||
# conditional parser functions could be created that would
|
|
||||||
# lead to malformed references here.
|
|
||||||
$call = array_pop( $this->mRefCallStack );
|
$call = array_pop( $this->mRefCallStack );
|
||||||
|
$redoStack[] = $call;
|
||||||
if ( $call !== false ) {
|
if ( $call !== false ) {
|
||||||
list( $type, $ref_argv, $ref_str,
|
list( $type, $ref_argv, $ref_str,
|
||||||
$ref_key, $ref_group, $ref_index ) = $call;
|
$ref_key, $ref_group, $ref_index ) = $call;
|
||||||
|
|
||||||
# Undo effects of calling <ref> while unaware of containing <references>
|
|
||||||
$this->rollbackRef( $type, $ref_key, $ref_group, $ref_index );
|
$this->rollbackRef( $type, $ref_key, $ref_group, $ref_index );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Rerun <ref> call now that mInReferences is set.
|
# Rerun <ref> call now that mInReferences is set.
|
||||||
|
for ( $i = count( $redoStack ) - 1; $i >= 0; $i-- ) {
|
||||||
|
$call = $redoStack[$i];
|
||||||
|
if ( $call !== false ) {
|
||||||
|
list( $type, $ref_argv, $ref_str,
|
||||||
|
$ref_key, $ref_group, $ref_index ) = $call;
|
||||||
$this->guardedRef( $ref_str, $ref_argv, $parser );
|
$this->guardedRef( $ref_str, $ref_argv, $parser );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -584,3 +584,21 @@ Multiple definition (mixed outside/inside)
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
!! end
|
!! end
|
||||||
|
|
||||||
|
!! test
|
||||||
|
Multiple definition (inside {{#tag:references}})
|
||||||
|
!! input
|
||||||
|
<ref name=a />
|
||||||
|
{{#tag:references|
|
||||||
|
<ref name=a>abc</ref>
|
||||||
|
<ref name=a>def</ref>
|
||||||
|
}}
|
||||||
|
!! result
|
||||||
|
<p><sup id="cite_ref-a_1-0" class="reference"><a href="#cite_note-a-1">[1]</a></sup>
|
||||||
|
</p>
|
||||||
|
<ol class="references">
|
||||||
|
<li id="cite_note-a-1"><span class="mw-cite-backlink"><a href="#cite_ref-a_1-0">↑</a></span> <span class="reference-text">abc <strong class="error mw-ext-cite-error">Cite error: Invalid <code><ref></code> tag; name "a" defined multiple times with different content</strong></span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
!! end
|
||||||
|
|
Loading…
Reference in a new issue