diff --git a/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h b/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
--- a/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
+++ b/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
@@ -122,6 +122,32 @@
     : AsmRewrites(rewrites) {}
 };
 
+/// Ternary parse status returned by various parse* methods.
+class ParseStatus {
+  enum class StatusTy { Success, Failure, NoMatch } Status;
+
+public:
+#if __cplusplus >= 202002L
+  using enum StatusTy;
+#else
+  static constexpr StatusTy Success = StatusTy::Success;
+  static constexpr StatusTy Failure = StatusTy::Failure;
+  static constexpr StatusTy NoMatch = StatusTy::NoMatch;
+#endif
+
+  constexpr ParseStatus() : Status(NoMatch) {}
+
+  constexpr ParseStatus(StatusTy Status) : Status(Status) {}
+
+  constexpr ParseStatus(bool Error) : Status(Error ? Failure : Success) {}
+
+  template <typename T> constexpr ParseStatus(T) = delete;
+
+  constexpr bool isSuccess() const { return Status == StatusTy::Success; }
+  constexpr bool isFailure() const { return Status == StatusTy::Failure; }
+  constexpr bool isNoMatch() const { return Status == StatusTy::NoMatch; }
+};
+
 enum OperandMatchResultTy {
   MatchOperand_Success,  // operand matched successfully
   MatchOperand_NoMatch,  // operand did not match
@@ -408,6 +434,7 @@
   }
 
   /// ParseDirective - Parse a target specific assembler directive
+  /// This method is deprecated, use 'parseDirective' instead.
   ///
   /// The parser is positioned following the directive name.  The target
   /// specific directive parser should parse the entire directive doing or
@@ -417,7 +444,19 @@
   /// end-of-statement token and false is returned.
   ///
   /// \param DirectiveID - the identifier token of the directive.
-  virtual bool ParseDirective(AsmToken DirectiveID) = 0;
+  virtual bool ParseDirective(AsmToken DirectiveID) { return true; }
+
+  /// Parses a target-specific assembler directive.
+  ///
+  /// The parser is positioned following the directive name. The target-specific
+  /// directive parser should parse the entire directive doing or recording any
+  /// target-specific work, or emit an error. On success, the entire line should
+  /// be parsed up to and including the end-of-statement token. On failure, the
+  /// parser is not required to read to the end of the line. If the directive is
+  /// not target-specific, no tokens should be consumed and NoMatch is returned.
+  ///
+  /// \param DirectiveID - The token identifying the directive.
+  virtual ParseStatus parseDirective(AsmToken DirectiveID);
 
   /// MatchAndEmitInstruction - Recognize a series of operands of a parsed
   /// instruction as an actual MCInst and emit it to the specified MCStreamer.
diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -1999,20 +1999,12 @@
 
     getTargetParser().flushPendingInstructions(getStreamer());
 
-    SMLoc StartTokLoc = getTok().getLoc();
-    bool TPDirectiveReturn = getTargetParser().ParseDirective(ID);
-
-    if (hasPendingError())
-      return true;
-    // Currently the return value should be true if we are
-    // uninterested but as this is at odds with the standard parsing
-    // convention (return true = error) we have instances of a parsed
-    // directive that fails returning true as an error. Catch these
-    // cases as best as possible errors here.
-    if (TPDirectiveReturn && StartTokLoc != getTok().getLoc())
+    ParseStatus TPDirectiveReturn = getTargetParser().parseDirective(ID);
+    assert(TPDirectiveReturn.isFailure() == hasPendingError() &&
+           "Should only return Failure iff there was an error");
+    if (TPDirectiveReturn.isFailure())
       return true;
-    // Return if we did some parsing or believe we succeeded.
-    if (!TPDirectiveReturn || StartTokLoc != getTok().getLoc())
+    if (TPDirectiveReturn.isSuccess())
       return false;
 
     // Next, check the extension directive map to see if any extension has
diff --git a/llvm/lib/MC/MCParser/MCTargetAsmParser.cpp b/llvm/lib/MC/MCParser/MCTargetAsmParser.cpp
--- a/llvm/lib/MC/MCParser/MCTargetAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/MCTargetAsmParser.cpp
@@ -27,3 +27,24 @@
 const MCSubtargetInfo &MCTargetAsmParser::getSTI() const {
   return *STI;
 }
+
+ParseStatus MCTargetAsmParser::parseDirective(AsmToken DirectiveID) {
+  SMLoc StartTokLoc = getTok().getLoc();
+  // Delegate to ParseDirective by default for transition period. Once the
+  // transition is over, this method should just return NoMatch.
+  bool Res = ParseDirective(DirectiveID);
+
+  // Some targets erroneously report success after emitting an error.
+  if (getParser().hasPendingError())
+    return ParseStatus::Failure;
+
+  // ParseDirective returns true if there was an error or if the directive is
+  // not target-specific. Disambiguate the two cases by comparing position of
+  // the lexer before and after calling the method: if no tokens were consumed,
+  // there was no match, otherwise there was a failure.
+  if (!Res)
+    return ParseStatus::Success;
+  if (getTok().getLoc() != StartTokLoc)
+    return ParseStatus::Failure;
+  return ParseStatus::NoMatch;
+}
diff --git a/llvm/lib/MC/MCParser/MasmParser.cpp b/llvm/lib/MC/MCParser/MasmParser.cpp
--- a/llvm/lib/MC/MCParser/MasmParser.cpp
+++ b/llvm/lib/MC/MCParser/MasmParser.cpp
@@ -2304,21 +2304,15 @@
       return (*Handler.second)(Handler.first, IDVal, IDLoc);
 
     // Next, let the target-specific assembly parser try.
