mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
3
test/Syntax/syntax_diagnostics.swift
Normal file
3
test/Syntax/syntax_diagnostics.swift
Normal 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}}
|
||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user