Skip to content

Commit 71af76a

Browse files
authored
libSyntax: optionally emit diagnostics for unknown expressions and declarations. (#13973)
With more syntax nodes being specialized, we'd like this straight-forward way to pinpoint unknown entities. This diagnostics is only issued in -emit-syntax frontend action and swift-syntax-test invocation.
1 parent ce8fb62 commit 71af76a

File tree

8 files changed

+75
-10
lines changed

8 files changed

+75
-10
lines changed

include/swift/AST/DiagnosticsParse.def

+6
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,12 @@ ERROR(pound_available_swift_not_allowed, none,
15531553
ERROR(availability_query_repeated_platform, none,
15541554
"version for '%0' already specified", (StringRef))
15551555

1556+
//------------------------------------------------------------------------------
1557+
// syntax parsing diagnostics
1558+
//------------------------------------------------------------------------------
1559+
WARNING(unknown_syntax_entity, PointsToFirstBadToken,
1560+
"unknown %0 syntax exists in the source", (StringRef))
1561+
15561562
#ifndef DIAG_NO_UNDEF
15571563
# if defined(DIAG)
15581564
# undef DIAG

include/swift/Basic/LangOptions.h

+3
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ namespace swift {
256256
/// this source file.
257257
bool KeepSyntaxInfoInSourceFile = false;
258258

259+
/// Whether to verify the parsed syntax tree and emit related diagnostics.
260+
bool VerifySyntaxTree = false;
261+
259262
/// Sets the target we are building for and updates platform conditions
260263
/// to match.
261264
///

include/swift/Syntax/SyntaxParsingContext.h

+10-2
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,18 @@ struct alignas(1 << SyntaxAlignInBits) RootContextData {
7373
// Where to issue diagnostics.
7474
DiagnosticEngine &Diags;
7575

76+
SourceManager &SourceMgr;
77+
78+
unsigned BufferID;
79+
7680
// Storage for Collected parts.
7781
std::vector<RC<RawSyntax>> Storage;
7882

79-
RootContextData(SourceFile &SF, DiagnosticEngine &Diags): SF(SF), Diags(Diags) {}
83+
RootContextData(SourceFile &SF,
84+
DiagnosticEngine &Diags,
85+
SourceManager &SourceMgr,
86+
unsigned BufferID): SF(SF), Diags(Diags),
87+
SourceMgr(SourceMgr), BufferID(BufferID) {}
8088
};
8189

8290
/// RAII object which receive RawSyntax parts. On destruction, this constructs
@@ -133,7 +141,7 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext {
133141
public:
134142
/// Construct root context.
135143
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF,
136-
DiagnosticEngine &Diags);
144+
DiagnosticEngine &Diags, SourceManager &SourceMgr, unsigned BufferID);
137145

138146
/// Designated constructor for child context.
139147
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder)

lib/FrontendTool/FrontendTool.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,10 @@ static bool performCompile(CompilerInstance &Instance,
515515
FrontendOptions opts = Invocation.getFrontendOptions();
516516
FrontendOptions::ActionType Action = opts.RequestedAction;
517517

518-
if (Action == FrontendOptions::ActionType::EmitSyntax)
518+
if (Action == FrontendOptions::ActionType::EmitSyntax) {
519519
Instance.getASTContext().LangOpts.KeepSyntaxInfoInSourceFile = true;
520+
Instance.getASTContext().LangOpts.VerifySyntaxTree = true;
521+
}
520522

521523
// We've been asked to precompile a bridging header; we want to
522524
// avoid touching any other inputs and just parse, emit and exit.

lib/Parse/Parser.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,8 @@ Parser::Parser(std::unique_ptr<Lexer> Lex, SourceFile &SF,
467467
TokReceiver(SF.shouldKeepSyntaxInfo() ?
468468
new TokenRecorder(SF) :
469469
new ConsumeTokenReceiver()),
470-
SyntaxContext(new SyntaxParsingContext(SyntaxContext, SF, Diags)) {
470+
SyntaxContext(new SyntaxParsingContext(SyntaxContext, SF, Diags, SourceMgr,
471+
L->getBufferID())) {
471472
State = PersistentState;
472473
if (!State) {
473474
OwnedState.reset(new PersistentParserState());

lib/Syntax/SyntaxParsingContext.cpp

+47-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/Syntax/References.h"
2121
#include "swift/Syntax/Syntax.h"
2222
#include "swift/Syntax/SyntaxFactory.h"
23+
#include "swift/Syntax/SyntaxVisitor.h"
2324
#include "swift/Syntax/TokenKinds.h"
2425
#include "swift/Syntax/TokenSyntax.h"
2526
#include "swift/Syntax/Trivia.h"
@@ -52,10 +53,12 @@ RC<RawSyntax> createSyntaxAs(SyntaxKind Kind, ArrayRef<RC<RawSyntax>> Parts) {
5253

5354
SyntaxParsingContext::SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder,
5455
SourceFile &SF,
55-
DiagnosticEngine &Diags)
56-
: RootDataOrParent(new RootContextData(SF, Diags)), CtxtHolder(CtxtHolder),
57-
Storage(getRootData().Storage), Offset(0), Mode(AccumulationMode::Root),
58-
Enabled(SF.shouldKeepSyntaxInfo()) {
56+
DiagnosticEngine &Diags,
57+
SourceManager &SourceMgr,
58+
unsigned BufferID)
59+
: RootDataOrParent(new RootContextData(SF, Diags, SourceMgr, BufferID)),
60+
CtxtHolder(CtxtHolder), Storage(getRootData().Storage), Offset(0),
61+
Mode(AccumulationMode::Root), Enabled(SF.shouldKeepSyntaxInfo()) {
5962
CtxtHolder = this;
6063
}
6164

@@ -214,7 +217,37 @@ RC<RawSyntax> bridgeAs(SyntaxContextKind Kind, ArrayRef<RC<RawSyntax>> Parts) {
214217
}
215218
}
216219

217-
void finalizeSourceFile(SourceFile &SF, ArrayRef<RC<RawSyntax>> Parts) {
220+
/// This verifier traverses a syntax node to emit proper diagnostics.
221+
class SyntaxVerifier: public SyntaxVisitor {
222+
SourceFileSyntax Root;
223+
RootContextData &RootData;
224+
template<class T>
225+
SourceLoc getSourceLoc(T Node) {
226+
return RootData.SourceMgr.getLocForOffset(RootData.BufferID,
227+
Node.getAbsolutePosition(Root).getOffset());
228+
}
229+
public:
230+
SyntaxVerifier(SourceFileSyntax Root, RootContextData &RootData) :
231+
Root(Root), RootData(RootData) {}
232+
void visit(UnknownDeclSyntax Node) override {
233+
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
234+
"declaration");
235+
visitChildren(Node);
236+
}
237+
void visit(UnknownExprSyntax Node) override {
238+
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
239+
"expression");
240+
visitChildren(Node);
241+
}
242+
243+
void verify(Syntax Node) {
244+
Node.accept(*this);
245+
}
246+
};
247+
248+
void finalizeSourceFile(RootContextData &RootData,
249+
ArrayRef<RC<RawSyntax>> Parts) {
250+
SourceFile &SF = RootData.SF;
218251
std::vector<DeclSyntax> AllTopLevel;
219252
llvm::Optional<TokenSyntax> EOFToken;
220253

@@ -242,6 +275,14 @@ void finalizeSourceFile(SourceFile &SF, ArrayRef<RC<RawSyntax>> Parts) {
242275
SyntaxFactory::makeDeclList(AllTopLevel),
243276
EOFToken.hasValue() ? *EOFToken
244277
: TokenSyntax::missingToken(tok::eof, "")));
278+
279+
if (SF.getASTContext().LangOpts.VerifySyntaxTree) {
280+
// Verify the added nodes if specified.
281+
SyntaxVerifier Verifier(SF.getSyntaxRoot(), RootData);
282+
for (auto RawNode: Parts) {
283+
Verifier.verify(make<Syntax>(RawNode));
284+
}
285+
}
245286
}
246287
} // End of anonymous namespace
247288

@@ -294,7 +335,7 @@ SyntaxParsingContext::~SyntaxParsingContext() {
294335
// Accumulate parsed toplevel syntax onto the SourceFile.
295336
case AccumulationMode::Root:
296337
assert(isRoot() && "AccumulationMode::Root is only for root context");
297-
finalizeSourceFile(getRootData().SF, getParts());
338+
finalizeSourceFile(getRootData(), getParts());
298339
break;
299340

300341
// Never.

test/Syntax/syntax_diagnostics.swift

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// RUN: %target-swift-frontend -emit-syntax -primary-file %s -verify
2+
3+
typealias Inner: Foo // expected-warning{{unknown declaration syntax exists in the source}} expected-error{{expected '=' in type alias declaration}}

tools/swift-syntax-test/swift-syntax-test.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ SourceFile *getSourceFile(CompilerInstance &Instance,
145145
const char *MainExecutablePath) {
146146
CompilerInvocation Invocation;
147147
Invocation.getLangOptions().KeepSyntaxInfoInSourceFile = true;
148+
Invocation.getLangOptions().VerifySyntaxTree = true;
148149
Invocation.getFrontendOptions().Inputs.addInputFile(InputFileName);
149150
Invocation.setMainExecutablePath(
150151
llvm::sys::fs::getMainExecutable(MainExecutablePath,

0 commit comments

Comments
 (0)