-    SMLoc StartTokLoc = getTok().getLoc();
-    bool TPDirectiveReturn =
-        ID.is(AsmToken::Identifier) && getTargetParser().ParseDirective(ID);
+    if (ID.isNot(AsmToken::Identifier))
+      return false;
 
-    if (hasPendingError())
-      return true;
-    // Currently the return value should be true if we are
-    // uninterested but as this is at odds with the standard parsing
-    // convention (return true = error) we have instances of a parsed
-    // directive that fails returning true as an error. Catch these
-    // cases as best as possible errors here.
-    if (TPDirectiveReturn && StartTokLoc != getTok().getLoc())
+    ParseStatus TPDirectiveReturn = getTargetParser().parseDirective(ID);
+    assert(TPDirectiveReturn.isFailure() == hasPendingError() &&
+           "Should only return Failure iff there was an error");
+    if (TPDirectiveReturn.isFailure())
       return true;
-    // Return if we did some parsing or believe we succeeded.
-    if (!TPDirectiveReturn || StartTokLoc != getTok().getLoc())
+    if (TPDirectiveReturn.isSuccess())
       return false;
 
     // Finally, if no one else is interested in this directive, it must be
diff --git a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
--- a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
+++ b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
@@ -64,7 +64,7 @@
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
 
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
 
   OperandMatchResultTy parseMemriOperand(OperandVector &Operands);
 
@@ -90,7 +90,7 @@
                       uint64_t const &ErrorInfo);
   bool missingFeature(SMLoc const &Loc, uint64_t const &ErrorInfo);
 
-  bool parseLiteralValues(unsigned SizeInBytes, SMLoc L);
+  ParseStatus parseLiteralValues(unsigned SizeInBytes, SMLoc L);
 
 public:
   AVRAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
@@ -674,19 +674,18 @@
   return false;
 }
 
