Skip to content

Commit 2bf7e64

Browse files
committed
Improve reporting of preprocessor-related parsing failures
Line information was incorrect or incomplete in some cases: - within include files - within compiler directives - when include processing fails due to self-referencing include files This was discovered due to the new `ParsingError` check triggering file pointer errors when trying to raise issues on included tokens.
1 parent ff68f3a commit 2bf7e64

File tree

12 files changed

+309
-71
lines changed

12 files changed

+309
-71
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
### Changed
2121

2222
- Exclude types annotated with attributes in `UnusedType`.
23+
- Improve reporting of parsing failures occurring within include files.
24+
- Improve reporting of parsing failures occurring within compiler directives.
2325

2426
### Deprecated
2527

delphi-frontend/src/main/antlr3/au/com/integradev/delphi/antlr/Delphi.g

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ tokens {
100100
package au.com.integradev.delphi.antlr;
101101

102102
import au.com.integradev.delphi.antlr.ast.node.*;
103+
import au.com.integradev.delphi.antlr.ast.token.DelphiTokenImpl;
104+
import au.com.integradev.delphi.antlr.ast.token.IncludeToken;
105+
import au.com.integradev.delphi.utils.LocatableException;
103106
}
104107

105108
@lexer::header
@@ -127,6 +130,7 @@ import au.com.integradev.delphi.antlr.ast.node.*;
127130
package au.com.integradev.delphi.antlr;
128131

129132
import org.apache.commons.lang3.StringUtils;
133+
import au.com.integradev.delphi.utils.LocatableException;
130134
}
131135

