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.
This commit is contained in:
Xi Ge
2018-01-16 16:14:57 -08:00
committed by GitHub
parent ce8fb6272d
commit 71af76a87e
8 changed files with 75 additions and 10 deletions

View File

@@ -1553,6 +1553,12 @@ ERROR(pound_available_swift_not_allowed, none,
ERROR(availability_query_repeated_platform, none, ERROR(availability_query_repeated_platform, none,
"version for '%0' already specified", (StringRef)) "version for '%0' already specified", (StringRef))
//------------------------------------------------------------------------------
// syntax parsing diagnostics
//------------------------------------------------------------------------------
WARNING(unknown_syntax_entity, PointsToFirstBadToken,
"unknown %0 syntax exists in the source", (StringRef))
#ifndef DIAG_NO_UNDEF #ifndef DIAG_NO_UNDEF
# if defined(DIAG) # if defined(DIAG)
# undef DIAG # undef DIAG

View File

@@ -256,6 +256,9 @@ namespace swift {
/// this source file. /// this source file.
bool KeepSyntaxInfoInSourceFile = false; bool KeepSyntaxInfoInSourceFile = false;
/// Whether to verify the parsed syntax tree and emit related diagnostics.
bool VerifySyntaxTree = false;
/// Sets the target we are building for and updates platform conditions /// Sets the target we are building for and updates platform conditions
/// to match. /// to match.
/// ///

View File

@@ -73,10 +73,18 @@ struct alignas(1 << SyntaxAlignInBits) RootContextData {
// Where to issue diagnostics. // Where to issue diagnostics.
DiagnosticEngine &Diags; DiagnosticEngine &Diags;
SourceManager &SourceMgr;
unsigned BufferID;
// Storage for Collected parts. // Storage for Collected parts.
std::vector<RC<RawSyntax>> Storage; std::vector<RC<RawSyntax>> Storage;
RootContextData(SourceFile &SF, DiagnosticEngine &Diags): SF(SF), Diags(Diags) {} RootContextData(SourceFile &SF,
DiagnosticEngine &Diags,
SourceManager &SourceMgr,
unsigned BufferID): SF(SF), Diags(Diags),
SourceMgr(SourceMgr), BufferID(BufferID) {}
}; };
/// RAII object which receive RawSyntax parts. On destruction, this constructs /// RAII object which receive RawSyntax parts. On destruction, this constructs
@@ -133,7 +141,7 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext {
public: public:
/// Construct root context. /// Construct root context.
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF, SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF,
DiagnosticEngine &Diags); DiagnosticEngine &Diags, SourceManager &SourceMgr, unsigned BufferID);
/// Designated constructor for child context. /// Designated constructor for child context.
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder) SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder)

View File

