diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java index ac963485b3..bdbe158bcb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java @@ -137,18 +137,37 @@ public final class AssertionUtil { * @throws IndexOutOfBoundsException If value < 0 || value >= maxValue */ public static int requireInNonNegativeRange(String name, int value, int maxValue) { - if (value < 0 || value >= maxValue) { - throw mustBe(name, value, "in range [0," + maxValue + "[", IndexOutOfBoundsException::new); - } - return value; + return requireInExclusiveRange(name, value, 0, maxValue); } /** * @throws IndexOutOfBoundsException If value < 1 || value >= maxValue */ public static int requireInPositiveRange(String name, int value, int maxValue) { - if (value < 0 || value >= maxValue) { - throw mustBe(name, value, "in range [1," + maxValue + "[", IndexOutOfBoundsException::new); + return requireInExclusiveRange(name, value, 1, maxValue); + } + + // the difference between those two is the message + + /** + * @throws IndexOutOfBoundsException If {@code value < minValue || value > maxValue} + */ + public static int requireInInclusiveRange(String name, int value, int minValue, int maxValue) { + return requireInRange(name, value, minValue, maxValue, true); + } + + /** + * @throws IndexOutOfBoundsException If {@code value < minValue || value > maxValue} + */ + public static int requireInExclusiveRange(String name, int value, int minValue, int maxValue) { + return requireInRange(name, value, minValue, maxValue, false); + } + + public static int requireInRange(String name, int value, int minValue, int maxValue, boolean inclusive) { + if (value < 0 || inclusive && value > maxValue || !inclusive && value >= maxValue) { + String message = "in range [" + minValue + "," + maxValue; + message += inclusive ? "]" : "["; + throw mustBe(name, value, message, IndexOutOfBoundsException::new); } return value; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/document/RootTextDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/document/RootTextDocument.java index 52bcba9838..2fa79daa20 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/document/RootTextDocument.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/document/RootTextDocument.java @@ -67,6 +67,8 @@ final class RootTextDocument extends BaseCloseable implements TextDocument { checkInRange(region); SourceCodePositioner positioner = content.getPositioner(); + // We use longs to return both numbers at the same time + // This limits us to 2 billion lines or columns, which is FINE long bpos = positioner.lineColFromOffset(region.getStartOffset(), true); long epos = region.isEmpty() ? bpos : positioner.lineColFromOffset(region.getEndOffset(), false); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/document/SourceCodePositioner.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/document/SourceCodePositioner.java index b3e1966208..3c146a688f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/document/SourceCodePositioner.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/document/SourceCodePositioner.java @@ -38,7 +38,7 @@ final class SourceCodePositioner { } long lineColFromOffset(int offset, boolean inclusive) { - AssertionUtil.requireInNonNegativeRange("offset", offset, sourceCodeLength); + AssertionUtil.requireInInclusiveRange("offset", offset, 0, sourceCodeLength); int line = searchLineOffset(offset); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/document/TextDocumentTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/document/TextDocumentTest.java index fab67f41e4..3fb840819e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/util/document/TextDocumentTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/document/TextDocumentTest.java @@ -88,6 +88,21 @@ public class TextDocumentTest { assertEquals(1 + "bonjour\n".length(), withLines.getEndColumn()); } + @Test + public void testRegionAtEndOfFile() { + TextDocument doc = TextDocument.readOnlyString("flemme", dummyVersion); + + TextRegion region = TextRegion.fromOffsetLength(0, doc.getLength()); + assertEquals(doc.getText(), doc.sliceText(region)); + + FileLocation withLines = doc.toLocation(region); + + assertEquals(1, withLines.getBeginLine()); + assertEquals(1, withLines.getEndLine()); + assertEquals(1, withLines.getBeginColumn()); + assertEquals(1 + doc.getLength(), withLines.getEndColumn()); + } + @Test public void testMultiLineRegion() { TextDocument doc = TextDocument.readOnlyString("bonjour\noha\ntristesse", dummyVersion); diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index 12d2b866d5..de268cd6a1 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -296,6 +296,8 @@ public abstract class RuleTst { listener.close(); return reportBuilder.getResult(); } + } catch (RuntimeException e) { + throw e; } catch (Exception e) { throw new RuntimeException(e); } diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java index caa435633b..199259659f 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java @@ -23,7 +23,6 @@ import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.xml.XmlParserOptions; import net.sourceforge.pmd.lang.xml.ast.XmlNode; -import net.sourceforge.pmd.util.document.TextDocument; public class XmlParserImpl { @@ -95,7 +94,7 @@ public class XmlParserImpl { private final AstInfo astInfo; - RootXmlNode(XmlParserImpl parser, Node domNode, TextDocument textDoc) { + RootXmlNode(XmlParserImpl parser, Node domNode, ParserTask task) { super(parser, domNode); this.astInfo = new AstInfo<>(task, this); }