-bool AVRAsmParser::ParseDirective(llvm::AsmToken DirectiveID) {
+ParseStatus AVRAsmParser::parseDirective(llvm::AsmToken DirectiveID) {
   StringRef IDVal = DirectiveID.getIdentifier();
-  if (IDVal.lower() == ".long") {
-    parseLiteralValues(SIZE_LONG, DirectiveID.getLoc());
-  } else if (IDVal.lower() == ".word" || IDVal.lower() == ".short") {
-    parseLiteralValues(SIZE_WORD, DirectiveID.getLoc());
-  } else if (IDVal.lower() == ".byte") {
-    parseLiteralValues(1, DirectiveID.getLoc());
-  }
-  return true;
+  if (IDVal.lower() == ".long")
+    return parseLiteralValues(SIZE_LONG, DirectiveID.getLoc());
+  if (IDVal.lower() == ".word" || IDVal.lower() == ".short")
+    return parseLiteralValues(SIZE_WORD, DirectiveID.getLoc());
+  if (IDVal.lower() == ".byte")
+    return parseLiteralValues(1, DirectiveID.getLoc());
+  return ParseStatus::NoMatch;
 }
 
-bool AVRAsmParser::parseLiteralValues(unsigned SizeInBytes, SMLoc L) {
+ParseStatus AVRAsmParser::parseLiteralValues(unsigned SizeInBytes, SMLoc L) {
   MCAsmParser &Parser = getParser();
   AVRMCELFStreamer &AVRStreamer =
       static_cast<AVRMCELFStreamer &>(Parser.getStreamer());
@@ -698,7 +697,7 @@
     MCSymbol *Symbol = getContext().getOrCreateSymbol(".text");
     AVRStreamer.emitValueForModiferKind(Symbol, SizeInBytes, L,
                                         AVRMCExpr::VK_AVR_None);
-    return false;
+    return ParseStatus::NoMatch;
   }
 
   if (Parser.getTok().getKind() == AsmToken::Identifier &&
@@ -715,7 +714,10 @@
     MCSymbol *Symbol =
         getContext().getOrCreateSymbol(Parser.getTok().getString());
     AVRStreamer.emitValueForModiferKind(Symbol, SizeInBytes, L, ModifierKind);
-    return false;
+    Lex(); // Eat the symbol name.
+    if (parseToken(AsmToken::RParen))
+      return ParseStatus::Failure;
+    return parseEOL();
   }
 
   auto parseOne = [&]() -> bool {
diff --git a/llvm/lib/Target/BPF/AsmParser/BPFAsmParser.cpp b/llvm/lib/Target/BPF/AsmParser/BPFAsmParser.cpp
--- a/llvm/lib/Target/BPF/AsmParser/BPFAsmParser.cpp
+++ b/llvm/lib/Target/BPF/AsmParser/BPFAsmParser.cpp
@@ -47,7 +47,7 @@
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
 
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
 
   // "=" is used as assignment operator for assembly statment, so can't be used
   // for symbol assignment.
@@ -516,7 +516,9 @@
   return false;
 }
 
-bool BPFAsmParser::ParseDirective(AsmToken DirectiveID) { return true; }
+ParseStatus BPFAsmParser::parseDirective(AsmToken DirectiveID) {
+  return ParseStatus::NoMatch;
+}
 
 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeBPFAsmParser() {
   RegisterMCAsmParser<BPFAsmParser> X(getTheBPFTarget());
diff --git a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
--- a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
+++ b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
@@ -77,7 +77,7 @@
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
 
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
 
   // Helper to actually emit an instruction to the MCStreamer. Also, when
   // possible, compression of the instruction is performed.
@@ -1573,17 +1573,13 @@
   return MatchOperand_Success;
 }
 
-bool CSKYAsmParser::ParseDirective(AsmToken DirectiveID) {
-  // This returns false if this function recognizes the directive
-  // regardless of whether it is successfully handles or reports an
-  // error. Otherwise it returns true to give the generic parser a
-  // chance at recognizing it.
+ParseStatus CSKYAsmParser::parseDirective(AsmToken DirectiveID) {
   StringRef IDVal = DirectiveID.getString();
 
   if (IDVal == ".csky_attribute")
     return parseDirectiveAttribute();
 
-  return true;
+  return ParseStatus::NoMatch;
 }
 
 /// parseDirectiveAttribute
@@ -1597,10 +1593,8 @@
     StringRef Name = Parser.getTok().getIdentifier();
     std::optional<unsigned> Ret =
         ELFAttrs::attrTypeFromString(Name, CSKYAttrs::getCSKYAttributeTags());
-    if (!Ret) {
-      Error(TagLoc, "attribute name not recognised: " + Name);
-      return false;
-    }
+    if (!Ret)
+      return Error(TagLoc, "attribute name not recognised: " + Name);
     Tag = *Ret;
     Parser.Lex();
   } else {
@@ -1611,8 +1605,8 @@
       return true;
 
     const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(AttrExpr);
-    if (check(!CE, TagLoc, "expected numeric constant"))
-      return true;
+    if (!CE)
+      return Error(TagLoc, "expected numeric constant");
 
     Tag = CE->getValue();
   }
diff --git a/llvm/lib/Target/Lanai/AsmParser/LanaiAsmParser.cpp b/llvm/lib/Target/Lanai/AsmParser/LanaiAsmParser.cpp
--- a/llvm/lib/Target/Lanai/AsmParser/LanaiAsmParser.cpp
+++ b/llvm/lib/Target/Lanai/AsmParser/LanaiAsmParser.cpp
@@ -62,7 +62,7 @@
 
   bool parsePrePost(StringRef Type, int *OffsetValue);
 
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
 
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
@@ -649,7 +649,9 @@
 
 } // end anonymous namespace
 
-bool LanaiAsmParser::ParseDirective(AsmToken /*DirectiveId*/) { return true; }
+ParseStatus LanaiAsmParser::parseDirective(AsmToken DirectiveID) {
+  return ParseStatus::NoMatch;
+}
 
 bool LanaiAsmParser::MatchAndEmitInstruction(SMLoc IdLoc, unsigned &Opcode,
                                              OperandVector &Operands,
diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -51,7 +51,9 @@
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
 
-  bool ParseDirective(AsmToken DirectiveID) override { return true; }
+  ParseStatus parseDirective(AsmToken DirectiveID) override {
+    return ParseStatus::NoMatch;
+  }
 
   bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                                OperandVector &Operands, MCStreamer &Out,
diff --git a/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp b/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp
--- a/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp
+++ b/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp
@@ -72,7 +72,7 @@
                                         SMLoc &EndLoc) override;
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
   bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                                OperandVector &Operands, MCStreamer &Out,
                                uint64_t &ErrorInfo,
@@ -992,7 +992,9 @@
   return false;
 }
 
-bool M68kAsmParser::ParseDirective(AsmToken DirectiveID) { return true; }
+ParseStatus M68kAsmParser::parseDirective(AsmToken DirectiveID) {
+  return ParseStatus::NoMatch;
+}
 
 bool M68kAsmParser::invalidOperand(SMLoc const &Loc,
                                    OperandVector const &Operands,
diff --git a/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp b/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp
--- a/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp
+++ b/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp
@@ -53,7 +53,7 @@
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
 
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
   bool ParseDirectiveRefSym(AsmToken DirectiveID);
 
   unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
@@ -424,27 +424,26 @@
 }
 
 bool MSP430AsmParser::ParseDirectiveRefSym(AsmToken DirectiveID) {
-    StringRef Name;
-    if (getParser().parseIdentifier(Name))
-      return TokError("expected identifier in directive");
+  StringRef Name;
+  if (getParser().parseIdentifier(Name))
+    return TokError("expected identifier in directive");
 
-    MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
-    getStreamer().emitSymbolAttribute(Sym, MCSA_Global);
-    return false;
+  MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
+  getStreamer().emitSymbolAttribute(Sym, MCSA_Global);
+  return parseEOL();
 }
 
