From e1166994805e4e28d5b4ea89eedc400ca25e19d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Dziewo=C5=84ski?= Date: Wed, 20 Jan 2016 21:38:59 +0100 Subject: [PATCH] Rollback all, then redo all, when fixing out-of-order tags; not one-by-one Imagine the following wikitext: A B This is simple. Cite would see these as the following operations, in order: 1. Use only: 2. References block 3. Define only: A 4. Define only: B is defined twice with different content and we get an error message. Now, imagine the following wikitext: {{#tag:references| A B }} Cite would see these as the following operations, in order: 1. Use only: 2. Use and define: A 3. Use and define: B 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: reused 6. Define only: B 7. Undo: defined (Right now, it appears to Cite that was never defined!) 8. Define only: A Thus we get no errors, although we should. This patch changes the order of the rollback operations: 5. Undo: reused 6. Undo: defined 7. Define only: A 8. Define only: B Aha! is defined twice with different content! We get an error correctly. Bug: T124227 Change-Id: I61766c4104856323987cca9a5e4ff85a76b3618b --- Cite_body.php | 33 +++++++++++++++++++++------------ citeParserTests.txt | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Cite_body.php b/Cite_body.php index b40a12e77..79a8176e0 100644 --- a/Cite_body.php +++ b/Cite_body.php @@ -604,30 +604,39 @@ class Cite { if ( strval( $str ) !== '' ) { $this->mReferencesGroup = $group; - # Detect whether we were sent already rendered s - # Mostly a side effect of using #tag to call references + # Detect whether we were sent already rendered s. + # Mostly a side effect of using #tag to call references. + # The following assumes that the parsed s sent within + # the block were the most recent calls to + # . 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, and + # conditional parser functions could be created that would + # lead to malformed references here. $count = substr_count( $str, Parser::MARKER_PREFIX . "-ref-" ); + $redoStack = array(); + + # Undo effects of calling while unaware of containing for ( $i = 1; $i <= $count; $i++ ) { if ( !$this->mRefCallStack ) { break; } - # The following assumes that the parsed s sent within - # the block were the most recent calls to - # . 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, and - # conditional parser functions could be created that would - # lead to malformed references here. $call = array_pop( $this->mRefCallStack ); + $redoStack[] = $call; if ( $call !== false ) { list( $type, $ref_argv, $ref_str, $ref_key, $ref_group, $ref_index ) = $call; - - # Undo effects of calling while unaware of containing $this->rollbackRef( $type, $ref_key, $ref_group, $ref_index ); + } + } - # Rerun call now that mInReferences is set. + # Rerun 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 ); } } diff --git a/citeParserTests.txt b/citeParserTests.txt index 695378c04..189a3f1be 100644 --- a/citeParserTests.txt +++ b/citeParserTests.txt @@ -584,3 +584,21 @@ Multiple definition (mixed outside/inside) !! end + +!! test +Multiple definition (inside {{#tag:references}}) +!! input + +{{#tag:references| +abc +def +}} +!! result +

[1] +

+
    +
  1. abc Cite error: Invalid <ref> tag; name "a" defined multiple times with different content +
  2. +
+ +!! end