Permalink
Browse files

Implement #warning and #error (#14048)

* Implement #warning and #error

* Fix #warning/#error in switch statements

* Fix AST printing for #warning/#error

* Add to test case

* Add extra handling to ParseDeclPoundDiagnostic

* fix dumping

* Consume the right paren even in the failure case

* Diagnose extra tokens on the same line after a diagnostic directive
  • Loading branch information...
harlanhaskins committed Feb 3, 2018
1 parent ca7d186 commit 5e02d2a877a8028c74b4221f226d2c694cd00987
@@ -295,6 +295,7 @@ class DeclAttribute : public AttributeBase {
// Cannot have any attributes.
OnMissingMember = 0,
OnPoundDiagnostic = 0,
// More coarse-grained aggregations for use in Attr.def.
OnOperator = OnInfixOperator|OnPrefixOperator|OnPostfixOperator,
@@ -23,6 +23,7 @@
#include "swift/AST/ClangNode.h"
#include "swift/AST/ConcreteDeclRef.h"
#include "swift/AST/DefaultArgumentKind.h"
#include "swift/AST/DiagnosticConsumer.h"
#include "swift/AST/GenericParamKey.h"
#include "swift/AST/IfConfigClause.h"
#include "swift/AST/LayoutConstraint.h"
@@ -107,6 +108,7 @@ enum class DescriptiveDeclKind : uint8_t {
EnumCase,
TopLevelCode,
IfConfig,
PoundDiagnostic,
PatternBinding,
Var,
Param,
@@ -591,6 +593,14 @@ class alignas(1 << DeclAlignInBits) Decl {
HadMissingEnd : 1
);
SWIFT_INLINE_BITFIELD(PoundDiagnosticDecl, Decl, 1+1,
/// `true` if the diagnostic is an error, `false` if it's a warning.
IsError : 1,
/// Whether this diagnostic has already been emitted.
HasBeenEmitted : 1
);
SWIFT_INLINE_BITFIELD(MissingMemberDecl, Decl, 1+2,
NumberOfFieldOffsetVectorEntries : 1,
NumberOfVTableEntries : 2
@@ -2059,6 +2069,52 @@ class IfConfigDecl : public Decl {
}
};
class StringLiteralExpr;
class PoundDiagnosticDecl : public Decl {
SourceLoc StartLoc;
SourceLoc EndLoc;
StringLiteralExpr *Message;
public:
PoundDiagnosticDecl(DeclContext *Parent, bool IsError, SourceLoc StartLoc,
SourceLoc EndLoc, StringLiteralExpr *Message)
: Decl(DeclKind::PoundDiagnostic, Parent), StartLoc(StartLoc),
EndLoc(EndLoc), Message(Message) {
Bits.PoundDiagnosticDecl.IsError = IsError;
Bits.PoundDiagnosticDecl.HasBeenEmitted = false;
}
DiagnosticKind getKind() {
return isError() ? DiagnosticKind::Error : DiagnosticKind::Warning;
}
StringLiteralExpr *getMessage() { return Message; }
bool isError() {
return Bits.PoundDiagnosticDecl.IsError;
}
bool hasBeenEmitted() {
return Bits.PoundDiagnosticDecl.HasBeenEmitted;
}
void markEmitted() {
Bits.PoundDiagnosticDecl.HasBeenEmitted = true;
}
SourceLoc getEndLoc() const { return EndLoc; };
SourceLoc getLoc() const { return StartLoc; }
SourceRange getSourceRange() const {
return SourceRange(StartLoc, EndLoc);
}
static bool classof(const Decl *D) {
return D->getKind() == DeclKind::PoundDiagnostic;
}
};
/// ValueDecl - All named decls that are values in the language. These can
/// have a type, etc.
class ValueDecl : public Decl {
@@ -171,6 +171,7 @@ ITERABLE_GENERIC_DECL(Extension, Decl)
CONTEXT_DECL(TopLevelCode, Decl)
DECL(Import, Decl)
DECL(IfConfig, Decl)
DECL(PoundDiagnostic, Decl)
DECL(PrecedenceGroup, Decl)
DECL(MissingMember, Decl)
DECL(PatternBinding, Decl)
@@ -68,6 +68,17 @@ ERROR(extra_tokens_conditional_compilation_directive,none,
ERROR(unexpected_rbrace_in_conditional_compilation_block,none,
"unexpected '}' in conditional compilation block", ())
ERROR(pound_diagnostic_expected_string,none,
"expected string literal in %select{#warning|#error}0 directive",(bool))
ERROR(pound_diagnostic_expected,none,
"expected '%0' in %select{#warning|#error}1 directive",(StringRef,bool))
ERROR(pound_diagnostic_expected_parens,none,
"%select{#warning|#error}0 directive requires parentheses",(bool))
ERROR(pound_diagnostic_interpolation,none,
"string interpolation is not allowed in %select{#warning|#error}0 directives",(bool))
ERROR(extra_tokens_pound_diagnostic_directive,none,
"extra tokens following %select{#warning|#error}0 directive", (bool))
ERROR(sourceLocation_expected,none,
"expected '%0' in #sourceLocation directive", (StringRef))
@@ -1007,6 +1007,9 @@ ERROR(enum_element_not_materializable,none,
ERROR(missing_initializer_def,PointsToFirstBadToken,
"initializer requires a body", ())
WARNING(pound_warning, none, "%0", (StringRef))
ERROR(pound_error, none, "%0", (StringRef))
// Attributes
ERROR(operator_not_func,none,
@@ -955,8 +955,8 @@ class SwitchStmt final : public LabeledStmt,
}
private:
struct AsCaseStmtWithSkippingIfConfig {
AsCaseStmtWithSkippingIfConfig() {}
struct AsCaseStmtWithSkippingNonCaseStmts {
AsCaseStmtWithSkippingNonCaseStmts() {}
Optional<CaseStmt*> operator()(const ASTNode &N) const {
if (auto *CS = llvm::dyn_cast_or_null<CaseStmt>(N.dyn_cast<Stmt*>()))
return CS;
@@ -966,11 +966,11 @@ class SwitchStmt final : public LabeledStmt,
public:
using AsCaseStmtRange = OptionalTransformRange<ArrayRef<ASTNode>,
AsCaseStmtWithSkippingIfConfig>;
AsCaseStmtWithSkippingNonCaseStmts>;
/// Get the list of case clauses.
AsCaseStmtRange getCases() const {
return AsCaseStmtRange(getRawCases(), AsCaseStmtWithSkippingIfConfig());
return AsCaseStmtRange(getRawCases(), AsCaseStmtWithSkippingNonCaseStmts());
}
static bool classof(const Stmt *S) {
@@ -48,6 +48,11 @@ class TypeMemberVisitor : public DeclVisitor<ImplClass, RetTy> {
return RetTy();
}
// These decls are disregarded.
RetTy visitPoundDiagnosticDecl(PoundDiagnosticDecl *D) {
return RetTy();
}
/// A convenience method to visit all the members.
void visitMembers(NominalTypeDecl *D) {
for (Decl *member : D->getMembers()) {
@@ -755,6 +755,9 @@ class Parser {
ParserResult<IfConfigDecl> parseIfConfig(
llvm::function_ref<void(SmallVectorImpl<ASTNode> &, bool)> parseElements);
/// Parse a #error or #warning diagnostic.
ParserResult<PoundDiagnosticDecl> parseDeclPoundDiagnostic();
/// Parse a #line/#sourceLocation directive.
/// 'isLine = true' indicates parsing #line instead of #sourcelocation
ParserStatus parseLineDirective(bool isLine = false);
@@ -168,6 +168,10 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
// We only care about the active members, which were already subsumed by the
// enclosing type.
}
void visitPoundDiagnosticDecl(PoundDiagnosticDecl *pdd) {
// We don't care about diagnostics at this stage.
}
};
} // end namespace swift
@@ -259,6 +259,8 @@ POUND_KEYWORD(keyPath)
POUND_KEYWORD(line)
POUND_KEYWORD(sourceLocation)
POUND_KEYWORD(selector)
POUND_KEYWORD(warning)
POUND_KEYWORD(error)
// Keywords prefixed with a '#' that are build configurations.
POUND_CONFIG(available)
@@ -1098,6 +1098,16 @@ namespace {
PrintWithColorRAII(OS, ParenthesisColor) << ')';
}
void visitPoundDiagnosticDecl(PoundDiagnosticDecl *PDD) {
printCommon(PDD, "pound_diagnostic_decl");
auto kind = PDD->isError() ? "error" : "warning";
OS << " kind=" << kind << "\n";
Indent += 2;
printRec(PDD->getMessage());
Indent -= 2;
PrintWithColorRAII(OS, ParenthesisColor) << ')';
}
void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD) {
printCommon(PGD, "precedence_group_decl ");
OS << PGD->getName() << "\n";
@@ -1917,6 +1917,17 @@ void PrintAST::visitIfConfigDecl(IfConfigDecl *ICD) {
Printer << tok::pound_endif;
}
void PrintAST::visitPoundDiagnosticDecl(PoundDiagnosticDecl *PDD) {
/// TODO: Should we even print #error/#warning?
if (PDD->isError()) {
Printer << tok::pound_error;
} else {
Printer << tok::pound_warning;
}
Printer << "(\"" << PDD->getMessage()->getValue() << "\")";
}
void PrintAST::visitTypeAliasDecl(TypeAliasDecl *decl) {
printDocumentationComment(decl);
printAttributes(decl);
@@ -881,6 +881,7 @@ ASTScope *ASTScope::createIfNeeded(const ASTScope *parent, Decl *decl) {
case DeclKind::Param:
case DeclKind::EnumElement:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
case DeclKind::MissingMember:
// These declarations do not introduce scopes.
return nullptr;
@@ -188,6 +188,11 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
return false;
}
bool visitPoundDiagnosticDecl(PoundDiagnosticDecl *PDD) {
// By default, ignore #error/#warning.
return false;
}
bool visitOperatorDecl(OperatorDecl *OD) {
return false;
}
@@ -1476,7 +1481,8 @@ Stmt *Traversal::visitSwitchStmt(SwitchStmt *S) {
} else
return nullptr;
} else {
assert(isa<IfConfigDecl>(N.get<Decl*>()));
assert(isa<IfConfigDecl>(N.get<Decl*>()) ||
isa<PoundDiagnosticDecl>(N.get<Decl*>()));
if (doIt(N.get<Decl*>()))
return nullptr;
}
@@ -125,6 +125,7 @@ DescriptiveDeclKind Decl::getDescriptiveKind() const {
TRIVIAL_KIND(EnumCase);
TRIVIAL_KIND(TopLevelCode);
TRIVIAL_KIND(IfConfig);
TRIVIAL_KIND(PoundDiagnostic);
TRIVIAL_KIND(PatternBinding);
TRIVIAL_KIND(PrecedenceGroup);
TRIVIAL_KIND(InfixOperator);
@@ -235,6 +236,7 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) {
ENTRY(EnumCase, "case");
ENTRY(TopLevelCode, "top-level code");
ENTRY(IfConfig, "conditional block");
ENTRY(PoundDiagnostic, "diagnostic");
ENTRY(PatternBinding, "pattern binding");
ENTRY(Var, "var");
ENTRY(Param, "parameter");
@@ -769,6 +771,7 @@ ImportKind ImportDecl::getBestImportKind(const ValueDecl *VD) {
case DeclKind::PostfixOperator:
case DeclKind::EnumCase:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
case DeclKind::PrecedenceGroup:
case DeclKind::MissingMember:
llvm_unreachable("not a ValueDecl");
@@ -1535,6 +1538,7 @@ bool ValueDecl::isDefinition() const {
case DeclKind::PrefixOperator:
case DeclKind::PostfixOperator:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
case DeclKind::PrecedenceGroup:
case DeclKind::MissingMember:
assert(!isa<ValueDecl>(this));
@@ -1578,6 +1582,7 @@ bool ValueDecl::isInstanceMember() const {
case DeclKind::PrefixOperator:
case DeclKind::PostfixOperator:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
case DeclKind::PrecedenceGroup:
case DeclKind::MissingMember:
llvm_unreachable("Not a ValueDecl");
@@ -404,7 +404,8 @@ SwitchStmt *SwitchStmt::create(LabeledStmtInfo LabelInfo, SourceLoc SwitchLoc,
#ifndef NDEBUG
for (auto N : Cases)
assert((N.is<Stmt*>() && isa<CaseStmt>(N.get<Stmt*>())) ||
(N.is<Decl*>() && isa<IfConfigDecl>(N.get<Decl*>())));
(N.is<Decl*>() && (isa<IfConfigDecl>(N.get<Decl*>()) ||
isa<PoundDiagnosticDecl>(N.get<Decl*>()))));
#endif
void *p = C.Allocate(totalSizeToAlloc<ASTNode>(Cases.size()),
@@ -69,6 +69,7 @@ static bool declIsPrivate(const Decl *member) {
case DeclKind::EnumCase:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
return true;
case DeclKind::Extension:
@@ -240,6 +241,7 @@ bool swift::emitReferenceDependencies(DiagnosticEngine &diags,
case DeclKind::PatternBinding:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
// No action necessary.
break;
@@ -548,6 +548,7 @@ CodeCompletionResult::getCodeCompletionDeclKind(const Decl *D) {
case DeclKind::EnumCase:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
case DeclKind::MissingMember:
llvm_unreachable("not expecting such a declaration result");
case DeclKind::Module:
@@ -1881,7 +1881,8 @@ void IRGenModule::emitGlobalDecl(Decl *D) {
case DeclKind::TypeAlias:
case DeclKind::GenericTypeParam:
case DeclKind::AssociatedType:
case DeclKind::IfConfig:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
return;
case DeclKind::Enum:
@@ -3345,6 +3346,7 @@ void IRGenModule::emitNestedTypeDecls(DeclRange members) {
llvm_unreachable("decl not allowed in type context");
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
continue;
case DeclKind::PatternBinding:
@@ -214,6 +214,7 @@ SymbolInfo index::getSymbolInfoForDecl(const Decl *D) {
case DeclKind::EnumCase:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
case DeclKind::MissingMember:
case DeclKind::Module:
break;
Oops, something went wrong.

0 comments on commit 5e02d2a

Please sign in to comment.