@@ -515,8 +515,10 @@ static bool performCompile(CompilerInstance &Instance,
FrontendOptions opts = Invocation.getFrontendOptions(); FrontendOptions opts = Invocation.getFrontendOptions();
FrontendOptions::ActionType Action = opts.RequestedAction; FrontendOptions::ActionType Action = opts.RequestedAction;
if (Action == FrontendOptions::ActionType::EmitSyntax) if (Action == FrontendOptions::ActionType::EmitSyntax) {
Instance.getASTContext().LangOpts.KeepSyntaxInfoInSourceFile = true; Instance.getASTContext().LangOpts.KeepSyntaxInfoInSourceFile = true;
Instance.getASTContext().LangOpts.VerifySyntaxTree = true;
}
// We've been asked to precompile a bridging header; we want to // We've been asked to precompile a bridging header; we want to
// avoid touching any other inputs and just parse, emit and exit. // avoid touching any other inputs and just parse, emit and exit.

View File

@@ -467,7 +467,8 @@ Parser::Parser(std::unique_ptr<Lexer> Lex, SourceFile &SF,
TokReceiver(SF.shouldKeepSyntaxInfo() ? TokReceiver(SF.shouldKeepSyntaxInfo() ?
new TokenRecorder(SF) : new TokenRecorder(SF) :
new ConsumeTokenReceiver()), new ConsumeTokenReceiver()),
SyntaxContext(new SyntaxParsingContext(SyntaxContext, SF, Diags)) { SyntaxContext(new SyntaxParsingContext(SyntaxContext, SF, Diags, SourceMgr,
L->getBufferID())) {
State = PersistentState; State = PersistentState;
if (!State) { if (!State) {
OwnedState.reset(new PersistentParserState()); OwnedState.reset(new PersistentParserState());

View File

@@ -20,6 +20,7 @@
#include "swift/Syntax/References.h" #include "swift/Syntax/References.h"
#include "swift/Syntax/Syntax.h" #include "swift/Syntax/Syntax.h"
#include "swift/Syntax/SyntaxFactory.h" #include "swift/Syntax/SyntaxFactory.h"
#include "swift/Syntax/SyntaxVisitor.h"
#include "swift/Syntax/TokenKinds.h" #include "swift/Syntax/TokenKinds.h"
#include "swift/Syntax/TokenSyntax.h" #include "swift/Syntax/TokenSyntax.h"
#include "swift/Syntax/Trivia.h" #include "swift/Syntax/Trivia.h"
@@ -52,10 +53,12 @@ RC<RawSyntax> createSyntaxAs(SyntaxKind Kind, ArrayRef<RC<RawSyntax>> Parts) {
SyntaxParsingContext::SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SyntaxParsingContext::SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder,
SourceFile &SF, SourceFile &SF,
DiagnosticEngine &Diags) DiagnosticEngine &Diags,
: RootDataOrParent(new RootContextData(SF, Diags)), CtxtHolder(CtxtHolder), SourceManager &SourceMgr,
Storage(getRootData().Storage), Offset(0), Mode(AccumulationMode::Root), unsigned BufferID)
Enabled(SF.shouldKeepSyntaxInfo()) { : RootDataOrParent(new RootContextData(SF, Diags, SourceMgr, BufferID)),
CtxtHolder(CtxtHolder), Storage(getRootData().Storage), Offset(0),
Mode(AccumulationMode::Root), Enabled(SF.shouldKeepSyntaxInfo()) {
CtxtHolder = this; CtxtHolder = this;
} }
@@ -214,7 +217,37 @@ RC<RawSyntax> bridgeAs(SyntaxContextKind Kind, ArrayRef<RC<RawSyntax>> Parts) {
} }
} }
void finalizeSourceFile(SourceFile &SF, ArrayRef<RC<RawSyntax>> Parts) { /// This verifier traverses a syntax node to emit proper diagnostics.
class SyntaxVerifier: public SyntaxVisitor {
SourceFileSyntax Root;
RootContextData &RootData;
template<class T>
SourceLoc getSourceLoc(T Node) {
return RootData.SourceMgr.getLocForOffset(RootData.BufferID,
Node.getAbsolutePosition(Root).getOffset());
}
public:
SyntaxVerifier(SourceFileSyntax Root, RootContextData &RootData) :
Root(Root), RootData(RootData) {}
void visit(UnknownDeclSyntax Node) override {
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
"declaration");
visitChildren(Node);
}
void visit(UnknownExprSyntax Node) override {
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
"expression");
visitChildren(Node);
}
void verify(Syntax Node) {
Node.accept(*this);
}
};
void finalizeSourceFile(RootContextData &RootData,
ArrayRef<RC<RawSyntax>> Parts) {
SourceFile &SF = RootData.SF;
std::vector<DeclSyntax> AllTopLevel; std::vector<DeclSyntax> AllTopLevel;
llvm::Optional<TokenSyntax> EOFToken; llvm::Optional<TokenSyntax> EOFToken;
@@ -242,6 +275,14 @@ void finalizeSourceFile(SourceFile &SF, ArrayRef<RC<RawSyntax>> Parts) {
SyntaxFactory::makeDeclList(AllTopLevel), SyntaxFactory::makeDeclList(AllTopLevel),
EOFToken.hasValue() ? *EOFToken EOFToken.hasValue() ? *EOFToken
: TokenSyntax::missingToken(tok::eof, ""))); : TokenSyntax::missingToken(tok::eof, "")));
if (SF.getASTContext().LangOpts.VerifySyntaxTree) {
// Verify the added nodes if specified.
SyntaxVerifier Verifier(SF.getSyntaxRoot(), RootData);
for (auto RawNode: Parts) {
Verifier.verify(make<Syntax>(RawNode));
}
}
} }
} // End of anonymous namespace } // End of anonymous namespace
@@ -294,7 +335,7 @@ SyntaxParsingContext::~SyntaxParsingContext() {
// Accumulate parsed toplevel syntax onto the SourceFile. // Accumulate parsed toplevel syntax onto the SourceFile.
case AccumulationMode::Root: case AccumulationMode::Root:
assert(isRoot() && "AccumulationMode::Root is only for root context"); assert(isRoot() && "AccumulationMode::Root is only for root context");
finalizeSourceFile(getRootData().SF, getParts()); finalizeSourceFile(getRootData(), getParts());
break; break;
// Never. // Never.

View File

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

View File

@@ -145,6 +145,7 @@ SourceFile *getSourceFile(CompilerInstance &Instance,
const char *MainExecutablePath) { const char *MainExecutablePath) {
CompilerInvocation Invocation; CompilerInvocation Invocation;
Invocation.getLangOptions().KeepSyntaxInfoInSourceFile = true; Invocation.getLangOptions().KeepSyntaxInfoInSourceFile = true;
Invocation.getLangOptions().VerifySyntaxTree = true;
Invocation.getFrontendOptions().Inputs.addInputFile(InputFileName); Invocation.getFrontendOptions().Inputs.addInputFile(InputFileName);
Invocation.setMainExecutablePath( Invocation.setMainExecutablePath(
llvm::sys::fs::getMainExecutable(MainExecutablePath, llvm::sys::fs::getMainExecutable(MainExecutablePath,