-bool MSP430AsmParser::ParseDirective(AsmToken DirectiveID) {
+ParseStatus MSP430AsmParser::parseDirective(AsmToken DirectiveID) {
   StringRef IDVal = DirectiveID.getIdentifier();
-  if (IDVal.lower() == ".long") {
-    ParseLiteralValues(4, DirectiveID.getLoc());
-  } else if (IDVal.lower() == ".word" || IDVal.lower() == ".short") {
-    ParseLiteralValues(2, DirectiveID.getLoc());
-  } else if (IDVal.lower() == ".byte") {
-    ParseLiteralValues(1, DirectiveID.getLoc());
-  } else if (IDVal.lower() == ".refsym") {
+  if (IDVal.lower() == ".long")
+    return ParseLiteralValues(4, DirectiveID.getLoc());
+  if (IDVal.lower() == ".word" || IDVal.lower() == ".short")
+    return ParseLiteralValues(2, DirectiveID.getLoc());
+  if (IDVal.lower() == ".byte")
+    return ParseLiteralValues(1, DirectiveID.getLoc());
+  if (IDVal.lower() == ".refsym")
     return ParseDirectiveRefSym(DirectiveID);
-  }
-  return true;
+  return ParseStatus::NoMatch;
 }
 
 bool MSP430AsmParser::ParseOperand(OperandVector &Operands) {
diff --git a/llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp b/llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp
--- a/llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp
+++ b/llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp
@@ -76,7 +76,7 @@
                                         SMLoc &EndLoc) override;
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
 
   unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
                                       unsigned Kind) override;
@@ -769,25 +769,23 @@
   return false;
 }
 
-bool SparcAsmParser::
-ParseDirective(AsmToken DirectiveID)
-{
+ParseStatus SparcAsmParser::parseDirective(AsmToken DirectiveID) {
   StringRef IDVal = DirectiveID.getString();
 
   if (IDVal == ".register") {
     // For now, ignore .register directive.
     Parser.eatToEndOfStatement();
-    return false;
+    return ParseStatus::Success;
   }
   if (IDVal == ".proc") {
     // For compatibility, ignore this directive.
     // (It's supposed to be an "optimization" in the Sun assembler)
     Parser.eatToEndOfStatement();
-    return false;
+    return ParseStatus::Success;
   }
 
   // Let the MC layer to handle other directives.
-  return true;
+  return ParseStatus::NoMatch;
 }
 
 OperandMatchResultTy
diff --git a/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp b/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp
--- a/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp
+++ b/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp
@@ -494,7 +494,7 @@
   }
 
   // Override MCTargetAsmParser.
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
   bool parseRegister(MCRegister &RegNo, SMLoc &StartLoc,
                      SMLoc &EndLoc) override;
   bool ParseRegister(MCRegister &RegNo, SMLoc &StartLoc, SMLoc &EndLoc,
@@ -1219,7 +1219,7 @@
   return MatchOperand_Success;
 }
 
-bool SystemZAsmParser::ParseDirective(AsmToken DirectiveID) {
+ParseStatus SystemZAsmParser::parseDirective(AsmToken DirectiveID) {
   StringRef IDVal = DirectiveID.getIdentifier();
 
   if (IDVal == ".insn")
@@ -1229,7 +1229,7 @@
   if (IDVal.startswith(".gnu_attribute"))
     return ParseGNUAttribute(DirectiveID.getLoc());
 
-  return true;
+  return ParseStatus::NoMatch;
 }
 
 /// ParseDirectiveInsn
@@ -1346,12 +1346,12 @@
   MCAsmParser &Parser = getParser();
   if (Parser.getTok().isNot(AsmToken::Identifier) &&
       Parser.getTok().isNot(AsmToken::String))
-    return Error(L, "unexpected token in '.machine' directive");
+    return TokError("unexpected token in '.machine' directive");
 
   StringRef CPU = Parser.getTok().getIdentifier();
   Parser.Lex();
-  if (parseToken(AsmToken::EndOfStatement))
-    return addErrorSuffix(" in '.machine' directive");
+  if (parseEOL())
+    return true;
 
   MCSubtargetInfo &STI = copySTI();
   STI.setDefaultFeatures(CPU, /*TuneCPU*/ CPU, "");
@@ -1366,18 +1366,15 @@
   int64_t Tag;
   int64_t IntegerValue;
   if (!Parser.parseGNUAttribute(L, Tag, IntegerValue))
-    return false;
+    return Error(L, "malformed .gnu_attribute directive");
 
   // Tag_GNU_S390_ABI_Vector tag is '8' and can be 0, 1, or 2.
-  if (Tag != 8 || (IntegerValue < 0 || IntegerValue > 2)) {
-    Error(Parser.getTok().getLoc(),
-          "Unrecognized .gnu_attribute tag/value pair.");
-    return false;
-  }
+  if (Tag != 8 || (IntegerValue < 0 || IntegerValue > 2))
+    return Error(L, "unrecognized .gnu_attribute tag/value pair.");
 
   Parser.getStreamer().emitGNUAttribute(Tag, IntegerValue);
 
-  return true;
+  return parseEOL();
 }
 
 bool SystemZAsmParser::ParseRegister(MCRegister &RegNo, SMLoc &StartLoc,
diff --git a/llvm/lib/Target/VE/AsmParser/VEAsmParser.cpp b/llvm/lib/Target/VE/AsmParser/VEAsmParser.cpp
--- a/llvm/lib/Target/VE/AsmParser/VEAsmParser.cpp
+++ b/llvm/lib/Target/VE/AsmParser/VEAsmParser.cpp
@@ -62,7 +62,7 @@
                                         SMLoc &EndLoc) override;
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override;
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
 
   unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
                                       unsigned Kind) override;
