Detect missing braces in tabs, ignore other problems until fixed

This commit is contained in:
Jakub Valtar
2016-12-16 04:36:23 +01:00
parent dd31bf2fbe
commit b72a39564b
5 changed files with 145 additions and 37 deletions
@@ -60,21 +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) {
public static JavaProblem fromIProblem(IProblem iProblem, int tabIndex, int lineNumber) {
int type = 0;
if(iProblem.isError()) {
type = ERROR;
} else if (iProblem.isWarning()) {
type = WARNING;
}
this.tabIndex = tabIndex;
this.lineNumber = lineNumber;
this.message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem);
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem);
return new JavaProblem(message, type, tabIndex, lineNumber);
}
public void setPDEOffsets(int startOffset, int stopOffset){
+76 -32
View File
@@ -1079,40 +1079,69 @@ public class PDEX {
Map<String, String[]> suggCache =
JavaMode.importSuggestEnabled ? new HashMap<>() : Collections.emptyMap();
// Process problems
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);
// Handle import suggestions
if (JavaMode.importSuggestEnabled && isUndefinedTypeProblem(iproblem)) {
ClassPath cp = ps.searchClassPath;
String[] s = suggCache.computeIfAbsent(iproblem.getArguments()[0],
name -> getImportSuggestions(cp, name));
p.setImportSuggestions(s);
}
{ // 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);
return p;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 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);
}
}
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 = ps.searchClassPath;
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);
@@ -1129,13 +1158,28 @@ public class PDEX {
}
private boolean isUndefinedTypeProblem(IProblem iproblem) {
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(
@@ -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;
@@ -34,6 +35,8 @@ public class PreprocessedSketch {
public final OffsetMapper offsetMapper;
public final List<Problem> missingBraceProblems;
public final boolean hasSyntaxErrors;
public final boolean hasCompilationErrors;
@@ -212,6 +215,8 @@ public class PreprocessedSketch {
public OffsetMapper offsetMapper;
public final List<Problem> missingBraceProblems = new ArrayList<>(0);
public boolean hasSyntaxErrors;
public boolean hasCompilationErrors;
@@ -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;
@@ -392,6 +392,15 @@ public class PreprocessingService {
}
}
{ // 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 +423,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
@@ -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;
}
}