diff --git a/build.xml b/build.xml index 144d91c34..21675b1e1 100644 --- a/build.xml +++ b/build.xml @@ -73,8 +73,8 @@ - + targetfile="${checkstyle.dir}/GeneratedJava14Lexer.java" > + @@ -88,6 +88,14 @@ dir="${checkstyle.dir}"> + + + + + @@ -105,12 +113,16 @@ todir="classes/checkstyle"/> + diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/Checker.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/Checker.java index 6e8ce062a..0e3edbd93 100644 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/Checker.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/Checker.java @@ -40,6 +40,95 @@ import org.apache.regexp.RESyntaxException; public class Checker implements Defn { + + /** + * Overrides ANTLR error reporting so we completely control + * checkstyle's output during parsing. This is important because + * we try parsing with several grammers (with/without support for + * assert). We must not write any error messages when + * parsing fails because with the next grammar it might succeed + * and the user will be confused. + */ + private class SilentJava14Recognizer extends GeneratedJava14Recognizer + { + /** + * Creates a new SilentJava14Recognizer instance. + * + * @param aLexer the tokenstream the recognizer operates on. + */ + private SilentJava14Recognizer(GeneratedJava14Lexer aLexer) + { + super(aLexer); + } + + /** + * Parser error-reporting function, does nothing. + * @param aRex the exception to be reported + */ + public void reportError(RecognitionException aRex) + { + } + + /** + * Parser error-reporting function, does nothing. + * @param aMsg the error message + */ + public void reportError(String aMsg) + { + } + + /** + * Parser warning-reporting function, does nothing. + * @param aMsg the error message + */ + public void reportWarning(String aMsg) + { + } + } + + /** + * Overrides ANTLR error reporting so we completely control + * checkstyle's output during parsing. + * + * @see SilentJava14Recognizer + */ + private class SilentJavaRecognizer extends GeneratedJavaRecognizer + { + /** + * Creates a new SilentJavaRecognizer instance. + * + * @param aLexer the tokenstream the recognizer operates on. + */ + private SilentJavaRecognizer(GeneratedJavaLexer aLexer) + { + super(aLexer); + } + + /** + * Parser error-reporting function, does nothing. + * @param aRex the exception to be reported + */ + public void reportError(RecognitionException aRex) + { + } + + /** + * Parser error-reporting function, does nothing. + * @param aMsg the error message + */ + public void reportError(String aMsg) + { + } + + /** + * Parser warning-reporting function, does nothing. + * @param aMsg the error message + */ + public void reportWarning(String aMsg) + { + } + } + /** configuration */ private final Configuration mConfig; @@ -140,15 +229,38 @@ public class Checker try { fireFileStarted(aFileName); final String[] lines = getLines(aFileName); - VerifierSingleton.getInstance().clearMessages(); - VerifierSingleton.getInstance().setLines(lines); - final Reader sar = new StringArrayReader(lines); - final GeneratedJavaLexer jl = new GeneratedJavaLexer(sar); - jl.setFilename(aFileName); - final GeneratedJavaRecognizer jr = new GeneratedJavaRecognizer(jl); - jr.setFilename(aFileName); - jr.setASTNodeClass(MyCommonAST.class.getName()); - jr.compilationUnit(); + try { + VerifierSingleton.getInstance().clearMessages(); + VerifierSingleton.getInstance().setLines(lines); + final Reader sar = new StringArrayReader(lines); + final GeneratedJava14Lexer jl = new GeneratedJava14Lexer(sar); + jl.setFilename(aFileName); + final GeneratedJava14Recognizer jr = + new SilentJava14Recognizer(jl); + jr.setFilename(aFileName); + jr.setASTNodeClass(MyCommonAST.class.getName()); + jr.compilationUnit(); + } + catch (RecognitionException re) { + + // Parsing might have failed because the checked + // filecontains "assert" statement. Retry with a + // grammar that treats "assert" as an identifier + // and not as a keyword + + // Arghh - the pain - duplicate code! + + VerifierSingleton.getInstance().clearMessages(); + VerifierSingleton.getInstance().setLines(lines); + final Reader sar = new StringArrayReader(lines); + final GeneratedJavaLexer jl = new GeneratedJavaLexer(sar); + jl.setFilename(aFileName); + final GeneratedJavaRecognizer jr = + new GeneratedJavaRecognizer(jl); + jr.setFilename(aFileName); + jr.setASTNodeClass(MyCommonAST.class.getName()); + jr.compilationUnit(); + } errors = VerifierSingleton.getInstance().getMessages(); } catch (FileNotFoundException fnfe) { diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/java.g b/src/checkstyle/com/puppycrawl/tools/checkstyle/java.g index 712e055ba..17526fd53 100644 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/java.g +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/java.g @@ -535,7 +535,15 @@ compoundStatement[MyCommonAST[] aCurlies] ; +// This production provides a slot for adding additional statement productions. +// It is used to simplify an inherited grammar that includes assert statements +// (new Java language feature in JDK 1.4) statement[int[] aType, MyCommonAST[] aCurlies] + : traditionalStatement[aType, aCurlies] + ; + +// a traditional (JDK < 1.4) java statement, assert keyword is not allowed +traditionalStatement[int[] aType, MyCommonAST[] aCurlies] { final MyModifierSet modSet = new MyModifierSet(); final int[] stmtType = new int[1]; @@ -676,9 +684,6 @@ statement[int[] aType, MyCommonAST[] aCurlies] // empty statement | s:SEMI {#s.setType(EMPTY_STAT);} - - // assert statement - // | "assert"^ expression (COLON^ expression)? SEMI! ; diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/java14.g b/src/checkstyle/com/puppycrawl/tools/checkstyle/java14.g new file mode 100644 index 000000000..897ae9dcb --- /dev/null +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/java14.g @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// +// checkstyle: Checks Java source code for adherence to a set of rules. +// Copyright (C) 2001-2002 Oliver Burn +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +//////////////////////////////////////////////////////////////////////////////// +header { +package com.puppycrawl.tools.checkstyle; +} + + +/** Java 1.4 Recognizer + * + * Based heavily on the Grammer example that comes with ANTLR. See + * http://www.antlr.org. + * + */ +class GeneratedJava14Recognizer extends GeneratedJavaRecognizer; + +// Options don't get inherited, copy of option block required. +options { + k = 2; // two token lookahead + exportVocab=GeneratedJava14; // Call its vocabulary "GeneratedJava14" + codeGenMakeSwitchThreshold = 2; // Some optimizations + codeGenBitsetTestThreshold = 3; + defaultErrorHandler = false; // Don't generate parser error handlers + buildAST = true; +} + +// overrides the statement production in java.g, adds assertStatement +statement[int[] aType, MyCommonAST[] aCurlies] + : traditionalStatement[aType, aCurlies] + | assertStatement[aType, aCurlies] + ; + +// assert statement, available since JDK 1.4 +assertStatement[int[] aType, MyCommonAST[] aCurlies] +{ + final MyModifierSet modSet = new MyModifierSet(); + final int[] stmtType = new int[1]; + final MyCommonAST[] stmtBraces = new MyCommonAST[2]; + stmtType[0] = STMT_OTHER; +} + : ASSERT^ expression ( COLON! expression )? SEMI! + ; + +class GeneratedJava14Lexer extends GeneratedJavaLexer; + +options { + exportVocab=GeneratedJava14; // call the vocabulary "GeneratedJava14" + testLiterals=false; // don't automatically test for literals + k=4; // four characters of lookahead + charVocabulary='\u0003'..'\uFFFF'; + codeGenBitsetTestThreshold=20; +} + +tokens { + ASSERT="assert"; +} + +// antlr expects a definition here: 'unexpected token: null' +// To avoid that message, one definition from GeneratedJavaLexer +// is repeated. Rather inelegant but I didn't find a better solution :-( +// Feel free to improve this... +protected +FLOAT_SUFFIX + : 'f'|'F'|'d'|'D' + ; diff --git a/src/tests/com/puppycrawl/tools/checkstyle/InputWhitespace.java b/src/tests/com/puppycrawl/tools/checkstyle/InputWhitespace.java index f3a6b3ae6..ad4a8be66 100644 --- a/src/tests/com/puppycrawl/tools/checkstyle/InputWhitespace.java +++ b/src/tests/com/puppycrawl/tools/checkstyle/InputWhitespace.java @@ -136,4 +136,25 @@ class InputWhitespace o . toString(); return o.toString(); } + + /** assert statement test */ + public void assertTest() + { + // OK + assert true; + + // OK + assert true : "Whups"; + + // evil colons, should be OK + assert "OK".equals(null) ? false : true : "Whups"; + + // WS tests for assert not implemented yet + + // missing WS around assert + // assert(true); + + // missing WS around colon + // assert true:"Whups"; + } }