Merge pull request #4777 from JakubValtar/error-check-update

Report missing brace in correct tab, suppress other errors until fixed
This commit is contained in:
Ben Fry
2016-12-18 11:04:47 -05:00
committed by GitHub
6 changed files with 339 additions and 285 deletions

View File

@@ -23,11 +23,14 @@ package processing.mode.java.pdex;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
import processing.app.Language;
import processing.app.Messages;
import processing.core.PApplet;
import processing.data.StringList;
@@ -42,25 +45,20 @@ public class ErrorMessageSimplifier {
*/
private static TreeMap<Integer, String> constantsMap;
private static final boolean DEBUG = false;
public ErrorMessageSimplifier() {
new Thread() {
public void run() {
prepareConstantsList();
}
}.start();
}
private static final Pattern tokenRegExp = Pattern.compile("\\b token\\b");
private static void prepareConstantsList() {
constantsMap = new TreeMap<Integer, String>();
constantsMap = new TreeMap<>();
Class<DefaultProblem> probClass = DefaultProblem.class;
Field f[] = probClass.getFields();
for (Field field : f) {
if (Modifier.isStatic(field.getModifiers()))
try {
//System.out.println(field.getName() + " :" + field.get(null));
if (DEBUG) {
Messages.log(field.getName() + " :" + field.get(null));
}
Object val = field.get(null);
if (val instanceof Integer) {
constantsMap.put((Integer) (val), field.getName());
@@ -70,12 +68,14 @@ public class ErrorMessageSimplifier {
break;
}
}
//System.out.println("Total items: " + constantsMap.size());
if (DEBUG) {
Messages.log("Total items: " + constantsMap.size());
}
}
public static String getIDName(int id) {
if (constantsMap == null){
if (constantsMap == null) {
prepareConstantsList();
}
return constantsMap.get(id);
@@ -85,184 +85,199 @@ public class ErrorMessageSimplifier {
/**
* Tones down the jargon in the ecj reported errors.
*/
public static String getSimplifiedErrorMessage(JavaProblem problem) {
if (problem == null) return null;
public static String getSimplifiedErrorMessage(IProblem iprob) {
if (iprob == null) return null;
IProblem iprob = problem.getIProblem();
String args[] = iprob.getArguments();
// Base.log("Simplifying message: " + problem.getMessage() + " ID: "
// + getIDName(iprob.getID()));
// Base.log("Arg count: " + args.length);
// for (int i = 0; i < args.length; i++) {
// Base.log("Arg " + args[i]);
// }
if (DEBUG) {
Messages.log("Simplifying message: " + iprob.getMessage() +
" ID: " + getIDName(iprob.getID()));
Messages.log("Arg count: " + args.length);
for (String arg : args) {
Messages.log("Arg " + arg);
}
}
String result = null;
switch (iprob.getID()) {
case IProblem.ParsingError:
if (args.length > 0) {
result = Language.interpolate("editor.status.error_on", args[0]);
}
break;
case IProblem.ParsingError:
if (args.length > 0) {
result = Language.interpolate("editor.status.error_on", args[0]);
}
break;
case IProblem.ParsingErrorDeleteToken:
if (args.length > 0) {
result = Language.interpolate("editor.status.error_on", args[0]);
}
break;
case IProblem.ParsingErrorDeleteToken:
if (args.length > 0) {
result = Language.interpolate("editor.status.error_on", args[0]);
}
break;
case IProblem.ParsingErrorInsertToComplete:
if (args.length > 0) {
if (args[0].length() == 1) {
result = getErrorMessageForBracket(args[0].charAt(0));
} else {
if (args[0].equals("AssignmentOperator Expression")) {
result = Language.interpolate("editor.status.missing.add", "=");
} else if (args[0].equalsIgnoreCase(") Statement")) {
case IProblem.ParsingErrorInsertToComplete:
if (args.length > 0) {
if (args[0].length() == 1) {
result = getErrorMessageForBracket(args[0].charAt(0));
} else {
result = Language.interpolate("editor.status.error_on", args[0]);
if (args[0].equals("AssignmentOperator Expression")) {
result = Language.interpolate("editor.status.missing.add", "=");
} else if (args[0].equalsIgnoreCase(") Statement")) {
result = getErrorMessageForBracket(args[0].charAt(0));
} else {
result = Language.interpolate("editor.status.error_on", args[0]);
}
}
}
}
break;
break;
case IProblem.ParsingErrorInvalidToken:
if (args.length > 0) {
if (args[1].equals("VariableDeclaratorId")) {
if (args[0].equals("int")) {
result = Language.text ("editor.status.reserved_words");
case IProblem.ParsingErrorInvalidToken:
if (args.length > 0) {
if (args[1].equals("VariableDeclaratorId")) {
if (args[0].equals("int")) {
result = Language.text("editor.status.reserved_words");
} else {
result = Language.interpolate("editor.status.error_on", args[0]);
}
} else {
result = Language.interpolate("editor.status.error_on", args[0]);
}
} else {
result = Language.interpolate("editor.status.error_on", args[0]);
}
}
break;
break;
case IProblem.ParsingErrorInsertTokenAfter:
if (args.length > 0) {
if (args[1].length() == 1) {
result = getErrorMessageForBracket(args[1].charAt(0));
}
else {
// https://github.com/processing/processing/issues/3104
if (args[1].equalsIgnoreCase("Statement")) {
result = Language.interpolate("editor.status.error_on", args[0]);
case IProblem.ParsingErrorInsertTokenAfter:
if (args.length > 0) {
if (args[1].length() == 1) {
result = getErrorMessageForBracket(args[1].charAt(0));
} else {
result =
Language.interpolate("editor.status.error_on", args[0]) + " " +
Language.interpolate("editor.status.missing.add", args[1]);
// https://github.com/processing/processing/issues/3104
if (args[1].equalsIgnoreCase("Statement")) {
result = Language.interpolate("editor.status.error_on", args[0]);
} else {
result =
Language.interpolate("editor.status.error_on", args[0]) + " " +
Language.interpolate("editor.status.missing.add", args[1]);
}
}
}
}
break;
break;
case IProblem.UndefinedConstructor:
if (args.length == 2) {
String constructorName = args[0];
// For messages such as "contructor sketch_name.ClassXYZ() is undefined", change
// constructor name to "ClassXYZ()". See #3434
if (constructorName.contains(".")) {
// arg[0] contains sketch name twice: sketch_150705a.sketch_150705a.Thing
constructorName = constructorName.substring(constructorName.indexOf('.') + 1);
constructorName = constructorName.substring(constructorName.indexOf('.') + 1);
case IProblem.UndefinedConstructor:
if (args.length == 2) {
String constructorName = args[0];
// For messages such as "contructor sketch_name.ClassXYZ() is undefined", change
// constructor name to "ClassXYZ()". See #3434
if (constructorName.contains(".")) {
// arg[0] contains sketch name twice: sketch_150705a.sketch_150705a.Thing
constructorName = constructorName.substring(constructorName.indexOf('.') + 1);
constructorName = constructorName.substring(constructorName.indexOf('.') + 1);
}
String constructorArgs = removePackagePrefixes(args[args.length - 1]);
result = Language.interpolate("editor.status.undefined_constructor", constructorName, constructorArgs);
}
String constructorArgs = removePackagePrefixes(args[args.length - 1]);
result = Language.interpolate("editor.status.undefined_constructor", constructorName, constructorArgs);
}
break;
break;
case IProblem.UndefinedMethod:
if (args.length > 2) {
String methodName = args[args.length - 2];
String methodArgs = removePackagePrefixes(args[args.length - 1]);
result = Language.interpolate("editor.status.undefined_method", methodName, methodArgs);
}
break;
case IProblem.UndefinedMethod:
if (args.length > 2) {
String methodName = args[args.length - 2];
String methodArgs = removePackagePrefixes(args[args.length - 1]);
result = Language.interpolate("editor.status.undefined_method", methodName, methodArgs);
}
break;
case IProblem.ParameterMismatch:
if (args.length > 3) {
// 2nd arg is method name, 3rd arg is correct param list
if (args[2].trim().length() == 0) {
// the case where no params are needed.
result = Language.interpolate("editor.status.empty_param", args[1]);
case IProblem.ParameterMismatch:
if (args.length > 3) {
// 2nd arg is method name, 3rd arg is correct param list
if (args[2].trim().length() == 0) {
// the case where no params are needed.
result = Language.interpolate("editor.status.empty_param", args[1]);
} else {
result = Language.interpolate("editor.status.wrong_param",
args[1], args[1], removePackagePrefixes(args[2]));
} else {
result = Language.interpolate("editor.status.wrong_param",
args[1], args[1], removePackagePrefixes(args[2]));
// String method = q(args[1]);
// String methodDef = " \"" + args[1] + "(" + getSimpleName(args[2]) + ")\"";
// result = result.replace("method", method);
// result += methodDef;
}
}
}
break;
break;
case IProblem.UndefinedField:
if (args.length > 0) {
result = Language.interpolate("editor.status.undef_global_var", args[0]);
}
break;
case IProblem.UndefinedField:
if (args.length > 0) {
result = Language.interpolate("editor.status.undef_global_var", args[0]);
}
break;
case IProblem.UndefinedType:
if (args.length > 0) {
result = Language.interpolate("editor.status.undef_class", args[0]);
}
break;
case IProblem.UndefinedType:
if (args.length > 0) {
result = Language.interpolate("editor.status.undef_class", args[0]);
}
break;
case IProblem.UnresolvedVariable:
if (args.length > 0) {
result = Language.interpolate("editor.status.undef_var", args[0]);
}
break;
case IProblem.UnresolvedVariable:
if (args.length > 0) {
result = Language.interpolate("editor.status.undef_var", args[0]);
}
break;
case IProblem.UndefinedName:
if (args.length > 0) {
result = Language.interpolate("editor.status.undef_name", args[0]);
}
break;
case IProblem.UndefinedName:
if (args.length > 0) {
result = Language.interpolate("editor.status.undef_name", args[0]);
}
break;
case IProblem.TypeMismatch:
if (args.length > 1) {
result = Language.interpolate("editor.status.type_mismatch", args[0], args[1]);
case IProblem.TypeMismatch:
if (args.length > 1) {
result = Language.interpolate("editor.status.type_mismatch", args[0], args[1]);
// result = result.replace("typeA", q(args[0]));
// result = result.replace("typeB", q(args[1]));
}
break;
}
break;
case IProblem.LocalVariableIsNeverUsed:
if (args.length > 0) {
result = Language.interpolate("editor.status.unused_variable", args[0]);
}
break;
case IProblem.LocalVariableIsNeverUsed:
if (args.length > 0) {
result = Language.interpolate("editor.status.unused_variable", args[0]);
}
break;
case IProblem.UninitializedLocalVariable:
if (args.length > 0) {
result = Language.interpolate("editor.status.uninitialized_variable", args[0]);
}
break;
case IProblem.UninitializedLocalVariable:
if (args.length > 0) {
result = Language.interpolate("editor.status.uninitialized_variable", args[0]);
}
break;
case IProblem.AssignmentHasNoEffect:
if (args.length > 0) {
result = Language.interpolate("editor.status.no_effect_assignment", args[0]);
}
break;
case IProblem.AssignmentHasNoEffect:
if (args.length > 0) {
result = Language.interpolate("editor.status.no_effect_assignment", args[0]);
}
break;
case IProblem.HidingEnclosingType:
if (args.length > 0) {
result = Language.interpolate("editor.status.hiding_enclosing_type", args[0]);
}
case IProblem.HidingEnclosingType:
if (args.length > 0) {
result = Language.interpolate("editor.status.hiding_enclosing_type", args[0]);
}
break;
default:
String message = iprob.getMessage();
if (message != null) {
// Remove all instances of token
// "Syntax error on token 'blah', delete this token"
Matcher matcher = tokenRegExp.matcher(message);
message = matcher.replaceAll("");
result = message;
}
}
//log("Simplified Error Msg: " + result);
return (result == null) ? problem.getMessage() : result;
if (DEBUG) {
Messages.log("Simplified Error Msg: " + result);
}
return result;
}

View File

@@ -20,9 +20,6 @@ along with this program; if not, write to the Free Software Foundation, Inc.
package processing.mode.java.pdex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.core.compiler.IProblem;
import processing.app.Problem;
@@ -33,10 +30,6 @@ import processing.app.Problem;
* according to its tab, including the original IProblem object
*/
public class JavaProblem implements Problem {
/**
* The IProblem which is being wrapped
*/
private IProblem iProblem;
/**
* The tab number to which the error belongs to
*/
@@ -67,25 +60,28 @@ public class JavaProblem implements Problem {
public static final int ERROR = 1, WARNING = 2;
public JavaProblem(String message, int type, int tabIndex, int lineNumber) {
this.message = message;
this.type = type;
this.tabIndex = tabIndex;
this.lineNumber = lineNumber;
}
/**
*
* @param iProblem - The IProblem which is being wrapped
* @param tabIndex - The tab number to which the error belongs to
* @param lineNumber - Line number(pde code) of the error
*/
public JavaProblem(IProblem iProblem, int tabIndex, int lineNumber) {
this.iProblem = iProblem;
public static JavaProblem fromIProblem(IProblem iProblem, int tabIndex, int lineNumber) {
int type = 0;
if(iProblem.isError()) {
type = ERROR;
}
else if(iProblem.isWarning()) {
} else if (iProblem.isWarning()) {
type = WARNING;
}
this.tabIndex = tabIndex;
this.lineNumber = lineNumber;
this.message = process(iProblem);
this.message = ErrorMessageSimplifier.getSimplifiedErrorMessage(this);
//ErrorMessageSimplifier.getSimplifiedErrorMessage(this);
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem);
return new JavaProblem(message, type, tabIndex, lineNumber);
}
public void setPDEOffsets(int startOffset, int stopOffset){
@@ -93,62 +89,41 @@ public class JavaProblem implements Problem {
this.stopOffset = stopOffset;
}
@Override
public int getStartOffset() {
return startOffset;
}
@Override
public int getStopOffset() {
return stopOffset;
}
public String toString() {
return new String("TAB " + tabIndex + ",LN " + lineNumber + "LN START OFF: "
+ startOffset + ",LN STOP OFF: " + stopOffset + ",PROB: "
+ message);
}
@Override
public boolean isError() {
return type == ERROR;
}
@Override
public boolean isWarning() {
return type == WARNING;
}
@Override
public String getMessage() {
return message;
}
public IProblem getIProblem() {
return iProblem;
}
@Override
public int getTabIndex() {
return tabIndex;
}
@Override
public int getLineNumber() {
return lineNumber;
}
/**
* Remember to subtract a -1 to line number because in compile check code an
* extra package statement is added, so all line numbers are increased by 1
*
* @return
*/
public int getSourceLineNumber() {
return iProblem.getSourceLineNumber();
}
public void setType(int ProblemType){
if(ProblemType == ERROR)
type = ERROR;
else if(ProblemType == WARNING)
type = WARNING;
else throw new IllegalArgumentException("Illegal Problem type passed to Problem.setType(int)");
}
public String[] getImportSuggestions() {
return importSuggestions;
}
@@ -157,47 +132,11 @@ public class JavaProblem implements Problem {
importSuggestions = a;
}
private static final Pattern tokenRegExp = Pattern.compile("\\b token\\b");
public static String process(IProblem problem) {
return process(problem.getMessage());
}
/**
* Processes error messages and attempts to make them a bit more english like.
* Currently performs:
* <li>Remove all instances of token. "Syntax error on token 'blah', delete this token"
* becomes "Syntax error on 'blah', delete this"
* @param message - The message to be processed
* @return String - The processed message
*/
public static String process(String message) {
// Remove all instances of token
// "Syntax error on token 'blah', delete this token"
if(message == null) return null;
Matcher matcher = tokenRegExp.matcher(message);
message = matcher.replaceAll("");
return message;
}
// Split camel case words into separate words.
// "VaraibleDeclaration" becomes "Variable Declaration"
// But sadly "PApplet" become "P Applet" and so on.
public static String splitCamelCaseWord(String word) {
String newWord = "";
for (int i = 1; i < word.length(); i++) {
if (Character.isUpperCase(word.charAt(i))) {
// System.out.println(word.substring(0, i) + " "
// + word.substring(i));
newWord += word.substring(0, i) + " ";
word = word.substring(i);
i = 1;
}
}
newWord += word;
// System.out.println(newWord);
return newWord.trim();
@Override
public String toString() {
return "TAB " + tabIndex + ",LN " + lineNumber + "LN START OFF: "
+ startOffset + ",LN STOP OFF: " + stopOffset + ",PROB: "
+ message;
}
}

View File

@@ -1,6 +1,7 @@
package processing.mode.java.pdex;
import com.google.classpath.ClassPath;
import com.google.classpath.ClassPathFactory;
import com.google.classpath.RegExpResourceFilter;
import org.eclipse.jdt.core.compiler.IProblem;
@@ -36,12 +37,15 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -1074,59 +1078,76 @@ public class PDEX {
private void handleSketchProblems(PreprocessedSketch ps) {
// Process problems
Map<String, String[]> suggCache =
JavaMode.importSuggestEnabled ? new HashMap<>() : Collections.emptyMap();
final List<Problem> problems = new ArrayList<>();
IProblem[] iproblems = ps.compilationUnit.getProblems();
final List<Problem> problems = Arrays.stream(iproblems)
// Filter Warnings if they are not enabled
.filter(iproblem -> !(iproblem.isWarning() && !JavaMode.warningsEnabled))
// Hide a useless error which is produced when a line ends with
// an identifier without a semicolon. "Missing a semicolon" is
// also produced and is preferred over this one.
// (Syntax error, insert ":: IdentifierOrNew" to complete Expression)
// See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=405780
.filter(iproblem -> !iproblem.getMessage()
.contains("Syntax error, insert \":: IdentifierOrNew\""))
// Transform into our Problems
.map(iproblem -> {
int start = iproblem.getSourceStart();
int stop = iproblem.getSourceEnd() + 1; // make it exclusive
SketchInterval in = ps.mapJavaToSketch(start, stop);
if (in == SketchInterval.BEFORE_START) return null;
int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
JavaProblem p = new JavaProblem(iproblem, in.tabIndex, line);
p.setPDEOffsets(in.startTabOffset, in.stopTabOffset);
return p;
})
.filter(p -> p != null)
.collect(Collectors.toList());
// Handle import suggestions
if (JavaMode.importSuggestEnabled) {
Map<String, List<Problem>> undefinedTypeProblems = problems.stream()
// Get only problems with undefined types/names
.filter(p -> {
int id = ((JavaProblem) p).getIProblem().getID();
return id == IProblem.UndefinedType ||
id == IProblem.UndefinedName ||
id == IProblem.UnresolvedVariable;
})
// Group problems by the missing type/name
.collect(Collectors.groupingBy(p -> ((JavaProblem) p).getIProblem().getArguments()[0]));
{ // Handle missing brace problems
IProblem missingBraceProblem = Arrays.stream(iproblems)
.filter(ErrorChecker::isMissingBraceProblem)
.findFirst()
// Ignore if it is at the end of file
.filter(p -> p.getSourceEnd() + 1 < ps.javaCode.length())
// Ignore if the tab number does not match our detected tab number
.filter(p -> ps.missingBraceProblems.isEmpty() ||
ps.missingBraceProblems.get(0).getTabIndex() ==
ps.mapJavaToSketch(p.getSourceStart(), p.getSourceEnd()+1).tabIndex
)
.orElse(null);
if (!undefinedTypeProblems.isEmpty()) {
final ClassPath cp = ps.searchClassPath;
// Get suggestions for each missing type, update the problems
undefinedTypeProblems.entrySet().stream()
.forEach(entry -> {
String missingClass = entry.getKey();
List<Problem> affectedProblems = entry.getValue();
String[] suggestions = getImportSuggestions(cp, missingClass);
affectedProblems.forEach(p -> ((JavaProblem) p).setImportSuggestions(suggestions));
});
// If there is missing brace ignore all other problems
if (missingBraceProblem != null) {
// Prefer ECJ problem, shows location more accurately
iproblems = new IProblem[]{missingBraceProblem};
} else if (!ps.missingBraceProblems.isEmpty()) {
// Fallback to manual detection
problems.addAll(ps.missingBraceProblems);
}
}
AtomicReference<ClassPath> searchClassPath = new AtomicReference<>(null);
if (problems.isEmpty()) {
List<Problem> cuProblems = Arrays.stream(iproblems)
// Filter Warnings if they are not enabled
.filter(iproblem -> !(iproblem.isWarning() && !JavaMode.warningsEnabled))
// Hide a useless error which is produced when a line ends with
// an identifier without a semicolon. "Missing a semicolon" is
// also produced and is preferred over this one.
// (Syntax error, insert ":: IdentifierOrNew" to complete Expression)
// See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=405780
.filter(iproblem -> !iproblem.getMessage()
.contains("Syntax error, insert \":: IdentifierOrNew\""))
// Transform into our Problems
.map(iproblem -> {
int start = iproblem.getSourceStart();
int stop = iproblem.getSourceEnd() + 1; // make it exclusive
SketchInterval in = ps.mapJavaToSketch(start, stop);
if (in == SketchInterval.BEFORE_START) return null;
int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
JavaProblem p = JavaProblem.fromIProblem(iproblem, in.tabIndex, line);
p.setPDEOffsets(in.startTabOffset, in.stopTabOffset);
// Handle import suggestions
if (JavaMode.importSuggestEnabled && isUndefinedTypeProblem(iproblem)) {
ClassPath cp = searchClassPath.updateAndGet(prev -> prev != null ?
prev : new ClassPathFactory().createFromPaths(ps.searchClassPathArray));
String[] s = suggCache.computeIfAbsent(iproblem.getArguments()[0],
name -> getImportSuggestions(cp, name));
p.setImportSuggestions(s);
}
return p;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
problems.addAll(cuProblems);
}
if (scheduledUiUpdate != null) {
scheduledUiUpdate.cancel(true);
}
@@ -1142,6 +1163,29 @@ public class PDEX {
}
static private boolean isUndefinedTypeProblem(IProblem iproblem) {
int id = iproblem.getID();
return id == IProblem.UndefinedType ||
id == IProblem.UndefinedName ||
id == IProblem.UnresolvedVariable;
}
static private boolean isMissingBraceProblem(IProblem iproblem) {
switch (iproblem.getID()) {
case IProblem.ParsingErrorInsertToComplete: {
char brace = iproblem.getArguments()[0].charAt(0);
return brace == '{' || brace == '}';
}
case IProblem.ParsingErrorInsertTokenAfter: {
char brace = iproblem.getArguments()[1].charAt(0);
return brace == '{' || brace == '}';
}
default:
return false;
}
}
public static String[] getImportSuggestions(ClassPath cp, String className) {
RegExpResourceFilter regf = new RegExpResourceFilter(
Pattern.compile(".*"),

View File

@@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import processing.app.Problem;
import processing.app.Sketch;
import processing.core.PApplet;
import processing.mode.java.pdex.TextTransform.OffsetMapper;
@@ -25,7 +26,7 @@ public class PreprocessedSketch {
public final ClassPath classPath;
public final URLClassLoader classLoader;
public final ClassPath searchClassPath;
public final String[] searchClassPathArray;
public final int[] tabStartOffsets;
@@ -34,6 +35,8 @@ public class PreprocessedSketch {
public final OffsetMapper offsetMapper;
public final List<Problem> missingBraceProblems;
public final boolean hasSyntaxErrors;
public final boolean hasCompilationErrors;
@@ -203,7 +206,7 @@ public class PreprocessedSketch {
public ClassPath classPath;
public URLClassLoader classLoader;
public ClassPath searchClassPath;
public String[] searchClassPathArray;
public int[] tabStartOffsets = new int[0];
@@ -212,6 +215,8 @@ public class PreprocessedSketch {
public OffsetMapper offsetMapper;
public final List<Problem> missingBraceProblems = new ArrayList<>(0);
public boolean hasSyntaxErrors;
public boolean hasCompilationErrors;
@@ -237,7 +242,7 @@ public class PreprocessedSketch {
classPath = b.classPath;
classLoader = b.classLoader;
searchClassPath = b.searchClassPath;
searchClassPathArray = b.searchClassPathArray;
tabStartOffsets = b.tabStartOffsets;
@@ -246,6 +251,8 @@ public class PreprocessedSketch {
offsetMapper = b.offsetMapper != null ? b.offsetMapper : OffsetMapper.EMPTY_MAPPER;
missingBraceProblems = Collections.unmodifiableList(b.missingBraceProblems);
hasSyntaxErrors = b.hasSyntaxErrors;
hasCompilationErrors = b.hasCompilationErrors;

View File

@@ -341,7 +341,7 @@ public class PreprocessingService {
boolean rebuildClassPath = reloadCodeFolder || rebuildLibraryClassPath ||
prevResult.classLoader == null || prevResult.classPath == null ||
prevResult.classPathArray == null || prevResult.searchClassPath == null;
prevResult.classPathArray == null || prevResult.searchClassPathArray == null;
if (reloadCodeFolder) {
codeFolderClassPath = buildCodeFolderClassPath(sketch);
@@ -381,17 +381,25 @@ public class PreprocessingService {
searchClassPath.addAll(coreLibraryClassPath);
searchClassPath.addAll(codeFolderClassPath);
String[] searchClassPathArray = searchClassPath.stream().toArray(String[]::new);
result.searchClassPath = classPathFactory.createFromPaths(searchClassPathArray);
result.searchClassPathArray = searchClassPath.stream().toArray(String[]::new);
}
} else {
result.classLoader = prevResult.classLoader;
result.classPath = prevResult.classPath;
result.searchClassPath = prevResult.searchClassPath;
result.searchClassPathArray = prevResult.searchClassPathArray;
result.classPathArray = prevResult.classPathArray;
}
}
{ // Check for missing braces
List<JavaProblem> missingBraceProblems =
SourceUtils.checkForMissingBraces(workBuffer, result.tabStartOffsets);
if (!missingBraceProblems.isEmpty()) {
result.missingBraceProblems.addAll(missingBraceProblems);
result.hasSyntaxErrors = true;
}
}
// Transform code to parsable state
String parsableStage = toParsable.apply();
OffsetMapper parsableMapper = toParsable.getMapper();
@@ -414,7 +422,7 @@ public class PreprocessingService {
makeAST(parser, compilableStageChars, COMPILER_OPTIONS);
// Get syntax problems from compilable AST
result.hasSyntaxErrors = Arrays.stream(compilableCU.getProblems())
result.hasSyntaxErrors |= Arrays.stream(compilableCU.getProblems())
.anyMatch(IProblem::isError);
// Generate bindings after getting problems - avoids

View File

@@ -323,4 +323,45 @@ public class SourceUtils {
}
static public List<JavaProblem> checkForMissingBraces(StringBuilder p, int[] tabStartOffsets) {
List<JavaProblem> problems = new ArrayList<>(0);
tabLoop: for (int tabIndex = 0; tabIndex < tabStartOffsets.length; tabIndex++) {
int tabStartOffset = tabStartOffsets[tabIndex];
int tabEndOffset = (tabIndex < tabStartOffsets.length - 1) ?
tabStartOffsets[tabIndex + 1] : p.length();
int depth = 0;
int lineNumber = 0;
for (int i = tabStartOffset; i < tabEndOffset; i++) {
char ch = p.charAt(i);
switch (ch) {
case '{':
depth++;
break;
case '}':
depth--;
break;
case '\n':
lineNumber++;
break;
}
if (depth < 0) {
JavaProblem problem =
new JavaProblem("Found one too many } characters without { to match it.",
JavaProblem.ERROR, tabIndex, lineNumber);
problem.setPDEOffsets(i - tabStartOffset, i - tabStartOffset + 1);
problems.add(problem);
continue tabLoop;
}
}
if (depth > 0) {
JavaProblem problem =
new JavaProblem("Found one too many { characters without } to match it.",
JavaProblem.ERROR, tabIndex, lineNumber - 1);
problem.setPDEOffsets(tabEndOffset - tabStartOffset - 2, tabEndOffset - tabStartOffset - 1);
problems.add(problem);
}
}
return problems;
}
}