diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java index e51edb65a9..14ae5eff61 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java @@ -498,4 +498,16 @@ public void testLabelsForTokensWithMixedTypesLRWithoutLabels() { testErrors(test, false); } + + @Test public void testNotImpliedCharactersWithCaseInsensitiveOption() { + String[] test = { + "lexer grammar Test;\n" + + "options { caseInsensitive=true; }\n" + + "TOKEN: [A-z];", + + "warning(" + ErrorType.RANGE_PROBABLY_CONTAINS_NOT_IMPLIED_CHARACTERS.code + "): Test.g4:3:7: Range A..z probably contains not implied characters [\\]^_`. Both bounds should be defined in lower or UPPER case\n" + }; + + testErrors(test, false); + } } diff --git a/tool/src/org/antlr/v4/automata/CharactersDataCheckStatus.java b/tool/src/org/antlr/v4/automata/CharactersDataCheckStatus.java new file mode 100644 index 0000000000..be0ffcf5d9 --- /dev/null +++ b/tool/src/org/antlr/v4/automata/CharactersDataCheckStatus.java @@ -0,0 +1,11 @@ +package org.antlr.v4.automata; + +public class CharactersDataCheckStatus { + public final boolean collision; + public final boolean notImpliedCharacters; + + public CharactersDataCheckStatus(boolean collision, boolean notImpliedCharacters) { + this.collision = collision; + this.notImpliedCharacters = notImpliedCharacters; + } +} diff --git a/tool/src/org/antlr/v4/automata/LexerATNFactory.java b/tool/src/org/antlr/v4/automata/LexerATNFactory.java index 65d65afd26..5f39325d67 100644 --- a/tool/src/org/antlr/v4/automata/LexerATNFactory.java +++ b/tool/src/org/antlr/v4/automata/LexerATNFactory.java @@ -279,7 +279,7 @@ public Handle set(GrammarAST associatedAST, List alts, boolean inver int a = CharSupport.getCharValueFromGrammarCharLiteral(t.getChild(0).getText()); int b = CharSupport.getCharValueFromGrammarCharLiteral(t.getChild(1).getText()); if (checkRange((GrammarAST)t.getChild(0), (GrammarAST)t.getChild(1), a, b)) { - checkRangeAndAddToSet(associatedAST, t, set, a, b, caseInsensitive, true); + checkRangeAndAddToSet(associatedAST, t, set, a, b, caseInsensitive, null); } } else if ( t.getType()==ANTLRParser.LEXER_CHAR_SET ) { @@ -567,28 +567,31 @@ private void applyPrevState(GrammarAST charSetAST, IntervalSet set, CharSetParse } private void checkCharAndAddToSet(GrammarAST ast, IntervalSet set, int c) { - checkRangeAndAddToSet(ast, ast, set, c, c, caseInsensitive, true); + checkRangeAndAddToSet(ast, ast, set, c, c, caseInsensitive, null); } private void checkRangeAndAddToSet(GrammarAST mainAst, IntervalSet set, int a, int b) { - checkRangeAndAddToSet(mainAst, mainAst, set, a, b, caseInsensitive, true); + checkRangeAndAddToSet(mainAst, mainAst, set, a, b, caseInsensitive, null); } - private boolean checkRangeAndAddToSet(GrammarAST rootAst, GrammarAST ast, IntervalSet set, int a, int b, boolean caseInsensitive, boolean reportCollision) { - boolean charactersCollision = false; - RangeBorderCharactersData charactersData = RangeBorderCharactersData.getAndCheckCharactersData(a, b, g, ast); + private CharactersDataCheckStatus checkRangeAndAddToSet(GrammarAST rootAst, GrammarAST ast, IntervalSet set, int a, int b, boolean caseInsensitive, CharactersDataCheckStatus previousStatus) { + CharactersDataCheckStatus status; + RangeBorderCharactersData charactersData = RangeBorderCharactersData.getAndCheckCharactersData(a, b, g, ast, + previousStatus == null || !previousStatus.notImpliedCharacters); if (caseInsensitive) { + status = new CharactersDataCheckStatus(false, charactersData.mixOfLowerAndUpperCharCase); if (charactersData.isSingleRange()) { - checkRangeAndAddToSet(rootAst, ast, set, a, b, false, true); + status = checkRangeAndAddToSet(rootAst, ast, set, a, b, false, status); } else { - charactersCollision = checkRangeAndAddToSet(rootAst, ast, set, charactersData.lowerFrom, charactersData.lowerTo, false, true); + status = checkRangeAndAddToSet(rootAst, ast, set, charactersData.lowerFrom, charactersData.lowerTo, false, status); // Don't report similar warning twice - checkRangeAndAddToSet(rootAst, ast, set, charactersData.upperFrom, charactersData.upperTo, false, !charactersCollision); + status = checkRangeAndAddToSet(rootAst, ast, set, charactersData.upperFrom, charactersData.upperTo, false, status); } } else { - if (reportCollision) { + boolean charactersCollision = previousStatus != null && previousStatus.collision; + if (!charactersCollision) { for (int i = a; i <= b; i++) { if (set.contains(i)) { String setText; @@ -611,20 +614,22 @@ private boolean checkRangeAndAddToSet(GrammarAST rootAst, GrammarAST ast, Interv sb.replace(sb.length() - 3, sb.length(), ""); setText = sb.toString(); } + String charsString = a == b ? String.valueOf((char)a) : (char) a + "-" + (char) b; g.tool.errMgr.grammarError(ErrorType.CHARACTERS_COLLISION_IN_SET, g.fileName, ast.getToken(), - (char) a + "-" + (char) b, setText); + charsString, setText); charactersCollision = true; break; } } } + status = new CharactersDataCheckStatus(charactersCollision, charactersData.mixOfLowerAndUpperCharCase); set.add(a, b); } - return charactersCollision; + return status; } private Transition createTransition(ATNState target, int from, int to, CommonTree tree) { - RangeBorderCharactersData charactersData = RangeBorderCharactersData.getAndCheckCharactersData(from, to, g, tree); + RangeBorderCharactersData charactersData = RangeBorderCharactersData.getAndCheckCharactersData(from, to, g, tree, true); if (caseInsensitive) { if (charactersData.isSingleRange()) { return CodePointTransitions.createWithCodePointRange(target, from, to); diff --git a/tool/src/org/antlr/v4/automata/RangeBorderCharactersData.java b/tool/src/org/antlr/v4/automata/RangeBorderCharactersData.java index 7c30c0feb6..4bc44adf60 100644 --- a/tool/src/org/antlr/v4/automata/RangeBorderCharactersData.java +++ b/tool/src/org/antlr/v4/automata/RangeBorderCharactersData.java @@ -19,7 +19,9 @@ public RangeBorderCharactersData(int lowerFrom, int upperFrom, int lowerTo, int this.mixOfLowerAndUpperCharCase = mixOfLowerAndUpperCharCase; } - public static RangeBorderCharactersData getAndCheckCharactersData(int from, int to, Grammar grammar, CommonTree tree) { + public static RangeBorderCharactersData getAndCheckCharactersData(int from, int to, Grammar grammar, CommonTree tree, + boolean reportRangeContainsNotImpliedCharacters + ) { int lowerFrom = Character.toLowerCase(from); int upperFrom = Character.toUpperCase(from); int lowerTo = Character.toLowerCase(to); @@ -28,7 +30,7 @@ public static RangeBorderCharactersData getAndCheckCharactersData(int from, int boolean isLowerFrom = lowerFrom == from; boolean isLowerTo = lowerTo == to; boolean mixOfLowerAndUpperCharCase = isLowerFrom && !isLowerTo || !isLowerFrom && isLowerTo; - if (mixOfLowerAndUpperCharCase && from <= 0x7F && to <= 0x7F) { + if (reportRangeContainsNotImpliedCharacters && mixOfLowerAndUpperCharCase && from <= 0x7F && to <= 0x7F) { StringBuilder notImpliedCharacters = new StringBuilder(); for (int i = from; i < to; i++) { if (!Character.isAlphabetic(i)) {