Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement #warning and #error #14048

Merged
merged 8 commits into from Feb 3, 2018
@@ -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
@@ -255,6 +255,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:
@@ -3322,6 +3323,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;
ProTip! Use n and p to navigate between commits in a pull request.