@@ -998,7 +998,7 @@
   return false;
 }
 
-bool VEAsmParser::ParseDirective(AsmToken DirectiveID) {
+ParseStatus VEAsmParser::parseDirective(AsmToken DirectiveID) {
   std::string IDVal = DirectiveID.getIdentifier().lower();
 
   // Defines VE specific directives.  Reference is "Vector Engine Assembly
@@ -1018,7 +1018,7 @@
     return parseLiteralValues(8, DirectiveID.getLoc());
 
   // Let the MC layer to handle other directives.
-  return true;
+  return ParseStatus::NoMatch;
 }
 
 /// parseLiteralValues
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -785,31 +785,23 @@
   // This function processes wasm-specific directives streamed to
   // WebAssemblyTargetStreamer, all others go to the generic parser
   // (see WasmAsmParser).
-  bool ParseDirective(AsmToken DirectiveID) override {
-    // This function has a really weird return value behavior that is different
-    // from all the other parsing functions:
-    // - return true && no tokens consumed -> don't know this directive / let
-    //   the generic parser handle it.
-    // - return true && tokens consumed -> a parsing error occurred.
-    // - return false -> processed this directive successfully.
+  ParseStatus parseDirective(AsmToken DirectiveID) override {
     assert(DirectiveID.getKind() == AsmToken::Identifier);
     auto &Out = getStreamer();
     auto &TOut =
         reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer());
     auto &Ctx = Out.getContext();
 
-    // TODO: any time we return an error, at least one token must have been
-    // consumed, otherwise this will not signal an error to the caller.
     if (DirectiveID.getString() == ".globaltype") {
       auto SymName = expectIdent();
       if (SymName.empty())
-        return true;
+        return ParseStatus::Failure;
       if (expect(AsmToken::Comma, ","))
-        return true;
+        return ParseStatus::Failure;
       auto TypeTok = Lexer.getTok();
       auto TypeName = expectIdent();
       if (TypeName.empty())
-        return true;
+        return ParseStatus::Failure;
       auto Type = WebAssembly::parseType(TypeName);
       if (!Type)
         return error("Unknown type in .globaltype directive: ", TypeTok);
@@ -820,6 +812,8 @@
       if (isNext(AsmToken::Comma)) {
         TypeTok = Lexer.getTok();
         auto Id = expectIdent();
+        if (Id.empty())
+          return ParseStatus::Failure;
         if (Id == "immutable")
           Mutable = false;
         else
@@ -839,14 +833,14 @@
       // .tabletype SYM, ELEMTYPE[, MINSIZE[, MAXSIZE]]
       auto SymName = expectIdent();
       if (SymName.empty())
-        return true;
+        return ParseStatus::Failure;
       if (expect(AsmToken::Comma, ","))
-        return true;
+        return ParseStatus::Failure;
 
       auto ElemTypeTok = Lexer.getTok();
       auto ElemTypeName = expectIdent();
       if (ElemTypeName.empty())
-        return true;
+        return ParseStatus::Failure;
       std::optional<wasm::ValType> ElemType =
           WebAssembly::parseType(ElemTypeName);
       if (!ElemType)
@@ -854,7 +848,7 @@
 
       wasm::WasmLimits Limits = DefaultLimits();
       if (isNext(AsmToken::Comma) && parseLimits(&Limits))
-        return true;
+        return ParseStatus::Failure;
 
       // Now that we have the name and table type, we can actually create the
       // symbol
@@ -874,7 +868,7 @@
       // parses the locals separately.
       auto SymName = expectIdent();
       if (SymName.empty())
-        return true;
+        return ParseStatus::Failure;
       auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
       if (WasmSym->isDefined()) {
         // We push 'Function' either when a label is parsed or a .functype
@@ -890,7 +884,7 @@
         if (CurrentState != FunctionLabel) {
           // This .functype indicates a start of a function.
           if (ensureEmptyNestingStack())
-            return true;
+            return ParseStatus::Failure;
           push(Function);
         }
         CurrentState = FunctionStart;
@@ -898,7 +892,7 @@
       }
       auto Signature = std::make_unique<wasm::WasmSignature>();
       if (parseSignature(Signature.get()))
-        return true;
+        return ParseStatus::Failure;
       TC.funcDecl(*Signature);
       WasmSym->setSignature(Signature.get());
       addSignature(std::move(Signature));
@@ -911,47 +905,56 @@
     if (DirectiveID.getString() == ".export_name") {
       auto SymName = expectIdent();
       if (SymName.empty())
-        return true;
+        return ParseStatus::Failure;
       if (expect(AsmToken::Comma, ","))
-        return true;
+        return ParseStatus::Failure;
       auto ExportName = expectIdent();
+      if (ExportName.empty())
+        return ParseStatus::Failure;
       auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
       WasmSym->setExportName(storeName(ExportName));
       TOut.emitExportName(WasmSym, ExportName);
+      return expect(AsmToken::EndOfStatement, "EOL");
     }
 
     if (DirectiveID.getString() == ".import_module") {
       auto SymName = expectIdent();
       if (SymName.empty())
-        return true;
+        return ParseStatus::Failure;
       if (expect(AsmToken::Comma, ","))
-        return true;
+        return ParseStatus::Failure;
       auto ImportModule = expectIdent();
+      if (ImportModule.empty())
+        return ParseStatus::Failure;
       auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
       WasmSym->setImportModule(storeName(ImportModule));
       TOut.emitImportModule(WasmSym, ImportModule);
+      return expect(AsmToken::EndOfStatement, "EOL");
     }
 
     if (DirectiveID.getString() == ".import_name") {
       auto SymName = expectIdent();
       if (SymName.empty())
-        return true;
+        return ParseStatus::Failure;
       if (expect(AsmToken::Comma, ","))
-        return true;
+        return ParseStatus::Failure;
       auto ImportName = expectIdent();
+      if (ImportName.empty())
+        return ParseStatus::Failure;
       auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
       WasmSym->setImportName(storeName(ImportName));
       TOut.emitImportName(WasmSym, ImportName);
+      return expect(AsmToken::EndOfStatement, "EOL");
     }
 
     if (DirectiveID.getString() == ".tagtype") {
       auto SymName = expectIdent();
       if (SymName.empty())
-        return true;
+        return ParseStatus::Failure;
       auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
       auto Signature = std::make_unique<wasm::WasmSignature>();
       if (parseRegTypeList(Signature->Params))
-        return true;
+        return ParseStatus::Failure;
       WasmSym->setSignature(Signature.get());
       addSignature(std::move(Signature));
       WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG);
@@ -966,7 +969,7 @@
                      Lexer.getTok());
       SmallVector<wasm::ValType, 4> Locals;
       if (parseRegTypeList(Locals))
-        return true;
+        return ParseStatus::Failure;
       TC.localDecl(Locals);
       TOut.emitLocal(Locals);
       CurrentState = FunctionLocals;
@@ -978,7 +981,7 @@
         DirectiveID.getString() == ".int32" ||
         DirectiveID.getString() == ".int64") {
       if (CheckDataSection())
-        return true;
+        return ParseStatus::Failure;
       const MCExpr *Val;
       SMLoc End;
       if (Parser.parseExpression(Val, End))
@@ -991,7 +994,7 @@
 
     if (DirectiveID.getString() == ".asciz") {
       if (CheckDataSection())
-        return true;
+        return ParseStatus::Failure;
       std::string S;
       if (Parser.parseEscapedString(S))
         return error("Cannot parse string constant: ", Lexer.getTok());
@@ -999,7 +1002,7 @@
       return expect(AsmToken::EndOfStatement, "EOL");
     }
 
-    return true; // We didn't process this directive.
+    return ParseStatus::NoMatch; // We didn't process this directive.
   }
 
   // Called either when the first instruction is parsed of the function ends.
diff --git a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
--- a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
+++ b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
@@ -36,7 +36,7 @@
   SMLoc getLoc() const { return getParser().getTok().getLoc(); }
 
   // Override MCTargetAsmParser.
-  bool ParseDirective(AsmToken DirectiveID) override;
+  ParseStatus parseDirective(AsmToken DirectiveID) override;
   bool parseRegister(MCRegister &RegNo,
                      SMLoc &StartLoc, SMLoc &EndLoc) override;
   bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
@@ -697,7 +697,9 @@
   return false;
 }
 
-bool XtensaAsmParser::ParseDirective(AsmToken DirectiveID) { return true; }
+ParseStatus XtensaAsmParser::parseDirective(AsmToken DirectiveID) {
+  return ParseStatus::NoMatch;
+}
 
 // Force static initialization.
 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaAsmParser() {
diff --git a/llvm/test/MC/CSKY/invalid-attribute.s b/llvm/test/MC/CSKY/invalid-attribute.s
--- a/llvm/test/MC/CSKY/invalid-attribute.s
+++ b/llvm/test/MC/CSKY/invalid-attribute.s
@@ -5,6 +5,21 @@
 
 # RUN: not llvm-mc %s -triple=csky -filetype=asm 2>&1 | FileCheck %s
 
+.csky_attribute CSKY_UNKNOWN
+# CHECK: [[@LINE-1]]:17: error: attribute name not recognised: CSKY_UNKNOWN
+
+.csky_attribute CSKY_ARCH_NAME
+# CHECK: [[@LINE-1]]:31: error: expected comma
+
+.csky_attribute CSKY_ISA_FLAGS
+# CHECK: [[@LINE-1]]:31: error: expected comma
+
+.csky_attribute CSKY_ARCH_NAME, "foo",
+# CHECK: [[@LINE-1]]:38: error: expected newline
+
+.csky_attribute CSKY_ISA_FLAGS, 42,
+# CHECK: [[@LINE-1]]:35: error: expected newline
+
 .csky_attribute CSKY_ARCH_NAME, "foo"
 # CHECK: [[@LINE-1]]:33: error: unknown arch name 
 
diff --git a/llvm/test/MC/MSP430/directive-byte-word-long-invalid.s b/llvm/test/MC/MSP430/directive-byte-word-long-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/MSP430/directive-byte-word-long-invalid.s
@@ -0,0 +1,22 @@
+# RUN: not llvm-mc -triple=msp430 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+3]]:6: error: unknown token in expression
+# CHECK: [[#@LINE+3]]:6: error: unknown token in expression
+# CHECK: [[#@LINE+3]]:6: error: unknown token in expression
+.byte, 42
+.word, 42
+.long, 42
+
+# CHECK: [[#@LINE+3]]:10: error: unknown token in expression
+# CHECK: [[#@LINE+3]]:10: error: unknown token in expression
+# CHECK: [[#@LINE+3]]:10: error: unknown token in expression
+.byte 42,
+.word 42,
+.long 42,
+
+# CHECK: [[#@LINE+3]]:10: error: unexpected token
+# CHECK: [[#@LINE+3]]:10: error: unexpected token
+# CHECK: [[#@LINE+3]]:10: error: unexpected token
+.byte 42 42
+.word 42 42
+.long 42 42
diff --git a/llvm/test/MC/MSP430/refsym-invalid.s b/llvm/test/MC/MSP430/refsym-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/MSP430/refsym-invalid.s
@@ -0,0 +1,10 @@
+# RUN: not llvm-mc -triple=msp430 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:8: error: expected identifier in directive
+.refsym
+
+# CHECK: [[#@LINE+1]]:9: error: expected identifier in directive
+.refsym 42
+
+# CHECK: [[#@LINE+1]]:12: error: expected newline
+.refsym sym,
diff --git a/llvm/test/MC/Sparc/sparc-directives.s b/llvm/test/MC/Sparc/sparc-directives.s
--- a/llvm/test/MC/Sparc/sparc-directives.s
+++ b/llvm/test/MC/Sparc/sparc-directives.s
@@ -3,7 +3,10 @@
 
         ! '.proc' is documented to do nothing in the binutils assembler.
         ! so it should do nothing for clang either, i.e. not be an error.
-        .proc 1
+        .proc 1 x (
+
+        ! '.register' is currently ignored.
+        .register 8-)
 
         ! SPARC32: .byte 24
         ! SPARC64: .byte 24
diff --git a/llvm/test/MC/SystemZ/gnu-attributes-invalid.s b/llvm/test/MC/SystemZ/gnu-attributes-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/SystemZ/gnu-attributes-invalid.s
@@ -0,0 +1,13 @@
+# RUN: not llvm-mc -triple s390x %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:1: error: malformed .gnu_attribute directive
+.gnu_attribute tag, value
+
+# CHECK: [[#@LINE+1]]:1: error: unrecognized .gnu_attribute tag/value pair.
+.gnu_attribute 42, 8
+
+# CHECK: [[#@LINE+1]]:1: error: unrecognized .gnu_attribute tag/value pair.
+.gnu_attribute 8, 42
+
+# CHECK: [[#@LINE+1]]:20: error: expected newline
+.gnu_attribute 8, 1$
diff --git a/llvm/test/MC/SystemZ/machine-directive-invalid.s b/llvm/test/MC/SystemZ/machine-directive-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/SystemZ/machine-directive-invalid.s
@@ -0,0 +1,10 @@
+# RUN: not llvm-mc -triple=s390x %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:9: error: unexpected token in '.machine' directive
+.machine
+
+# CHECK: [[#@LINE+1]]:10: error: unexpected token in '.machine' directive
+.machine 42
+
+# CHECK: [[#@LINE+1]]:13: error: expected newline
+.machine z13+
diff --git a/llvm/test/MC/VE/data-size-error.s b/llvm/test/MC/VE/data-size-error.s
--- a/llvm/test/MC/VE/data-size-error.s
+++ b/llvm/test/MC/VE/data-size-error.s
@@ -34,3 +34,12 @@
 # CHECK-NEXT: .quad 0xff5588aadeadbeafde
 # CHECK:      data-size-error.s:15:8: error: literal value out of range for directive
 # CHECK-NEXT: .llong 0xff5588aadeadbeafde
+
+# CHECK: [[#@LINE+1]]:17: error: unknown token in expression
+.word 0xd0bb1e +
+
+# CHECK: [[#@LINE+1]]:16: error: unexpected token
+.long 0xd0bb1e =
+
+# CHECK: [[#@LINE+1]]:10: error: unexpected token
+.llong 2 0xd0bb1e
diff --git a/llvm/test/MC/WebAssembly/export-name-invalid.s b/llvm/test/MC/WebAssembly/export-name-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/export-name-invalid.s
@@ -0,0 +1,13 @@
+# RUN: not llvm-mc -triple=wasm32 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:14: error: Expected identifier, got: 42
+.export_name 42
+
+# CHECK: [[#@LINE+1]]:17: error: Expected ,, instead got:
+.export_name foo
+
+# CHECK: [[#@LINE+1]]:18: error: Expected identifier, got:
+.export_name foo,
+
+# CHECK: [[#@LINE+1]]:22: error: Expected EOL, instead got: ,
+.export_name foo, bar,
diff --git a/llvm/test/MC/WebAssembly/functype-invalid.s b/llvm/test/MC/WebAssembly/functype-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/functype-invalid.s
@@ -0,0 +1,28 @@
+# RUN: not llvm-mc -triple=wasm32 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:10: error: Expected identifier, got:
+.functype
+
+# CHECK: [[#@LINE+1]]:13: error: Expected (, instead got:
+.functype fn
+
+# CHECK: [[#@LINE+1]]:15: error: Expected ), instead got:
+.functype fn (
+
+# CHECK: [[#@LINE+1]]:15: error: unknown type: i42
+.functype fn (i42
+
+# CHECK: [[#@LINE+1]]:19: error: Expected ), instead got: i32
+.functype fn (i32 i32
+
+# CHECK: [[#@LINE+1]]:16: error: Expected ->, instead got:
+.functype fn ()
+
+# CHECK: [[#@LINE+1]]:17: error: Expected ->, instead got: <
+.functype fn () <- ()
+
+# CHECK: [[#@LINE+1]]:21: error: Expected ), instead got:
+.functype fn () -> (
+
+# CHECK: [[#@LINE+1]]:23: error: Expected EOL, instead got: ->
+.functype fn () -> () -> ()
diff --git a/llvm/test/MC/WebAssembly/globaltype-invalid.s b/llvm/test/MC/WebAssembly/globaltype-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/globaltype-invalid.s
@@ -0,0 +1,31 @@
+# RUN: not llvm-mc -triple=wasm32 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:12: error: Expected identifier, got:
+.globaltype
+
+# CHECK: [[#@LINE+1]]:13: error: Expected identifier, got: 42
+.globaltype 42
+
+# CHECK: [[#@LINE+1]]:16: error: Expected ,, instead got:
+.globaltype sym
+
+# CHECK: [[#@LINE+1]]:17: error: Expected identifier, got:
+.globaltype sym,
+
+# CHECK: [[#@LINE+1]]:18: error: Expected identifier, got: 42
+.globaltype sym, 42
+
+# CHECK: [[#@LINE+1]]:18: error: Unknown type in .globaltype directive: i42
+.globaltype sym, i42
+
+# CHECK: [[#@LINE+1]]:22: error: Expected identifier, got:
+.globaltype sym, i32,
+
+# CHECK: [[#@LINE+1]]:23: error: Expected identifier, got: 42
+.globaltype sym, i32, 42
+
+# CHECK: [[#@LINE+1]]:23: error: Unknown type in .globaltype modifier: unmutable
+.globaltype sym, i32, unmutable
+
+# CHECK: [[#@LINE+1]]:32: error: Expected EOL, instead got: ,
+.globaltype sym, i32, immutable,
diff --git a/llvm/test/MC/WebAssembly/import-module-invalid.s b/llvm/test/MC/WebAssembly/import-module-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/import-module-invalid.s
@@ -0,0 +1,13 @@
+# RUN: not llvm-mc -triple=wasm32 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:16: error: Expected identifier, got: 42
+.import_module 42
+
+# CHECK: [[#@LINE+1]]:19: error: Expected ,, instead got:
+.import_module foo
+
+# CHECK: [[#@LINE+1]]:20: error: Expected identifier, got:
+.import_module foo,
+
+# CHECK: [[#@LINE+1]]:24: error: Expected EOL, instead got: ,
+.import_module foo, bar,
diff --git a/llvm/test/MC/WebAssembly/import-name-invalid.s b/llvm/test/MC/WebAssembly/import-name-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/import-name-invalid.s
@@ -0,0 +1,13 @@
+# RUN: not llvm-mc -triple=wasm32 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:14: error: Expected identifier, got: 42
+.import_name 42
+
+# CHECK: [[#@LINE+1]]:17: error: Expected ,, instead got:
+.import_name foo
+
+# CHECK: [[#@LINE+1]]:18: error: Expected identifier, got:
+.import_name foo,
+
+# CHECK: [[#@LINE+1]]:22: error: Expected EOL, instead got: ,
+.import_name foo, bar,
diff --git a/llvm/test/MC/WebAssembly/tabletype-invalid.s b/llvm/test/MC/WebAssembly/tabletype-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/tabletype-invalid.s
@@ -0,0 +1,28 @@
+# RUN: not llvm-mc -triple=wasm32 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:11: error: Expected identifier, got:
+.tabletype
+
+# CHECK: [[#@LINE+1]]:12: error: Expected identifier, got: 42
+.tabletype 42
+
+# CHECK: [[#@LINE+1]]:15: error: Expected ,, instead got:
+.tabletype sym
+
+# CHECK: [[#@LINE+1]]:16: error: Expected identifier, got:
+.tabletype sym,
+
+# CHECK: [[#@LINE+1]]:17: error: Expected identifier, got: 42
+.tabletype sym, 42
+
+# CHECK: [[#@LINE+1]]:17: error: Unknown type in .tabletype directive: i42
+.tabletype sym, i42
+
+# CHECK: [[#@LINE+1]]:21: error: Expected integer constant, instead got:
+.tabletype sym, i32,
+
+# CHECK: [[#@LINE+1]]:25: error: Expected integer constant, instead got:
+.tabletype sym, i32, 42,
+
+# CHECK: [[#@LINE+1]]:28: error: Expected EOL, instead got: ,
+.tabletype sym, i32, 42, 42,
diff --git a/llvm/test/MC/WebAssembly/tagtype-invalid.s b/llvm/test/MC/WebAssembly/tagtype-invalid.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/tagtype-invalid.s
@@ -0,0 +1,10 @@
+# RUN: not llvm-mc -triple=wasm32 %s 2>&1 | FileCheck %s
+
+# CHECK: [[#@LINE+1]]:10: error: Expected identifier, got: 42
+.tagtype 42
+
+# CHECK: [[#@LINE+1]]:13: error: Expected EOL, instead got: ,
+.tagtype foo, i32
+
+# CHECK: [[#@LINE+1]]:18: error: Expected EOL, instead got: pub
+.tagtype bar i32 pub