132136
@lexer::members {
@@ -143,15 +147,23 @@ import org.apache.commons.lang3.StringUtils;
143147
public void reportError(RecognitionException e) {
144148
String hdr = this.getErrorHeader(e);
145149
String msg = this.getErrorMessage(e, this.getTokenNames());
146-
throw new LexerException(hdr + " " + msg, e.line, e);
150+
throwLexerException(hdr + " " + msg, e.line, e);
147151
}
148152

149153
@Override
150154
public String getErrorHeader(RecognitionException e) {
151155
return "line " + e.line + ":" + e.charPositionInLine;
152156
}
153157

154-
public static class LexerException extends RuntimeException {
158+
private void throwLexerException(String message, int line) {
159+
throwLexerException(message, line, null);
160+
}
161+
162+
protected void throwLexerException(String message, int line, Throwable cause) {
163+
throw new LexerException(message, line, cause);
164+
}
165+
166+
public static class LexerException extends RuntimeException implements LocatableException {
155167
private final int line;
156168

157169
public LexerException(String message, int line) {
@@ -164,6 +176,7 @@ import org.apache.commons.lang3.StringUtils;
164176
this.line = line;
165177
}
166178

179+
@Override
167180
public int getLine() {
168181
return line;
169182
}
@@ -237,13 +250,14 @@ import org.apache.commons.lang3.StringUtils;
237250
break;
238251

239252
case EOF:
240-
throw new LexerException(
253+
throwLexerException(
241254
"line "
242255
+ state.tokenStartLine
243256
+ ":"
244257
+ state.tokenStartCharPositionInLine
245258
+ " unterminated multi-line comment",
246259
state.tokenStartLine);
260+
break;
247261

248262
default:
249263
// do nothing
@@ -334,22 +348,29 @@ import org.apache.commons.lang3.StringUtils;
334348
}
335349

336350
private Token changeTokenType(int type, int offset) {
337-
CommonToken t = new CommonToken(input.LT(offset));
338-
t.setType(type);
339-
return t;
351+
CommonToken result = cloneToken((CommonToken) input.LT(offset));
352+
result.setType(type);
353+
return result;
340354
}
341355

342356
private Token combineLastNTokens(int type, int count) {
343357
CommonToken firstToken = (CommonToken) input.LT(-count);
344358
CommonToken lastToken = (CommonToken) input.LT(-1);
345-
CommonToken result = new CommonToken(lastToken);
359+
CommonToken result = cloneToken(lastToken);
346360
result.setType(type);
347361
result.setStartIndex(firstToken.getStartIndex());
348362
result.setLine(firstToken.getLine());
349363
result.setCharPositionInLine(firstToken.getCharPositionInLine());
350364
return result;
351365
}
352366

367+
private static CommonToken cloneToken(CommonToken other) {
368+
if (other instanceof IncludeToken) {
369+
return new IncludeToken((IncludeToken) other);
370+
}
371+
return new CommonToken(other);
372+
}
373+
353374
private BinaryExpressionNodeImpl createBinaryExpression(Object operator) {
354375
Token token = adaptor.getToken(operator);
355376
return new BinaryExpressionNodeImpl(token);
@@ -359,7 +380,20 @@ import org.apache.commons.lang3.StringUtils;
359380
public void reportError(RecognitionException e) {
360381
String hdr = this.getErrorHeader(e);
361382
String msg = this.getErrorMessage(e, this.getTokenNames());
362-
throw new ParserException(hdr + " " + msg, e.line, e);
383+
384+
String message = hdr + " " + msg;
385+
int line;
386+
387+
if (e.token == null) {
388+
line = 0;
389+
} else if (e.token instanceof IncludeToken) {
390+
line = ((DelphiTokenImpl) ((IncludeToken) e.token).getInsertionToken()).getBeginLine();
391+
message = "included on line " + line + " :: " + message;
392+
} else {
393+
line = e.token.getLine();
394+
}
395+
396+
throw new ParserException(message, line, e);
363397
}
364398

365399
@Override
@@ -381,16 +415,17 @@ import org.apache.commons.lang3.StringUtils;
381415
resetBinaryExpressionTokens(binaryExpression.getRight());
382416
}
383417

384-
public static class ParserException extends RuntimeException {
418+
public static class ParserException extends RuntimeException implements LocatableException {
385419
private final int line;
386420

387421
public ParserException(String message, int line, Throwable cause) {
388422
super(message, cause);
389423
this.line = line;
390424
}
391425

426+
@Override
392427
public int getLine() {
393-
return line;
428+
return this.line;
394429
}
395430
}
396431
}

delphi-frontend/src/main/java/au/com/integradev/delphi/antlr/ast/token/DelphiTokenImpl.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ public int getEndColumn() {
8484

8585
private void calculatePosition() {
8686
if (isIncludedToken()) {
87-
FilePosition insertionPosition = ((IncludeToken) token).getInsertionPosition();
88-
beginLine = insertionPosition.getBeginLine();
89-
beginColumn = insertionPosition.getBeginColumn();
90-
endLine = insertionPosition.getEndLine();
91-
endColumn = insertionPosition.getEndColumn();
87+
DelphiToken insertionToken = ((IncludeToken) token).getInsertionToken();
88+
beginLine = insertionToken.getBeginLine();
89+
beginColumn = insertionToken.getBeginColumn();
90+
endLine = insertionToken.getEndLine();
91+
endColumn = insertionToken.getEndColumn();
9292
} else if (isComment() || isCompilerDirective()) {
9393
TokenLocation location =
9494
new TokenLocation(token.getLine(), token.getCharPositionInLine(), token.getText());

delphi-frontend/src/main/java/au/com/integradev/delphi/antlr/ast/token/IncludeToken.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,31 @@
1818
*/
1919
package au.com.integradev.delphi.antlr.ast.token;
2020

21+
import org.antlr.runtime.CharStream;
2122
import org.antlr.runtime.CommonToken;
2223
import org.antlr.runtime.Token;
23-
import org.sonar.plugins.communitydelphi.api.check.FilePosition;
2424
import org.sonar.plugins.communitydelphi.api.token.DelphiToken;
2525

2626
public class IncludeToken extends CommonToken {
27-
private final FilePosition insertionPosition;
27+
private final DelphiToken insertionToken;
28+
29+
public IncludeToken(
30+
CharStream input, int type, int channel, int start, int stop, DelphiToken insertionToken) {
31+
super(input, type, channel, start, stop);
32+
this.insertionToken = insertionToken;
33+
}
2834

2935
public IncludeToken(Token token, DelphiToken insertionToken) {
3036
super(token);
31-
this.insertionPosition = FilePosition.from(insertionToken);
37+
this.insertionToken = insertionToken;
38+
}
39+
40+
public IncludeToken(IncludeToken other) {
41+
super(other);
42+
this.insertionToken = other.insertionToken;
3243
}
3344

34-
public FilePosition getInsertionPosition() {
35-
return insertionPosition;
45+
public DelphiToken getInsertionToken() {
46+
return insertionToken;
3647
}
3748
}

delphi-frontend/src/main/java/au/com/integradev/delphi/file/DelphiFile.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import au.com.integradev.delphi.preprocessor.CompilerSwitchRegistry;
2929
import au.com.integradev.delphi.preprocessor.DelphiPreprocessor;
3030
import au.com.integradev.delphi.preprocessor.DelphiPreprocessorFactory;
31+
import au.com.integradev.delphi.preprocessor.PreprocessorException;
3132
import au.com.integradev.delphi.preprocessor.TextBlockLineEndingModeRegistry;
3233
import au.com.integradev.delphi.preprocessor.search.SearchPath;
3334
import au.com.integradev.delphi.utils.DelphiUtils;
@@ -157,6 +158,7 @@ private static void setupFile(
157158
| RecognitionException
158159
| LexerException
159160
| ParserException
161+
| PreprocessorException
160162
| EmptyDelphiFileException e) {
161163
throw new DelphiFileConstructionException(e);
162164
}

delphi-frontend/src/main/java/au/com/integradev/delphi/preprocessor/DelphiPreprocessor.java

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.common.base.Preconditions;
3636
import com.google.common.collect.Iterables;
3737
import java.io.IOException;
38+
import java.nio.file.Files;
3839
import java.nio.file.Path;
3940
import java.util.ArrayDeque;
4041
import java.util.ArrayList;
@@ -46,6 +47,7 @@
4647
import java.util.Set;
4748
import java.util.TreeSet;
4849
import java.util.stream.Collectors;
50+
import org.antlr.runtime.CharStream;
4951
import org.antlr.runtime.Token;
5052
import org.apache.commons.io.FilenameUtils;
5153
import org.slf4j.Logger;
@@ -229,43 +231,46 @@ private List<Token> processIncludeFile(String filename, Path includePath, Delphi
229231
Path includeFile = config.getSearchPath().search(filename, includePath);
230232

231233
if (includeFile != null) {
232-
String path = includeFile.toAbsolutePath().normalize().toString();
233-
234-
if (path.equals(lexer.getSourceName())) {
234+
Path lexerFile = Path.of(lexer.getSourceName());
235+
if (Files.isSameFile(includeFile, lexerFile)) {
235236
throw new SelfReferencingIncludeFileException(
236-
"Include file <" + includeFile.toAbsolutePath() + "> references itself");
237+
String.format("Include file '%s' references itself", includeFile.toAbsolutePath()),
238+
location);
237239
}
238240

239-
DelphiFileStream fileStream = new DelphiFileStream(path, config.getEncoding());
240-
DelphiLexer includeLexer = new DelphiLexer(fileStream);
241-
DelphiPreprocessor preprocessor =
242-
new DelphiPreprocessor(
243-
includeLexer,
244-
config,
245-
platform,
246-
definitions,
247-
currentSwitches,
248-
switchRegistry,
249-
textBlockLineEndingModeRegistry,
250-
location.getIndex(),
251-
true);
252-
253-
preprocessor.process();
254-
255-
List<Token> includeTokens = preprocessor.getTokenStream().getTokens();
256-
return includeTokens.stream()
257-
.limit(includeTokens.size() - 1L)
258-
.map(token -> new IncludeToken(token, location))
259-
.collect(Collectors.toList());
241+
List<Token> result = preprocessIncludeFile(location, includeFile);
242+
result.remove(result.size() - 1); // Remove EOF token
243+
return result;
260244
}
261-
} catch (IOException | RuntimeException e) {
245+
} catch (IOException e) {
262246
LOG.debug("Error occurred while resolving includes: ", e);
263247
}
264248

265249
LOG.warn("Failed to resolve include '{}'.", filename);
266250
return Collections.emptyList();
267251
}
268252

253+
private List<Token> preprocessIncludeFile(DelphiToken location, Path path) throws IOException {
254+
var fileStream = new DelphiFileStream(path.toAbsolutePath().toString(), config.getEncoding());
255+
DelphiLexer includeLexer = new DelphiIncludeLexer(fileStream, location);
256+
257+
DelphiPreprocessor preprocessor =
258+
new DelphiPreprocessor(
259+
includeLexer,
260+
config,
261+
platform,
262+
definitions,
263+
currentSwitches,
264+
switchRegistry,
265+
textBlockLineEndingModeRegistry,
266+
location.getIndex(),
267+
true);
268+
269+
preprocessor.process();
270+
271+
return preprocessor.getTokenStream().getTokens();
272+
}
273+
269274
private void addBranchingDirective(BranchingDirective directive) {
270275
if (!parentDirective.isEmpty()) {
271276
parentDirective.peek().addDirective(directive);
@@ -363,9 +368,43 @@ public List<DelphiToken> getRawTokens() {
363368
.collect(Collectors.toUnmodifiableList());
364369
}
365370

366-
static class SelfReferencingIncludeFileException extends RuntimeException {
367-
SelfReferencingIncludeFileException(String message) {
368-
super(message);
371+
static class SelfReferencingIncludeFileException extends PreprocessorException {
372+
SelfReferencingIncludeFileException(String message, DelphiToken token) {
373+
super(message, token);
374+
}
375+
}
376+
377+
private static class DelphiIncludeLexer extends DelphiLexer {
378+
private final DelphiToken insertionToken;
379+
380+
public DelphiIncludeLexer(CharStream input, DelphiToken insertionToken) {
381+
super(input);
382+
this.insertionToken = insertionToken;
383+
}
384+
385+
@Override
386+
public Token emit() {
387+
Token token =
388+
new IncludeToken(
389+
input,
390+
state.type,
391+
state.channel,
392+
state.tokenStartCharIndex,
393+
getCharIndex() - 1,
394+
insertionToken);
395+
token.setLine(state.tokenStartLine);
396+
token.setText(state.text);
397+
token.setCharPositionInLine(state.tokenStartCharPositionInLine);
398+
emit(token);
399+
return token;
400+
}
401+
402+
@Override
403+
protected void throwLexerException(String message, int line, Throwable cause) {
404+
super.throwLexerException(
405+
String.format("included on line %d :: %s", insertionToken.getBeginLine(), message),
406+
insertionToken.getBeginLine(),
407+
cause);
369408
}
370409
}
371410
}

0 commit comments

Comments
 (0)