[val] Add extra context to error messages. (#1600)

[val] Add extra context to error messages.

This CL extends the error messages produced by the validator to output the
disassembly of the errored line.

The validation_id messages have also been updated to print the line number of
the error instead of the word number. Note, the error number is from the start
of the SPIR-V, it does not include any headers printed in the disassembled code.

Fixes #670, #1581
diff --git a/source/binary.cpp b/source/binary.cpp
index 7ac5765..3b842c4 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -122,7 +122,8 @@
   // returned object will be propagated to the current parse's diagnostic
   // object.
   libspirv::DiagnosticStream diagnostic(spv_result_t error) {
-    return libspirv::DiagnosticStream({0, 0, _.word_index}, consumer_, error);
+    return libspirv::DiagnosticStream({0, 0, _.word_index}, consumer_, "",
+                                      error);
   }
 
   // Returns a diagnostic stream object with the default parse error code.
diff --git a/source/comp/markv_codec.cpp b/source/comp/markv_codec.cpp
index cbc25ab..cdf0ce8 100644
--- a/source/comp/markv_codec.cpp
+++ b/source/comp/markv_codec.cpp
@@ -388,18 +388,7 @@
         model_(model),
         short_id_descriptors_(ShortHashU32Array),
         mtf_huffman_codecs_(GetMtfHuffmanCodecs()),
-        context_(context),
-        vstate_(validator_options
-                    ? new ValidationState_t(context, validator_options_)
-                    : nullptr) {}
-
-  // Validates a single instruction and updates validation state of the module.
-  // Does nothing and returns SPV_SUCCESS if validator was not created.
-  spv_result_t UpdateValidationState(const spv_parsed_instruction_t& inst) {
-    if (!vstate_) return SPV_SUCCESS;
-
-    return ValidateInstructionAndUpdateValidationState(vstate_.get(), &inst);
-  }
+        context_(context) {}
 
   // Returns instruction which created |id| or nullptr if such instruction was
   // not registered.
@@ -493,7 +482,7 @@
   // Returns diagnostic stream, position index is set to instruction number.
   DiagnosticStream Diag(spv_result_t error_code) const {
     return DiagnosticStream({0, 0, instructions_.size()}, context_->consumer,
-                            error_code);
+                            "", error_code);
   }
 
   // Returns current id bound.
@@ -503,7 +492,6 @@
   void SetIdBound(uint32_t id_bound) {
     assert(id_bound >= id_bound_);
     id_bound_ = id_bound;
-    if (vstate_) vstate_->setIdBound(id_bound);
   }
 
   // Returns Huffman codec for ranks of the mtf with given |handle|.
@@ -587,11 +575,9 @@
   // If not nullptr, codec will log comments on the compression process.
   std::unique_ptr<MarkvLogger> logger_;
 
- private:
   spv_const_context context_ = nullptr;
 
-  std::unique_ptr<ValidationState_t> vstate_;
-
+ private:
   // Maps result id to the instruction which defined it.
   std::unordered_map<uint32_t, const Instruction*> id_to_def_instruction_;
 
@@ -760,13 +746,6 @@
   // of if validation fails.
   spv_result_t DecodeModule(std::vector<uint32_t>* spirv_binary);
 
- private:
-  // Describes the format of a typed literal number.
-  struct NumberType {
-    spv_number_kind_t type;
-    uint32_t bit_width;
-  };
-
   // Creates and returns validator options. Returned value owned by the caller.
   static spv_validator_options GetValidatorOptions(
       const MarkvCodecOptions& options) {
@@ -774,6 +753,13 @@
                                          : nullptr;
   }
 
+ private:
+  // Describes the format of a typed literal number.
+  struct NumberType {
+    spv_number_kind_t type;
+    uint32_t bit_width;
+  };
+
   // Reads a single bit from reader_. The read bit is stored in |bit|.
   // Returns false iff reader_ fails.
   bool ReadBit(bool* bit) {
@@ -2148,9 +2134,6 @@
   SpvOp opcode = SpvOp(inst.opcode);
   inst_ = inst;
 
-  const spv_result_t validation_result = UpdateValidationState(inst);
-  if (validation_result != SPV_SUCCESS) return validation_result;
-
   LogDisassemblyInstruction();
 
   const spv_result_t opcode_encodig_result =
@@ -2324,11 +2307,16 @@
     inst_ = {};
     const spv_result_t decode_result = DecodeInstruction();
     if (decode_result != SPV_SUCCESS) return decode_result;
-
-    const spv_result_t validation_result = UpdateValidationState(inst_);
-    if (validation_result != SPV_SUCCESS) return validation_result;
   }
 
+  if (validator_options_) {
+    spv_const_binary_t validation_binary = {spirv_.data(), spirv_.size()};
+    const spv_result_t result = spvValidateWithOptions(
+        context_, validator_options_, &validation_binary, nullptr);
+    if (result != SPV_SUCCESS) return result;
+  }
+
+  // Validate the decode binary
   if (reader_.GetNumReadBits() != header_.markv_length_in_bits ||
       !reader_.OnlyZeroesLeft()) {
     return Diag(SPV_ERROR_INVALID_BINARY)
@@ -2847,25 +2835,18 @@
   spv_context_t hijack_context = *context;
   libspirv::SetContextMessageConsumer(&hijack_context, message_consumer);
 
-  spv_const_binary_t spirv_binary = {spirv.data(), spirv.size()};
-
-  spv_endianness_t endian;
-  spv_position_t position = {};
-  if (spvBinaryEndianness(&spirv_binary, &endian)) {
-    return DiagnosticStream(position, hijack_context.consumer,
-                            SPV_ERROR_INVALID_BINARY)
-           << "Invalid SPIR-V magic number.";
-  }
-
-  spv_header_t header;
-  if (spvBinaryHeaderGet(&spirv_binary, endian, &header)) {
-    return DiagnosticStream(position, hijack_context.consumer,
-                            SPV_ERROR_INVALID_BINARY)
-           << "Invalid SPIR-V header.";
+  spv_validator_options validator_options =
+      MarkvDecoder::GetValidatorOptions(options);
+  if (validator_options) {
+    spv_const_binary_t spirv_binary = {spirv.data(), spirv.size()};
+    const spv_result_t result = spvValidateWithOptions(
+        &hijack_context, validator_options, &spirv_binary, nullptr);
+    if (result != SPV_SUCCESS) return result;
   }
 
   MarkvEncoder encoder(&hijack_context, options, &markv_model);
 
+  spv_position_t position = {};
   if (log_consumer || debug_consumer) {
     encoder.CreateLogger(log_consumer, debug_consumer);
 
@@ -2873,7 +2854,7 @@
     if (spvBinaryToText(&hijack_context, spirv.data(), spirv.size(),
                         SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, &text,
                         nullptr) != SPV_SUCCESS) {
-      return DiagnosticStream(position, hijack_context.consumer,
+      return DiagnosticStream(position, hijack_context.consumer, "",
                               SPV_ERROR_INVALID_BINARY)
              << "Failed to disassemble SPIR-V binary.";
     }
@@ -2884,7 +2865,7 @@
 
   if (spvBinaryParse(&hijack_context, &encoder, spirv.data(), spirv.size(),
                      EncodeHeader, EncodeInstruction, nullptr) != SPV_SUCCESS) {
-    return DiagnosticStream(position, hijack_context.consumer,
+    return DiagnosticStream(position, hijack_context.consumer, "",
                             SPV_ERROR_INVALID_BINARY)
            << "Unable to encode to MARK-V.";
   }
@@ -2908,7 +2889,7 @@
     decoder.CreateLogger(log_consumer, debug_consumer);
 
   if (decoder.DecodeModule(spirv) != SPV_SUCCESS) {
-    return DiagnosticStream(position, hijack_context.consumer,
+    return DiagnosticStream(position, hijack_context.consumer, "",
                             SPV_ERROR_INVALID_BINARY)
            << "Unable to decode MARK-V.";
   }
diff --git a/source/diagnostic.cpp b/source/diagnostic.cpp
index 5787120..5b81718 100644
--- a/source/diagnostic.cpp
+++ b/source/diagnostic.cpp
@@ -71,6 +71,7 @@
     : stream_(),
       position_(other.position_),
       consumer_(other.consumer_),
+      disassembled_instruction_(std::move(other.disassembled_instruction_)),
       error_(other.error_) {
   // Prevent the other object from emitting output during destruction.
   other.error_ = SPV_FAILED_MATCH;
@@ -102,6 +103,9 @@
       default:
         break;
     }
+    if (disassembled_instruction_.size() > 0)
+      stream_ << std::endl << "  " << disassembled_instruction_ << std::endl;
+
     consumer_(level, "input", position_, stream_.str().c_str());
   }
 }
diff --git a/source/diagnostic.h b/source/diagnostic.h
index 3d06f8d..a36eb43 100644
--- a/source/diagnostic.h
+++ b/source/diagnostic.h
@@ -30,8 +30,12 @@
  public:
   DiagnosticStream(spv_position_t position,
                    const spvtools::MessageConsumer& consumer,
+                   const std::string& disassembled_instruction,
                    spv_result_t error)
-      : position_(position), consumer_(consumer), error_(error) {}
+      : position_(position),
+        consumer_(consumer),
+        disassembled_instruction_(disassembled_instruction),
+        error_(error) {}
 
   // Creates a DiagnosticStream from an expiring DiagnosticStream.
   // The new object takes the contents of the other, and prevents the
@@ -57,6 +61,7 @@
   std::ostringstream stream_;
   spv_position_t position_;
   spvtools::MessageConsumer consumer_;  // Message consumer callback.
+  std::string disassembled_instruction_;
   spv_result_t error_;
 };
 
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 50d1870..daa584d 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -172,7 +172,7 @@
 
   linked_binary->clear();
   if (num_binaries == 0u)
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_BINARY)
            << "No modules were given.";
 
@@ -183,7 +183,7 @@
     const uint32_t schema = binaries[i][4u];
     if (schema != 0u) {
       position.index = 4u;
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_BINARY)
              << "Schema is non-zero for module " << i << ".";
     }
@@ -191,7 +191,7 @@
     std::unique_ptr<IRContext> ir_context = BuildModule(
         c_context->target_env, consumer, binaries[i], binary_sizes[i]);
     if (ir_context == nullptr)
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_BINARY)
              << "Failed to build a module out of " << ir_contexts.size() << ".";
     modules.push_back(ir_context->module());
@@ -270,15 +270,15 @@
   spv_position_t position = {};
 
   if (modules == nullptr)
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|modules| of ShiftIdsInModules should not be null.";
   if (modules->empty())
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|modules| of ShiftIdsInModules should not be empty.";
   if (max_id_bound == nullptr)
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|max_id_bound| of ShiftIdsInModules should not be null.";
 
@@ -291,7 +291,7 @@
     });
     id_bound += module->IdBound() - 1u;
     if (id_bound > 0x3FFFFF)
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_ID)
              << "The limit of IDs, 4194303, was exceeded:"
              << " " << id_bound << " is the current ID bound.";
@@ -301,7 +301,8 @@
   }
   ++id_bound;
   if (id_bound > 0x3FFFFF)
-    return libspirv::DiagnosticStream(position, consumer, SPV_ERROR_INVALID_ID)
+    return libspirv::DiagnosticStream(position, consumer, "",
+                                      SPV_ERROR_INVALID_ID)
            << "The limit of IDs, 4194303, was exceeded:"
            << " " << id_bound << " is the current ID bound.";
 
@@ -317,11 +318,11 @@
   spv_position_t position = {};
 
   if (modules.empty())
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|modules| of GenerateHeader should not be empty.";
   if (max_id_bound == 0u)
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|max_id_bound| of GenerateHeader should not be null.";
 
@@ -345,7 +346,7 @@
   spv_position_t position = {};
 
   if (linked_context == nullptr)
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|linked_module| of MergeModules should not be null.";
   Module* linked_module = linked_context->module();
@@ -384,7 +385,7 @@
         grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
                               memory_model_inst->GetSingleWordOperand(0u),
                               &current_desc);
-        return libspirv::DiagnosticStream(position, consumer,
+        return libspirv::DiagnosticStream(position, consumer, "",
                                           SPV_ERROR_INTERNAL)
                << "Conflicting addressing models: " << initial_desc->name
                << " vs " << current_desc->name << ".";
@@ -396,7 +397,7 @@
         grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL,
                               memory_model_inst->GetSingleWordOperand(1u),
                               &current_desc);
-        return libspirv::DiagnosticStream(position, consumer,
+        return libspirv::DiagnosticStream(position, consumer, "",
                                           SPV_ERROR_INTERNAL)
                << "Conflicting memory models: " << initial_desc->name << " vs "
                << current_desc->name << ".";
@@ -422,7 +423,7 @@
       if (i != entry_points.end()) {
         spv_operand_desc desc = nullptr;
         grammar.lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODEL, model, &desc);
-        return libspirv::DiagnosticStream(position, consumer,
+        return libspirv::DiagnosticStream(position, consumer, "",
                                           SPV_ERROR_INTERNAL)
                << "The entry point \"" << name << "\", with execution model "
                << desc->name << ", was already defined.";
@@ -483,7 +484,8 @@
     }
   }
   if (num_global_values > 0xFFFF)
-    return libspirv::DiagnosticStream(position, consumer, SPV_ERROR_INTERNAL)
+    return libspirv::DiagnosticStream(position, consumer, "",
+                                      SPV_ERROR_INTERNAL)
            << "The limit of global values, 65535, was exceeded;"
            << " " << num_global_values << " global values were found.";
 
@@ -507,7 +509,7 @@
   spv_position_t position = {};
 
   if (linkings_to_do == nullptr)
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|linkings_to_do| of GetImportExportPairs should not be empty.";
 
@@ -547,7 +549,7 @@
     // types.
     const Instruction* def_inst = def_use_manager.GetDef(id);
     if (def_inst == nullptr)
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_BINARY)
              << "ID " << id << " is never defined:\n";
 
@@ -566,7 +568,7 @@
         });
       }
     } else {
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_BINARY)
              << "Only global variables and functions can be decorated using"
              << " LinkageAttributes; " << id << " is neither of them.\n";
@@ -584,11 +586,11 @@
     const auto& exp = exports.find(import.name);
     if (exp != exports.end()) possible_exports = exp->second;
     if (possible_exports.empty() && !allow_partial_linkage)
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_BINARY)
              << "Unresolved external reference to \"" << import.name << "\".";
     else if (possible_exports.size() > 1u)
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_BINARY)
              << "Too many external references, " << possible_exports.size()
              << ", were found for \"" << import.name << "\".";
@@ -613,7 +615,7 @@
             *def_use_manager.GetDef(linking_entry.imported_symbol.type_id),
             *def_use_manager.GetDef(linking_entry.exported_symbol.type_id),
             context))
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_BINARY)
              << "Type mismatch on symbol \""
              << linking_entry.imported_symbol.name
@@ -627,7 +629,7 @@
   for (const auto& linking_entry : linkings_to_do) {
     if (!decoration_manager.HaveTheSameDecorations(
             linking_entry.imported_symbol.id, linking_entry.exported_symbol.id))
-      return libspirv::DiagnosticStream(position, consumer,
+      return libspirv::DiagnosticStream(position, consumer, "",
                                         SPV_ERROR_INVALID_BINARY)
              << "Decorations mismatch on symbol \""
              << linking_entry.imported_symbol.name
@@ -652,12 +654,12 @@
   spv_position_t position = {};
 
   if (decoration_manager == nullptr)
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|decoration_manager| of RemoveLinkageSpecificInstructions "
               "should not be empty.";
   if (linked_context == nullptr)
-    return libspirv::DiagnosticStream(position, consumer,
+    return libspirv::DiagnosticStream(position, consumer, "",
                                       SPV_ERROR_INVALID_DATA)
            << "|linked_module| of RemoveLinkageSpecificInstructions should not "
               "be empty.";
diff --git a/source/spirv_stats.cpp b/source/spirv_stats.cpp
index ff4b3c6..7fab6b2 100644
--- a/source/spirv_stats.cpp
+++ b/source/spirv_stats.cpp
@@ -48,9 +48,11 @@
 // instruction.
 class StatsAggregator {
  public:
-  StatsAggregator(SpirvStats* in_out_stats, const spv_const_context context) {
+  StatsAggregator(SpirvStats* in_out_stats, const spv_const_context context,
+                  const uint32_t* words, size_t num_words) {
     stats_ = in_out_stats;
-    vstate_.reset(new ValidationState_t(context, &validator_options_));
+    vstate_.reset(
+        new ValidationState_t(context, &validator_options_, words, num_words));
   }
 
   // Collects header statistics and sets correct id_bound.
@@ -304,19 +306,19 @@
   spv_endianness_t endian;
   spv_position_t position = {};
   if (spvBinaryEndianness(&binary, &endian)) {
-    return libspirv::DiagnosticStream(position, context.consumer,
+    return libspirv::DiagnosticStream(position, context.consumer, "",
                                       SPV_ERROR_INVALID_BINARY)
            << "Invalid SPIR-V magic number.";
   }
 
   spv_header_t header;
   if (spvBinaryHeaderGet(&binary, endian, &header)) {
-    return libspirv::DiagnosticStream(position, context.consumer,
+    return libspirv::DiagnosticStream(position, context.consumer, "",
                                       SPV_ERROR_INVALID_BINARY)
            << "Invalid SPIR-V header.";
   }
 
-  StatsAggregator stats_aggregator(stats, &context);
+  StatsAggregator stats_aggregator(stats, &context, words, num_words);
 
   return spvBinaryParse(&context, &stats_aggregator, words, num_words,
                         ProcessHeader, ProcessInstruction, pDiagnostic);
diff --git a/source/text_handler.h b/source/text_handler.h
index e49b51b..6a07c8c 100644
--- a/source/text_handler.h
+++ b/source/text_handler.h
@@ -152,7 +152,7 @@
   // stream, and for the given error code. Any data written to this object will
   // show up in pDiagnsotic on destruction.
   DiagnosticStream diagnostic(spv_result_t error) {
-    return DiagnosticStream(current_position_, consumer_, error);
+    return DiagnosticStream(current_position_, consumer_, "", error);
   }
 
   // Returns a diagnostic object with the default assembly error code.
diff --git a/source/val/instruction.cpp b/source/val/instruction.cpp
index 56bd37f..cbea01e 100644
--- a/source/val/instruction.cpp
+++ b/source/val/instruction.cpp
@@ -39,6 +39,7 @@
       inst_({words_.data(), inst->num_words, inst->opcode, inst->ext_inst_type,
              inst->type_id, inst->result_id, operands_.data(),
              inst->num_operands}),
+      instruction_position_(0),
       function_(defining_function),
       block_(defining_block),
       uses_() {}
diff --git a/source/val/instruction.h b/source/val/instruction.h
index 9613632..8587ab3 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -83,10 +83,14 @@
     return *reinterpret_cast<const T*>(&words_[operand.offset]);
   }
 
+  int InstructionPosition() const { return instruction_position_; }
+  void SetInstructionPosition(int pos) { instruction_position_ = pos; }
+
  private:
   const std::vector<uint32_t> words_;
   const std::vector<spv_parsed_operand_t> operands_;
   spv_parsed_instruction_t inst_;
+  int instruction_position_;
 
   /// The function in which this instruction was declared
   Function* function_;
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index af581ae..137be8a 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -142,9 +142,13 @@
 }  // anonymous namespace
 
 ValidationState_t::ValidationState_t(const spv_const_context ctx,
-                                     const spv_const_validator_options opt)
+                                     const spv_const_validator_options opt,
+                                     const uint32_t* words,
+                                     const size_t num_words)
     : context_(ctx),
       options_(opt),
+      words_(words),
+      num_words_(num_words),
       instruction_counter_(0),
       unresolved_forward_ids_{},
       operand_names_{},
@@ -254,9 +258,19 @@
 }
 
 DiagnosticStream ValidationState_t::diag(spv_result_t error_code) const {
+  return diag(error_code, instruction_counter_);
+}
+
+DiagnosticStream ValidationState_t::diag(spv_result_t error_code,
+                                         int instruction_counter) const {
+  std::string disassembly;
+  if (instruction_counter >= 0 && static_cast<size_t>(instruction_counter) <=
+                                      ordered_instructions_.size()) {
+    disassembly = Disassemble(ordered_instructions_[instruction_counter - 1]);
+  }
   return libspirv::DiagnosticStream(
-      {0, 0, static_cast<size_t>(instruction_counter_)}, context_->consumer,
-      error_code);
+      {0, 0, static_cast<size_t>(instruction_counter)}, context_->consumer,
+      disassembly, error_code);
 }
 
 deque<Function>& ValidationState_t::functions() { return module_functions_; }
@@ -417,6 +431,8 @@
   } else {
     ordered_instructions_.emplace_back(&inst, nullptr, nullptr);
   }
+  ordered_instructions_.back().SetInstructionPosition(instruction_counter_);
+
   uint32_t id = ordered_instructions_.back().id();
   if (id) {
     all_definitions_.insert(make_pair(id, &ordered_instructions_.back()));
@@ -834,4 +850,19 @@
   }
 }
 
+std::string ValidationState_t::Disassemble(const Instruction& inst) const {
+  const spv_parsed_instruction_t& c_inst(inst.c_inst());
+  return Disassemble(c_inst.words, c_inst.num_words);
+}
+
+std::string ValidationState_t::Disassemble(const uint32_t* words,
+                                           uint16_t num_words) const {
+  uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                                 SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
+
+  return spvtools::spvInstructionBinaryToText(context()->target_env, words,
+                                              num_words, words_, num_words_,
+                                              disassembly_options);
+}
+
 }  // namespace libspirv
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index 07ae829..3c57387 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -26,6 +26,7 @@
 #include "assembly_grammar.h"
 #include "decoration.h"
 #include "diagnostic.h"
+#include "disassemble.h"
 #include "enum_set.h"
 #include "latest_version_spirv_header.h"
 #include "spirv-tools/libspirv.h"
@@ -76,7 +77,8 @@
   };
 
   ValidationState_t(const spv_const_context context,
-                    const spv_const_validator_options opt);
+                    const spv_const_validator_options opt,
+                    const uint32_t* words, const size_t num_words);
 
   /// Returns the context
   spv_const_context context() const { return context_; }
@@ -136,6 +138,8 @@
   bool IsOpcodeInCurrentLayoutSection(SpvOp op);
 
   libspirv::DiagnosticStream diag(spv_result_t error_code) const;
+  libspirv::DiagnosticStream diag(spv_result_t error_code,
+                                  int instruction_counter) const;
 
   /// Returns the function states
   std::deque<Function>& functions();
@@ -467,6 +471,12 @@
   // Returns tuple <is_int32, is_const_int32, value>.
   std::tuple<bool, bool, uint32_t> EvalInt32IfConst(uint32_t id);
 
+  // Returns the disassembly string for the given instruction.
+  std::string Disassemble(const Instruction& inst) const;
+
+  // Returns the disassembly string for the given instruction.
+  std::string Disassemble(const uint32_t* words, uint16_t num_words) const;
+
  private:
   ValidationState_t(const ValidationState_t&);
 
@@ -475,6 +485,10 @@
   /// Stores the Validator command line options. Must be a valid options object.
   const spv_const_validator_options options_;
 
+  /// The SPIR-V binary module we're validating.
+  const uint32_t* words_;
+  const size_t num_words_;
+
   /// Tracks the number of instructions evaluated by the validator
   int instruction_counter_;
 
diff --git a/source/validate.cpp b/source/validate.cpp
index cf07aef..73894fa 100644
--- a/source/validate.cpp
+++ b/source/validate.cpp
@@ -250,20 +250,20 @@
   spv_endianness_t endian;
   spv_position_t position = {};
   if (spvBinaryEndianness(binary.get(), &endian)) {
-    return libspirv::DiagnosticStream(position, context.consumer,
+    return libspirv::DiagnosticStream(position, context.consumer, "",
                                       SPV_ERROR_INVALID_BINARY)
            << "Invalid SPIR-V magic number.";
   }
 
   spv_header_t header;
   if (spvBinaryHeaderGet(binary.get(), endian, &header)) {
-    return libspirv::DiagnosticStream(position, context.consumer,
+    return libspirv::DiagnosticStream(position, context.consumer, "",
                                       SPV_ERROR_INVALID_BINARY)
            << "Invalid SPIR-V header.";
   }
 
   if (header.version > spvVersionForTargetEnv(context.target_env)) {
-    return libspirv::DiagnosticStream(position, context.consumer,
+    return libspirv::DiagnosticStream(position, context.consumer, "",
                                       SPV_ERROR_WRONG_VERSION)
            << "Invalid SPIR-V binary version "
            << SPV_SPIRV_VERSION_MAJOR_PART(header.version) << "."
@@ -391,7 +391,7 @@
   spv_validator_options default_options = spvValidatorOptionsCreate();
 
   // Create the ValidationState using the context and default options.
-  ValidationState_t vstate(&hijack_context, default_options);
+  ValidationState_t vstate(&hijack_context, default_options, words, num_words);
 
   spv_result_t result = ValidateBinaryUsingContextAndValidationState(
       hijack_context, words, num_words, pDiagnostic, &vstate);
@@ -411,7 +411,8 @@
   }
 
   // Create the ValidationState using the context.
-  ValidationState_t vstate(&hijack_context, options);
+  ValidationState_t vstate(&hijack_context, options, binary->code,
+                           binary->wordCount);
 
   return ValidateBinaryUsingContextAndValidationState(
       hijack_context, binary->code, binary->wordCount, pDiagnostic, &vstate);
@@ -429,7 +430,8 @@
     libspirv::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic);
   }
 
-  vstate->reset(new ValidationState_t(&hijack_context, options));
+  vstate->reset(
+      new ValidationState_t(&hijack_context, options, words, num_words));
 
   return ValidateBinaryUsingContextAndValidationState(
       hijack_context, words, num_words, pDiagnostic, vstate->get());
diff --git a/source/validate_cfg.cpp b/source/validate_cfg.cpp
index 9141ebe..f63c489 100644
--- a/source/validate_cfg.cpp
+++ b/source/validate_cfg.cpp
@@ -75,7 +75,8 @@
 
 spv_result_t FirstBlockAssert(ValidationState_t& _, uint32_t target) {
   if (_.current_function().IsFirstBlock(target)) {
-    return _.diag(SPV_ERROR_INVALID_CFG)
+    return _.diag(SPV_ERROR_INVALID_CFG,
+                  _.FindDef(_.current_function().id())->InstructionPosition())
            << "First block " << _.getIdName(target) << " of function "
            << _.getIdName(_.current_function().id()) << " is targeted by block "
            << _.getIdName(_.current_function().current_block()->id());
@@ -85,7 +86,8 @@
 
 spv_result_t MergeBlockAssert(ValidationState_t& _, uint32_t merge_block) {
   if (_.current_function().IsBlockType(merge_block, kBlockTypeMerge)) {
-    return _.diag(SPV_ERROR_INVALID_CFG)
+    return _.diag(SPV_ERROR_INVALID_CFG,
+                  _.FindDef(_.current_function().id())->InstructionPosition())
            << "Block " << _.getIdName(merge_block)
            << " is already a merge block for another header";
   }
@@ -342,7 +344,8 @@
     auto loop_header_id = loop_header->id();
     auto num_latch_blocks = loop_latch_blocks[loop_header_id].size();
     if (num_latch_blocks != 1) {
-      return _.diag(SPV_ERROR_INVALID_CFG)
+      return _.diag(SPV_ERROR_INVALID_CFG,
+                    _.FindDef(loop_header_id)->InstructionPosition())
              << "Loop header " << _.getIdName(loop_header_id)
              << " is targeted by " << num_latch_blocks
              << " back-edge blocks but the standard requires exactly one";
@@ -358,7 +361,8 @@
       string construct_name, header_name, exit_name;
       tie(construct_name, header_name, exit_name) =
           ConstructNames(construct.type());
-      return _.diag(SPV_ERROR_INTERNAL)
+      return _.diag(SPV_ERROR_INTERNAL,
+                    _.FindDef(header->id())->InstructionPosition())
              << "Construct " + construct_name + " with " + header_name + " " +
                     _.getIdName(header->id()) + " does not have a " +
                     exit_name + ". This may be a bug in the validator.";
@@ -368,25 +372,31 @@
     // header.
     if (merge && merge->reachable()) {
       if (!header->dominates(*merge)) {
-        return _.diag(SPV_ERROR_INVALID_CFG) << ConstructErrorString(
-                   construct, _.getIdName(header->id()),
-                   _.getIdName(merge->id()), "does not dominate");
+        return _.diag(SPV_ERROR_INVALID_CFG,
+                      _.FindDef(merge->id())->InstructionPosition())
+               << ConstructErrorString(construct, _.getIdName(header->id()),
+                                       _.getIdName(merge->id()),
+                                       "does not dominate");
       }
       // If it's really a merge block for a selection or loop, then it must be
       // *strictly* dominated by the header.
       if (construct.ExitBlockIsMergeBlock() && (header == merge)) {
-        return _.diag(SPV_ERROR_INVALID_CFG) << ConstructErrorString(
-                   construct, _.getIdName(header->id()),
-                   _.getIdName(merge->id()), "does not strictly dominate");
+        return _.diag(SPV_ERROR_INVALID_CFG,
+                      _.FindDef(merge->id())->InstructionPosition())
+               << ConstructErrorString(construct, _.getIdName(header->id()),
+                                       _.getIdName(merge->id()),
+                                       "does not strictly dominate");
       }
     }
     // Check post-dominance for continue constructs.  But dominance and
     // post-dominance only make sense when the construct is reachable.
     if (header->reachable() && construct.type() == ConstructType::kContinue) {
       if (!merge->postdominates(*header)) {
-        return _.diag(SPV_ERROR_INVALID_CFG) << ConstructErrorString(
-                   construct, _.getIdName(header->id()),
-                   _.getIdName(merge->id()), "is not post dominated by");
+        return _.diag(SPV_ERROR_INVALID_CFG,
+                      _.FindDef(merge->id())->InstructionPosition())
+               << ConstructErrorString(construct, _.getIdName(header->id()),
+                                       _.getIdName(merge->id()),
+                                       "is not post dominated by");
       }
     }
 
@@ -434,7 +444,8 @@
         }
         first = false;
       }
-      return _.diag(SPV_ERROR_INVALID_CFG)
+      return _.diag(SPV_ERROR_INVALID_CFG,
+                    _.FindDef(function.id())->InstructionPosition())
              << "Block(s) " << undef_blocks << "}"
              << " are referenced but not defined in function "
              << _.getIdName(function.id());
@@ -493,7 +504,8 @@
         if (auto idom = (*block)->immediate_dominator()) {
           if (idom != function.pseudo_entry_block() &&
               block == std::find(begin(blocks), block, idom)) {
-            return _.diag(SPV_ERROR_INVALID_CFG)
+            return _.diag(SPV_ERROR_INVALID_CFG,
+                          _.FindDef(idom->id())->InstructionPosition())
                    << "Block " << _.getIdName((*block)->id())
                    << " appears in the binary before its dominator "
                    << _.getIdName(idom->id());
@@ -508,7 +520,8 @@
         for (auto block = begin(blocks); block != end(blocks); ++block) {
           if (function.GetBlockDepth(*block) >
               control_flow_nesting_depth_limit) {
-            return _.diag(SPV_ERROR_INVALID_CFG)
+            return _.diag(SPV_ERROR_INVALID_CFG,
+                          _.FindDef((*block)->id())->InstructionPosition())
                    << "Maximum Control Flow nesting depth exceeded.";
           }
         }
@@ -577,7 +590,8 @@
       const Instruction* return_type_inst = _.FindDef(return_type);
       assert(return_type_inst);
       if (return_type_inst->opcode() != SpvOpTypeVoid)
-        return _.diag(SPV_ERROR_INVALID_CFG)
+        return _.diag(SPV_ERROR_INVALID_CFG,
+                      return_type_inst->InstructionPosition())
                << "OpReturn can only be called from a function with void "
                << "return type.";
     }
diff --git a/source/validate_id.cpp b/source/validate_id.cpp
index e41ce6e..ed4a9a9 100644
--- a/source/validate_id.cpp
+++ b/source/validate_id.cpp
@@ -106,10 +106,15 @@
       const vector<Decoration>& type2_decorations) const;
 };
 
-#define DIAG(INDEX)                                                \
-  position->index += INDEX;                                        \
-  libspirv::DiagnosticStream helper(*position, consumer_,          \
-                                    SPV_ERROR_INVALID_DIAGNOSTIC); \
+#define DIAG(inst)                                                          \
+  position->index = inst ? inst->InstructionPosition() : -1;                \
+  std::string disassembly;                                                  \
+  if (inst) {                                                               \
+    disassembly = module_.Disassemble(                                      \
+        inst->words().data(), static_cast<uint16_t>(inst->words().size())); \
+  }                                                                         \
+  libspirv::DiagnosticStream helper(*position, consumer_, disassembly,      \
+                                    SPV_ERROR_INVALID_DIAGNOSTIC);          \
   helper
 
 #if 0
@@ -127,19 +132,20 @@
   auto typeIndex = 1;
   auto type = module_.FindDef(inst->words[typeIndex]);
   if (!type || SpvOpTypeStruct != type->opcode()) {
-    DIAG(typeIndex) << "OpMemberName Type <id> '"
-                    << module_.getIdName(inst->words[typeIndex])
-                    << "' is not a struct type.";
+    DIAG(type) << "OpMemberName Type <id> '"
+               << module_.getIdName(inst->words[typeIndex])
+               << "' is not a struct type.";
     return false;
   }
   auto memberIndex = 2;
   auto member = inst->words[memberIndex];
   auto memberCount = (uint32_t)(type->words().size() - 2);
   if (memberCount <= member) {
-    DIAG(memberIndex) << "OpMemberName Member <id> '"
-                      << module_.getIdName(inst->words[memberIndex])
-                      << "' index is larger than Type <id> '"
-                      << module_.getIdName(type->id()) << "'s member count.";
+    DIAG(module_.FindDef(member))
+        << "OpMemberName Member <id> '"
+        << module_.getIdName(inst->words[memberIndex])
+        << "' index is larger than Type <id> '" << module_.getIdName(type->id())
+        << "'s member count.";
     return false;
   }
   return true;
@@ -151,9 +157,9 @@
   auto fileIndex = 1;
   auto file = module_.FindDef(inst->words[fileIndex]);
   if (!file || SpvOpString != file->opcode()) {
-    DIAG(fileIndex) << "OpLine Target <id> '"
-                    << module_.getIdName(inst->words[fileIndex])
-                    << "' is not an OpString.";
+    DIAG(file) << "OpLine Target <id> '"
+               << module_.getIdName(inst->words[fileIndex])
+               << "' is not an OpString.";
     return false;
   }
   return true;
@@ -168,9 +174,9 @@
     auto targetIndex = 1;
     auto target = module_.FindDef(inst->words[targetIndex]);
     if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) {
-      DIAG(targetIndex) << "OpDecorate SpectId decoration target <id> '"
-                        << module_.getIdName(inst->words[decorationIndex])
-                        << "' is not a scalar specialization constant.";
+      DIAG(target) << "OpDecorate SpectId decoration target <id> '"
+                   << module_.getIdName(inst->words[decorationIndex])
+                   << "' is not a scalar specialization constant.";
       return false;
     }
   }
@@ -184,21 +190,21 @@
   auto structTypeIndex = 1;
   auto structType = module_.FindDef(inst->words[structTypeIndex]);
   if (!structType || SpvOpTypeStruct != structType->opcode()) {
-    DIAG(structTypeIndex) << "OpMemberDecorate Structure type <id> '"
-                          << module_.getIdName(inst->words[structTypeIndex])
-                          << "' is not a struct type.";
+    DIAG(structType) << "OpMemberDecorate Structure type <id> '"
+                     << module_.getIdName(inst->words[structTypeIndex])
+                     << "' is not a struct type.";
     return false;
   }
   auto memberIndex = 2;
   auto member = inst->words[memberIndex];
   auto memberCount = static_cast<uint32_t>(structType->words().size() - 2);
   if (memberCount < member) {
-    DIAG(memberIndex) << "Index " << member
-                      << " provided in OpMemberDecorate for struct <id> "
-                      << module_.getIdName(inst->words[structTypeIndex])
-                      << " is out of bounds. The structure has " << memberCount
-                      << " members. Largest valid index is " << memberCount - 1
-                      << ".";
+    DIAG(structType) << "Index " << member
+                     << " provided in OpMemberDecorate for struct <id> "
+                     << module_.getIdName(inst->words[structTypeIndex])
+                     << " is out of bounds. The structure has " << memberCount
+                     << " members. Largest valid index is " << memberCount - 1
+                     << ".";
     return false;
   }
   return true;
@@ -215,9 +221,9 @@
     if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate &&
         use->opcode() != SpvOpGroupMemberDecorate &&
         use->opcode() != SpvOpName) {
-      DIAG(decorationGroupIndex) << "Result id of OpDecorationGroup can only "
-                                 << "be targeted by OpName, OpGroupDecorate, "
-                                 << "OpDecorate, and OpGroupMemberDecorate";
+      DIAG(decorationGroup) << "Result id of OpDecorationGroup can only "
+                            << "be targeted by OpName, OpGroupDecorate, "
+                            << "OpDecorate, and OpGroupMemberDecorate";
       return false;
     }
   }
@@ -230,10 +236,10 @@
   auto decorationGroupIndex = 1;
   auto decorationGroup = module_.FindDef(inst->words[decorationGroupIndex]);
   if (!decorationGroup || SpvOpDecorationGroup != decorationGroup->opcode()) {
-    DIAG(decorationGroupIndex)
-        << "OpGroupDecorate Decoration group <id> '"
-        << module_.getIdName(inst->words[decorationGroupIndex])
-        << "' is not a decoration group.";
+    DIAG(decorationGroup) << "OpGroupDecorate Decoration group <id> '"
+                          << module_.getIdName(
+                                 inst->words[decorationGroupIndex])
+                          << "' is not a decoration group.";
     return false;
   }
   return true;
@@ -245,10 +251,10 @@
   auto decorationGroupIndex = 1;
   auto decorationGroup = module_.FindDef(inst->words[decorationGroupIndex]);
   if (!decorationGroup || SpvOpDecorationGroup != decorationGroup->opcode()) {
-    DIAG(decorationGroupIndex)
-        << "OpGroupMemberDecorate Decoration group <id> '"
-        << module_.getIdName(inst->words[decorationGroupIndex])
-        << "' is not a decoration group.";
+    DIAG(decorationGroup) << "OpGroupMemberDecorate Decoration group <id> '"
+                          << module_.getIdName(
+                                 inst->words[decorationGroupIndex])
+                          << "' is not a decoration group.";
     return false;
   }
   // Grammar checks ensures that the number of arguments to this instruction
@@ -258,19 +264,21 @@
     const uint32_t index = inst->words[i + 1];
     auto struct_instr = module_.FindDef(struct_id);
     if (!struct_instr || SpvOpTypeStruct != struct_instr->opcode()) {
-      DIAG(i) << "OpGroupMemberDecorate Structure type <id> '"
-              << module_.getIdName(struct_id) << "' is not a struct type.";
+      DIAG(struct_instr) << "OpGroupMemberDecorate Structure type <id> '"
+                         << module_.getIdName(struct_id)
+                         << "' is not a struct type.";
       return false;
     }
     const uint32_t num_struct_members =
         static_cast<uint32_t>(struct_instr->words().size() - 2);
     if (index >= num_struct_members) {
-      DIAG(i) << "Index " << index
-              << " provided in OpGroupMemberDecorate for struct <id> "
-              << module_.getIdName(struct_id)
-              << " is out of bounds. The structure has " << num_struct_members
-              << " members. Largest valid index is " << num_struct_members - 1
-              << ".";
+      DIAG(struct_instr)
+          << "Index " << index
+          << " provided in OpGroupMemberDecorate for struct <id> "
+          << module_.getIdName(struct_id)
+          << " is out of bounds. The structure has " << num_struct_members
+          << " members. Largest valid index is " << num_struct_members - 1
+          << ".";
       return false;
     }
   }
@@ -289,9 +297,9 @@
   auto entryPointIndex = 2;
   auto entryPoint = module_.FindDef(inst->words[entryPointIndex]);
   if (!entryPoint || SpvOpFunction != entryPoint->opcode()) {
-    DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
-                          << module_.getIdName(inst->words[entryPointIndex])
-                          << "' is not a function.";
+    DIAG(entryPoint) << "OpEntryPoint Entry Point <id> '"
+                     << module_.getIdName(inst->words[entryPointIndex])
+                     << "' is not a function.";
     return false;
   }
   // don't check kernel function signatures
@@ -301,18 +309,18 @@
     // to change
     auto entryPointType = module_.FindDef(entryPoint->words()[4]);
     if (!entryPointType || 3 != entryPointType->words().size()) {
-      DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
-                            << module_.getIdName(inst->words[entryPointIndex])
-                            << "'s function parameter count is not zero.";
+      DIAG(entryPoint) << "OpEntryPoint Entry Point <id> '"
+                       << module_.getIdName(inst->words[entryPointIndex])
+                       << "'s function parameter count is not zero.";
       return false;
     }
   }
 
   auto returnType = module_.FindDef(entryPoint->type_id());
   if (!returnType || SpvOpTypeVoid != returnType->opcode()) {
-    DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
-                          << module_.getIdName(inst->words[entryPointIndex])
-                          << "'s function return type is not void.";
+    DIAG(entryPoint) << "OpEntryPoint Entry Point <id> '"
+                     << module_.getIdName(inst->words[entryPointIndex])
+                     << "'s function return type is not void.";
     return false;
   }
   return true;
@@ -326,10 +334,11 @@
   auto found =
       std::find(entry_points_.cbegin(), entry_points_.cend(), entryPointID);
   if (found == entry_points_.cend()) {
-    DIAG(entryPointIndex) << "OpExecutionMode Entry Point <id> '"
-                          << module_.getIdName(inst->words[entryPointIndex])
-                          << "' is not the Entry Point "
-                             "operand of an OpEntryPoint.";
+    DIAG(module_.FindDef(entryPointID))
+        << "OpExecutionMode Entry Point <id> '"
+        << module_.getIdName(inst->words[entryPointIndex])
+        << "' is not the Entry Point "
+           "operand of an OpEntryPoint.";
     return false;
   }
   return true;
@@ -341,9 +350,9 @@
   auto componentIndex = 2;
   auto componentType = module_.FindDef(inst->words[componentIndex]);
   if (!componentType || !spvOpcodeIsScalarType(componentType->opcode())) {
-    DIAG(componentIndex) << "OpTypeVector Component Type <id> '"
-                         << module_.getIdName(inst->words[componentIndex])
-                         << "' is not a scalar type.";
+    DIAG(componentType) << "OpTypeVector Component Type <id> '"
+                        << module_.getIdName(inst->words[componentIndex])
+                        << "' is not a scalar type.";
     return false;
   }
   return true;
@@ -355,9 +364,9 @@
   auto columnTypeIndex = 2;
   auto columnType = module_.FindDef(inst->words[columnTypeIndex]);
   if (!columnType || SpvOpTypeVector != columnType->opcode()) {
-    DIAG(columnTypeIndex) << "OpTypeMatrix Column Type <id> '"
-                          << module_.getIdName(inst->words[columnTypeIndex])
-                          << "' is not a vector.";
+    DIAG(columnType) << "OpTypeMatrix Column Type <id> '"
+                     << module_.getIdName(inst->words[columnTypeIndex])
+                     << "' is not a vector.";
     return false;
   }
   return true;
@@ -396,17 +405,17 @@
   auto elementTypeIndex = 2;
   auto elementType = module_.FindDef(inst->words[elementTypeIndex]);
   if (!elementType || !spvOpcodeGeneratesType(elementType->opcode())) {
-    DIAG(elementTypeIndex) << "OpTypeArray Element Type <id> '"
-                           << module_.getIdName(inst->words[elementTypeIndex])
-                           << "' is not a type.";
+    DIAG(elementType) << "OpTypeArray Element Type <id> '"
+                      << module_.getIdName(inst->words[elementTypeIndex])
+                      << "' is not a type.";
     return false;
   }
   auto lengthIndex = 3;
   auto length = module_.FindDef(inst->words[lengthIndex]);
   if (!length || !spvOpcodeIsConstant(length->opcode())) {
-    DIAG(lengthIndex) << "OpTypeArray Length <id> '"
-                      << module_.getIdName(inst->words[lengthIndex])
-                      << "' is not a scalar constant type.";
+    DIAG(length) << "OpTypeArray Length <id> '"
+                 << module_.getIdName(inst->words[lengthIndex])
+                 << "' is not a scalar constant type.";
     return false;
   }
 
@@ -415,9 +424,9 @@
   auto constResultTypeIndex = 1;
   auto constResultType = module_.FindDef(constInst[constResultTypeIndex]);
   if (!constResultType || SpvOpTypeInt != constResultType->opcode()) {
-    DIAG(lengthIndex) << "OpTypeArray Length <id> '"
-                      << module_.getIdName(inst->words[lengthIndex])
-                      << "' is not a constant integer type.";
+    DIAG(length) << "OpTypeArray Length <id> '"
+                 << module_.getIdName(inst->words[lengthIndex])
+                 << "' is not a constant integer type.";
     return false;
   }
 
@@ -427,9 +436,9 @@
       if (aboveZero(length->words(), constResultType->words())) break;
     // Else fall through!
     case SpvOpConstantNull: {
-      DIAG(lengthIndex) << "OpTypeArray Length <id> '"
-                        << module_.getIdName(inst->words[lengthIndex])
-                        << "' default value must be at least 1.";
+      DIAG(length) << "OpTypeArray Length <id> '"
+                   << module_.getIdName(inst->words[lengthIndex])
+                   << "' default value must be at least 1.";
       return false;
     }
     case SpvOpSpecConstantOp:
@@ -447,9 +456,9 @@
   auto elementTypeIndex = 2;
   auto elementType = module_.FindDef(inst->words[elementTypeIndex]);
   if (!elementType || !spvOpcodeGeneratesType(elementType->opcode())) {
-    DIAG(elementTypeIndex) << "OpTypeRuntimeArray Element Type <id> '"
-                           << module_.getIdName(inst->words[elementTypeIndex])
-                           << "' is not a type.";
+    DIAG(elementType) << "OpTypeRuntimeArray Element Type <id> '"
+                      << module_.getIdName(inst->words[elementTypeIndex])
+                      << "' is not a type.";
     return false;
   }
   return true;
@@ -460,19 +469,20 @@
                                        const spv_opcode_desc) {
   ValidationState_t& vstate = const_cast<ValidationState_t&>(module_);
   const uint32_t struct_id = inst->words[1];
+  auto structType = module_.FindDef(struct_id);
   for (size_t memberTypeIndex = 2; memberTypeIndex < inst->words.size();
        ++memberTypeIndex) {
     auto memberTypeId = inst->words[memberTypeIndex];
     auto memberType = module_.FindDef(memberTypeId);
     if (!memberType || !spvOpcodeGeneratesType(memberType->opcode())) {
-      DIAG(memberTypeIndex) << "OpTypeStruct Member Type <id> '"
-                            << module_.getIdName(inst->words[memberTypeIndex])
-                            << "' is not a type.";
+      DIAG(memberType) << "OpTypeStruct Member Type <id> '"
+                       << module_.getIdName(inst->words[memberTypeIndex])
+                       << "' is not a type.";
       return false;
     }
     if (SpvOpTypeStruct == memberType->opcode() &&
         module_.IsStructTypeWithBuiltInMember(memberTypeId)) {
-      DIAG(memberTypeIndex)
+      DIAG(memberType)
           << "Structure <id> " << module_.getIdName(memberTypeId)
           << " contains members with BuiltIn decoration. Therefore this "
              "structure may not be contained as a member of another structure "
@@ -483,8 +493,8 @@
     }
     if (module_.IsForwardPointer(memberTypeId)) {
       if (memberType->opcode() != SpvOpTypePointer) {
-        DIAG(memberTypeIndex) << "Found a forward reference to a non-pointer "
-                                 "type in OpTypeStruct instruction.";
+        DIAG(memberType) << "Found a forward reference to a non-pointer "
+                            "type in OpTypeStruct instruction.";
         return false;
       }
       // If we're dealing with a forward pointer:
@@ -493,7 +503,7 @@
       auto typePointingTo = module_.FindDef(memberType->words()[3]);
       if (typePointingTo && typePointingTo->opcode() != SpvOpTypeStruct) {
         // Forward declared operands of a struct may only point to a struct.
-        DIAG(memberTypeIndex)
+        DIAG(memberType)
             << "A forward reference operand in an OpTypeStruct must be an "
                "OpTypePointer that points to an OpTypeStruct. "
                "Found OpTypePointer that points to Op"
@@ -513,7 +523,7 @@
   int num_struct_members = static_cast<int>(inst->words.size() - 2);
   int num_builtin_members = static_cast<int>(built_in_members.size());
   if (num_builtin_members > 0 && num_builtin_members != num_struct_members) {
-    DIAG(0)
+    DIAG(structType)
         << "When BuiltIn decoration is applied to a structure-type member, "
            "all members of that structure type must also be decorated with "
            "BuiltIn (No allowed mixing of built-in variables and "
@@ -533,9 +543,9 @@
   auto typeIndex = 3;
   auto type = module_.FindDef(inst->words[typeIndex]);
   if (!type || !spvOpcodeGeneratesType(type->opcode())) {
-    DIAG(typeIndex) << "OpTypePointer Type <id> '"
-                    << module_.getIdName(inst->words[typeIndex])
-                    << "' is not a type.";
+    DIAG(type) << "OpTypePointer Type <id> '"
+               << module_.getIdName(inst->words[typeIndex])
+               << "' is not a type.";
     return false;
   }
   return true;
@@ -547,9 +557,9 @@
   auto returnTypeIndex = 2;
   auto returnType = module_.FindDef(inst->words[returnTypeIndex]);
   if (!returnType || !spvOpcodeGeneratesType(returnType->opcode())) {
-    DIAG(returnTypeIndex) << "OpTypeFunction Return Type <id> '"
-                          << module_.getIdName(inst->words[returnTypeIndex])
-                          << "' is not a type.";
+    DIAG(returnType) << "OpTypeFunction Return Type <id> '"
+                     << module_.getIdName(inst->words[returnTypeIndex])
+                     << "' is not a type.";
     return false;
   }
   size_t num_args = 0;
@@ -557,20 +567,20 @@
        ++paramTypeIndex, ++num_args) {
     auto paramType = module_.FindDef(inst->words[paramTypeIndex]);
     if (!paramType || !spvOpcodeGeneratesType(paramType->opcode())) {
-      DIAG(paramTypeIndex) << "OpTypeFunction Parameter Type <id> '"
-                           << module_.getIdName(inst->words[paramTypeIndex])
-                           << "' is not a type.";
+      DIAG(paramType) << "OpTypeFunction Parameter Type <id> '"
+                      << module_.getIdName(inst->words[paramTypeIndex])
+                      << "' is not a type.";
       return false;
     }
   }
   const uint32_t num_function_args_limit =
       module_.options()->universal_limits_.max_function_args;
   if (num_args > num_function_args_limit) {
-    DIAG(returnTypeIndex) << "OpTypeFunction may not take more than "
-                          << num_function_args_limit
-                          << " arguments. OpTypeFunction <id> '"
-                          << module_.getIdName(inst->words[1]) << "' has "
-                          << num_args << " arguments.";
+    DIAG(returnType) << "OpTypeFunction may not take more than "
+                     << num_function_args_limit
+                     << " arguments. OpTypeFunction <id> '"
+                     << module_.getIdName(inst->words[1]) << "' has "
+                     << num_args << " arguments.";
     return false;
   }
   return true;
@@ -589,9 +599,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || SpvOpTypeBool != resultType->opcode()) {
-    DIAG(resultTypeIndex) << "OpConstantTrue Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not a boolean type.";
+    DIAG(resultType) << "OpConstantTrue Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not a boolean type.";
     return false;
   }
   return true;
@@ -603,9 +613,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || SpvOpTypeBool != resultType->opcode()) {
-    DIAG(resultTypeIndex) << "OpConstantFalse Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not a boolean type.";
+    DIAG(resultType) << "OpConstantFalse Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not a boolean type.";
     return false;
   }
   return true;
@@ -617,9 +627,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || !spvOpcodeIsComposite(resultType->opcode())) {
-    DIAG(resultTypeIndex) << "OpConstantComposite Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not a composite type.";
+    DIAG(resultType) << "OpConstantComposite Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not a composite type.";
     return false;
   }
 
@@ -629,7 +639,7 @@
       auto componentCount = resultType->words()[3];
       if (componentCount != constituentCount) {
         // TODO: Output ID's on diagnostic
-        DIAG(inst->words.size() - 1)
+        DIAG(module_.FindDef(inst->words.back()))
             << "OpConstantComposite Constituent <id> count does not match "
                "Result Type <id> '"
             << module_.getIdName(resultType->id())
@@ -643,21 +653,19 @@
         auto constituent = module_.FindDef(inst->words[constituentIndex]);
         if (!constituent ||
             !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          DIAG(constituentIndex)
-              << "OpConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' is not a constant or undef.";
+          DIAG(constituent) << "OpConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' is not a constant or undef.";
           return false;
         }
         auto constituentResultType = module_.FindDef(constituent->type_id());
         if (!constituentResultType ||
             componentType->opcode() != constituentResultType->opcode()) {
-          DIAG(constituentIndex)
-              << "OpConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "'s type does not match Result Type <id> '"
-              << module_.getIdName(resultType->id())
-              << "'s vector element type.";
+          DIAG(constituent) << "OpConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "'s type does not match Result Type <id> '"
+                            << module_.getIdName(resultType->id())
+                            << "'s vector element type.";
           return false;
         }
       }
@@ -666,7 +674,7 @@
       auto columnCount = resultType->words()[3];
       if (columnCount != constituentCount) {
         // TODO: Output ID's on diagnostic
-        DIAG(inst->words.size() - 1)
+        DIAG(module_.FindDef(inst->words.back()))
             << "OpConstantComposite Constituent <id> count does not match "
                "Result Type <id> '"
             << module_.getIdName(resultType->id()) << "'s matrix column count.";
@@ -686,27 +694,25 @@
                               SpvOpUndef == constituent->opcode())) {
           // The message says "... or undef" because the spec does not say
           // undef is a constant.
-          DIAG(constituentIndex)
-              << "OpConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' is not a constant composite or undef.";
+          DIAG(constituent) << "OpConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' is not a constant composite or undef.";
           return false;
         }
         auto vector = module_.FindDef(constituent->type_id());
         assert(vector);
         if (columnType->opcode() != vector->opcode()) {
-          DIAG(constituentIndex)
-              << "OpConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' type does not match Result Type <id> '"
-              << module_.getIdName(resultType->id())
-              << "'s matrix column type.";
+          DIAG(constituent) << "OpConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' type does not match Result Type <id> '"
+                            << module_.getIdName(resultType->id())
+                            << "'s matrix column type.";
           return false;
         }
         auto vectorComponentType = module_.FindDef(vector->words()[2]);
         assert(vectorComponentType);
         if (componentType->id() != vectorComponentType->id()) {
-          DIAG(constituentIndex)
+          DIAG(constituent)
               << "OpConstantComposite Constituent <id> '"
               << module_.getIdName(inst->words[constituentIndex])
               << "' component type does not match Result Type <id> '"
@@ -715,7 +721,7 @@
           return false;
         }
         if (componentCount != vector->words()[3]) {
-          DIAG(constituentIndex)
+          DIAG(constituent)
               << "OpConstantComposite Constituent <id> '"
               << module_.getIdName(inst->words[constituentIndex])
               << "' vector component count does not match Result Type <id> '"
@@ -731,7 +737,7 @@
       auto length = module_.FindDef(resultType->words()[3]);
       assert(length);
       if (length->words()[3] != constituentCount) {
-        DIAG(inst->words.size() - 1)
+        DIAG(module_.FindDef(inst->words.back()))
             << "OpConstantComposite Constituent count does not match "
                "Result Type <id> '"
             << module_.getIdName(resultType->id()) << "'s array length.";
@@ -742,21 +748,19 @@
         auto constituent = module_.FindDef(inst->words[constituentIndex]);
         if (!constituent ||
             !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          DIAG(constituentIndex)
-              << "OpConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' is not a constant or undef.";
+          DIAG(constituent) << "OpConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' is not a constant or undef.";
           return false;
         }
         auto constituentType = module_.FindDef(constituent->type_id());
         assert(constituentType);
         if (elementType->id() != constituentType->id()) {
-          DIAG(constituentIndex)
-              << "OpConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "'s type does not match Result Type <id> '"
-              << module_.getIdName(resultType->id())
-              << "'s array element type.";
+          DIAG(constituent) << "OpConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "'s type does not match Result Type <id> '"
+                            << module_.getIdName(resultType->id())
+                            << "'s array element type.";
           return false;
         }
       }
@@ -764,11 +768,11 @@
     case SpvOpTypeStruct: {
       auto memberCount = resultType->words().size() - 2;
       if (memberCount != constituentCount) {
-        DIAG(resultTypeIndex)
-            << "OpConstantComposite Constituent <id> '"
-            << module_.getIdName(inst->words[resultTypeIndex])
-            << "' count does not match Result Type <id> '"
-            << module_.getIdName(resultType->id()) << "'s struct member count.";
+        DIAG(resultType) << "OpConstantComposite Constituent <id> '"
+                         << module_.getIdName(inst->words[resultTypeIndex])
+                         << "' count does not match Result Type <id> '"
+                         << module_.getIdName(resultType->id())
+                         << "'s struct member count.";
         return false;
       }
       for (uint32_t constituentIndex = 3, memberIndex = 2;
@@ -777,10 +781,9 @@
         auto constituent = module_.FindDef(inst->words[constituentIndex]);
         if (!constituent ||
             !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          DIAG(constituentIndex)
-              << "OpConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' is not a constant or undef.";
+          DIAG(constituent) << "OpConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' is not a constant or undef.";
           return false;
         }
         auto constituentType = module_.FindDef(constituent->type_id());
@@ -789,7 +792,7 @@
         auto memberType = module_.FindDef(resultType->words()[memberIndex]);
         assert(memberType);
         if (memberType->id() != constituentType->id()) {
-          DIAG(constituentIndex)
+          DIAG(constituent)
               << "OpConstantComposite Constituent <id> '"
               << module_.getIdName(inst->words[constituentIndex])
               << "' type does not match the Result Type <id> '"
@@ -809,9 +812,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || SpvOpTypeSampler != resultType->opcode()) {
-    DIAG(resultTypeIndex) << "OpConstantSampler Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not a sampler type.";
+    DIAG(resultType) << "OpConstantSampler Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not a sampler type.";
     return false;
   }
   return true;
@@ -860,9 +863,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || !IsTypeNullable(resultType->words(), module_)) {
-    DIAG(resultTypeIndex) << "OpConstantNull Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' cannot have a null value.";
+    DIAG(resultType) << "OpConstantNull Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' cannot have a null value.";
     return false;
   }
   return true;
@@ -874,9 +877,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || SpvOpTypeBool != resultType->opcode()) {
-    DIAG(resultTypeIndex) << "OpSpecConstantTrue Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not a boolean type.";
+    DIAG(resultType) << "OpSpecConstantTrue Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not a boolean type.";
     return false;
   }
   return true;
@@ -888,9 +891,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || SpvOpTypeBool != resultType->opcode()) {
-    DIAG(resultTypeIndex) << "OpSpecConstantFalse Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not a boolean type.";
+    DIAG(resultType) << "OpSpecConstantFalse Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not a boolean type.";
     return false;
   }
   return true;
@@ -915,7 +918,7 @@
       auto consumer_instr = module_.FindDef(consumer_id);
       auto consumer_opcode = consumer_instr->opcode();
       if (consumer_instr->block() != sampledImageInstr->block()) {
-        DIAG(resultTypeIndex)
+        DIAG(sampledImageInstr)
             << "All OpSampledImage instructions must be in the same block in "
                "which their Result <id> are consumed. OpSampledImage Result "
                "Type <id> '"
@@ -931,7 +934,7 @@
       // instructions by scanning for "Sampled Image" in the operand description
       // field in the grammar file.
       if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) {
-        DIAG(resultTypeIndex)
+        DIAG(sampledImageInstr)
             << "Result <id> from OpSampledImage instruction must not appear as "
                "operands of Op"
             << spvOpcodeString(static_cast<SpvOp>(consumer_opcode)) << "."
@@ -952,9 +955,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || !spvOpcodeIsComposite(resultType->opcode())) {
-    DIAG(resultTypeIndex) << "OpSpecConstantComposite Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not a composite type.";
+    DIAG(resultType) << "OpSpecConstantComposite Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not a composite type.";
     return false;
   }
   // Validation checks differ based on the type of composite type.
@@ -969,7 +972,7 @@
     case SpvOpTypeVector: {
       auto componentCount = resultType->words()[3];
       if (componentCount != constituentCount) {
-        DIAG(inst->words.size() - 1)
+        DIAG(module_.FindDef(inst->words.back()))
             << "OpSpecConstantComposite Constituent <id> count does not match "
                "Result Type <id> '"
             << module_.getIdName(resultType->id())
@@ -983,21 +986,19 @@
         auto constituent = module_.FindDef(inst->words[constituentIndex]);
         if (!constituent ||
             !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          DIAG(constituentIndex)
-              << "OpSpecConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' is not a constant or undef.";
+          DIAG(constituent) << "OpSpecConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' is not a constant or undef.";
           return false;
         }
         auto constituentResultType = module_.FindDef(constituent->type_id());
         if (!constituentResultType ||
             componentType->opcode() != constituentResultType->opcode()) {
-          DIAG(constituentIndex)
-              << "OpSpecConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "'s type does not match Result Type <id> '"
-              << module_.getIdName(resultType->id())
-              << "'s vector element type.";
+          DIAG(constituent) << "OpSpecConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "'s type does not match Result Type <id> '"
+                            << module_.getIdName(resultType->id())
+                            << "'s vector element type.";
           return false;
         }
       }
@@ -1006,7 +1007,7 @@
     case SpvOpTypeMatrix: {
       auto columnCount = resultType->words()[3];
       if (columnCount != constituentCount) {
-        DIAG(inst->words.size() - 1)
+        DIAG(module_.FindDef(inst->words.back()))
             << "OpSpecConstantComposite Constituent <id> count does not match "
                "Result Type <id> '"
             << module_.getIdName(resultType->id()) << "'s matrix column count.";
@@ -1028,27 +1029,25 @@
                               SpvOpUndef == constituentOpCode)) {
           // The message says "... or undef" because the spec does not say
           // undef is a constant.
-          DIAG(constituentIndex)
-              << "OpSpecConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' is not a constant composite or undef.";
+          DIAG(constituent) << "OpSpecConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' is not a constant composite or undef.";
           return false;
         }
         auto vector = module_.FindDef(constituent->type_id());
         assert(vector);
         if (columnType->opcode() != vector->opcode()) {
-          DIAG(constituentIndex)
-              << "OpSpecConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' type does not match Result Type <id> '"
-              << module_.getIdName(resultType->id())
-              << "'s matrix column type.";
+          DIAG(constituent) << "OpSpecConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' type does not match Result Type <id> '"
+                            << module_.getIdName(resultType->id())
+                            << "'s matrix column type.";
           return false;
         }
         auto vectorComponentType = module_.FindDef(vector->words()[2]);
         assert(vectorComponentType);
         if (componentType->id() != vectorComponentType->id()) {
-          DIAG(constituentIndex)
+          DIAG(constituent)
               << "OpSpecConstantComposite Constituent <id> '"
               << module_.getIdName(inst->words[constituentIndex])
               << "' component type does not match Result Type <id> '"
@@ -1057,7 +1056,7 @@
           return false;
         }
         if (componentCount != vector->words()[3]) {
-          DIAG(constituentIndex)
+          DIAG(constituent)
               << "OpSpecConstantComposite Constituent <id> '"
               << module_.getIdName(inst->words[constituentIndex])
               << "' vector component count does not match Result Type <id> '"
@@ -1074,7 +1073,7 @@
       auto length = module_.FindDef(resultType->words()[3]);
       assert(length);
       if (length->words()[3] != constituentCount) {
-        DIAG(inst->words.size() - 1)
+        DIAG(module_.FindDef(inst->words.back()))
             << "OpSpecConstantComposite Constituent count does not match "
                "Result Type <id> '"
             << module_.getIdName(resultType->id()) << "'s array length.";
@@ -1085,21 +1084,19 @@
         auto constituent = module_.FindDef(inst->words[constituentIndex]);
         if (!constituent ||
             !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          DIAG(constituentIndex)
-              << "OpSpecConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' is not a constant or undef.";
+          DIAG(constituent) << "OpSpecConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' is not a constant or undef.";
           return false;
         }
         auto constituentType = module_.FindDef(constituent->type_id());
         assert(constituentType);
         if (elementType->id() != constituentType->id()) {
-          DIAG(constituentIndex)
-              << "OpSpecConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "'s type does not match Result Type <id> '"
-              << module_.getIdName(resultType->id())
-              << "'s array element type.";
+          DIAG(constituent) << "OpSpecConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "'s type does not match Result Type <id> '"
+                            << module_.getIdName(resultType->id())
+                            << "'s array element type.";
           return false;
         }
       }
@@ -1108,11 +1105,11 @@
     case SpvOpTypeStruct: {
       auto memberCount = resultType->words().size() - 2;
       if (memberCount != constituentCount) {
-        DIAG(resultTypeIndex)
-            << "OpSpecConstantComposite Constituent <id> '"
-            << module_.getIdName(inst->words[resultTypeIndex])
-            << "' count does not match Result Type <id> '"
-            << module_.getIdName(resultType->id()) << "'s struct member count.";
+        DIAG(resultType) << "OpSpecConstantComposite Constituent <id> '"
+                         << module_.getIdName(inst->words[resultTypeIndex])
+                         << "' count does not match Result Type <id> '"
+                         << module_.getIdName(resultType->id())
+                         << "'s struct member count.";
         return false;
       }
       for (uint32_t constituentIndex = 3, memberIndex = 2;
@@ -1121,10 +1118,9 @@
         auto constituent = module_.FindDef(inst->words[constituentIndex]);
         if (!constituent ||
             !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          DIAG(constituentIndex)
-              << "OpSpecConstantComposite Constituent <id> '"
-              << module_.getIdName(inst->words[constituentIndex])
-              << "' is not a constant or undef.";
+          DIAG(constituent) << "OpSpecConstantComposite Constituent <id> '"
+                            << module_.getIdName(inst->words[constituentIndex])
+                            << "' is not a constant or undef.";
           return false;
         }
         auto constituentType = module_.FindDef(constituent->type_id());
@@ -1133,7 +1129,7 @@
         auto memberType = module_.FindDef(resultType->words()[memberIndex]);
         assert(memberType);
         if (memberType->id() != constituentType->id()) {
-          DIAG(constituentIndex)
+          DIAG(constituent)
               << "OpSpecConstantComposite Constituent <id> '"
               << module_.getIdName(inst->words[constituentIndex])
               << "' type does not match the Result Type <id> '"
@@ -1159,9 +1155,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || SpvOpTypePointer != resultType->opcode()) {
-    DIAG(resultTypeIndex) << "OpVariable Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not a pointer type.";
+    DIAG(resultType) << "OpVariable Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not a pointer type.";
     return false;
   }
   const auto initialiserIndex = 4;
@@ -1174,9 +1170,9 @@
     const auto is_constant =
         initialiser && spvOpcodeIsConstant(initialiser->opcode());
     if (!initialiser || !(is_constant || is_module_scope_var)) {
-      DIAG(initialiserIndex) << "OpVariable Initializer <id> '"
-                             << module_.getIdName(inst->words[initialiserIndex])
-                             << "' is not a constant or module-scope variable.";
+      DIAG(initialiser) << "OpVariable Initializer <id> '"
+                        << module_.getIdName(inst->words[initialiserIndex])
+                        << "' is not a constant or module-scope variable.";
       return false;
     }
   }
@@ -1189,9 +1185,9 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType) {
-    DIAG(resultTypeIndex) << "OpLoad Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' is not defind.";
+    DIAG(resultType) << "OpLoad Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' is not defind.";
     return false;
   }
   const bool uses_variable_pointer =
@@ -1205,24 +1201,24 @@
          !spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
         (uses_variable_pointer &&
          !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
-    DIAG(pointerIndex) << "OpLoad Pointer <id> '"
-                       << module_.getIdName(inst->words[pointerIndex])
-                       << "' is not a logical pointer.";
+    DIAG(pointer) << "OpLoad Pointer <id> '"
+                  << module_.getIdName(inst->words[pointerIndex])
+                  << "' is not a logical pointer.";
     return false;
   }
   auto pointerType = module_.FindDef(pointer->type_id());
   if (!pointerType || pointerType->opcode() != SpvOpTypePointer) {
-    DIAG(pointerIndex) << "OpLoad type for pointer <id> '"
-                       << module_.getIdName(inst->words[pointerIndex])
-                       << "' is not a pointer type.";
+    DIAG(pointer) << "OpLoad type for pointer <id> '"
+                  << module_.getIdName(inst->words[pointerIndex])
+                  << "' is not a pointer type.";
     return false;
   }
   auto pointeeType = module_.FindDef(pointerType->words()[3]);
   if (!pointeeType || resultType->id() != pointeeType->id()) {
-    DIAG(resultTypeIndex) << "OpLoad Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' does not match Pointer <id> '"
-                          << module_.getIdName(pointer->id()) << "'s type.";
+    DIAG(resultType) << "OpLoad Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' does not match Pointer <id> '"
+                     << module_.getIdName(pointer->id()) << "'s type.";
     return false;
   }
   return true;
@@ -1242,24 +1238,24 @@
          !spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
         (uses_variable_pointer &&
          !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
-    DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                       << module_.getIdName(inst->words[pointerIndex])
-                       << "' is not a logical pointer.";
+    DIAG(pointer) << "OpStore Pointer <id> '"
+                  << module_.getIdName(inst->words[pointerIndex])
+                  << "' is not a logical pointer.";
     return false;
   }
   auto pointerType = module_.FindDef(pointer->type_id());
   if (!pointer || pointerType->opcode() != SpvOpTypePointer) {
-    DIAG(pointerIndex) << "OpStore type for pointer <id> '"
-                       << module_.getIdName(inst->words[pointerIndex])
-                       << "' is not a pointer type.";
+    DIAG(pointer) << "OpStore type for pointer <id> '"
+                  << module_.getIdName(inst->words[pointerIndex])
+                  << "' is not a pointer type.";
     return false;
   }
   auto type = module_.FindDef(pointerType->words()[3]);
   assert(type);
   if (SpvOpTypeVoid == type->opcode()) {
-    DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                       << module_.getIdName(inst->words[pointerIndex])
-                       << "'s type is void.";
+    DIAG(pointer) << "OpStore Pointer <id> '"
+                  << module_.getIdName(inst->words[pointerIndex])
+                  << "'s type is void.";
     return false;
   }
 
@@ -1269,18 +1265,18 @@
     uint32_t storageClass;
     if (!module_.GetPointerTypeInfo(pointerType->id(), &dataType,
                                     &storageClass)) {
-      DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                         << module_.getIdName(inst->words[pointerIndex])
-                         << "' is not pointer type";
+      DIAG(pointer) << "OpStore Pointer <id> '"
+                    << module_.getIdName(inst->words[pointerIndex])
+                    << "' is not pointer type";
       return false;
     }
 
     if (storageClass == SpvStorageClassUniformConstant ||
         storageClass == SpvStorageClassInput ||
         storageClass == SpvStorageClassPushConstant) {
-      DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                         << module_.getIdName(inst->words[pointerIndex])
-                         << "' storage class is read-only";
+      DIAG(pointer) << "OpStore Pointer <id> '"
+                    << module_.getIdName(inst->words[pointerIndex])
+                    << "' storage class is read-only";
       return false;
     }
   }
@@ -1288,17 +1284,17 @@
   auto objectIndex = 2;
   auto object = module_.FindDef(inst->words[objectIndex]);
   if (!object || !object->type_id()) {
-    DIAG(objectIndex) << "OpStore Object <id> '"
-                      << module_.getIdName(inst->words[objectIndex])
-                      << "' is not an object.";
+    DIAG(object) << "OpStore Object <id> '"
+                 << module_.getIdName(inst->words[objectIndex])
+                 << "' is not an object.";
     return false;
   }
   auto objectType = module_.FindDef(object->type_id());
   assert(objectType);
   if (SpvOpTypeVoid == objectType->opcode()) {
-    DIAG(objectIndex) << "OpStore Object <id> '"
-                      << module_.getIdName(inst->words[objectIndex])
-                      << "'s type is void.";
+    DIAG(object) << "OpStore Object <id> '"
+                 << module_.getIdName(inst->words[objectIndex])
+                 << "'s type is void.";
     return false;
   }
 
@@ -1306,19 +1302,19 @@
     if (!module_.options()->relax_struct_store ||
         type->opcode() != SpvOpTypeStruct ||
         objectType->opcode() != SpvOpTypeStruct) {
-      DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                         << module_.getIdName(inst->words[pointerIndex])
-                         << "'s type does not match Object <id> '"
-                         << module_.getIdName(object->id()) << "'s type.";
+      DIAG(pointer) << "OpStore Pointer <id> '"
+                    << module_.getIdName(inst->words[pointerIndex])
+                    << "'s type does not match Object <id> '"
+                    << module_.getIdName(object->id()) << "'s type.";
       return false;
     }
 
     // TODO: Check for layout compatible matricies and arrays as well.
     if (!AreLayoutCompatibleStructs(type, objectType)) {
-      DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                         << module_.getIdName(inst->words[pointerIndex])
-                         << "'s layout does not match Object <id> '"
-                         << module_.getIdName(object->id()) << "'s layout.";
+      DIAG(pointer) << "OpStore Pointer <id> '"
+                    << module_.getIdName(inst->words[pointerIndex])
+                    << "'s layout does not match Object <id> '"
+                    << module_.getIdName(object->id()) << "'s layout.";
       return false;
     }
   }
@@ -1343,10 +1339,10 @@
   auto sourceType = module_.FindDef(sourcePointerType->words()[3]);
   assert(sourceType);
   if (targetType->id() != sourceType->id()) {
-    DIAG(sourceIndex) << "OpCopyMemory Target <id> '"
-                      << module_.getIdName(inst->words[sourceIndex])
-                      << "'s type does not match Source <id> '"
-                      << module_.getIdName(sourceType->id()) << "'s type.";
+    DIAG(source) << "OpCopyMemory Target <id> '"
+                 << module_.getIdName(inst->words[sourceIndex])
+                 << "'s type does not match Source <id> '"
+                 << module_.getIdName(sourceType->id()) << "'s type.";
     return false;
   }
   return true;
@@ -1366,16 +1362,16 @@
   if (!size) return false;
   auto targetPointerType = module_.FindDef(target->type_id());
   if (!targetPointerType || SpvOpTypePointer != targetPointerType->opcode()) {
-    DIAG(targetIndex) << "OpCopyMemorySized Target <id> '"
-                      << module_.getIdName(inst->words[targetIndex])
-                      << "' is not a pointer.";
+    DIAG(target) << "OpCopyMemorySized Target <id> '"
+                 << module_.getIdName(inst->words[targetIndex])
+                 << "' is not a pointer.";
     return false;
   }
   auto sourcePointerType = module_.FindDef(source->type_id());
   if (!sourcePointerType || SpvOpTypePointer != sourcePointerType->opcode()) {
-    DIAG(sourceIndex) << "OpCopyMemorySized Source <id> '"
-                      << module_.getIdName(inst->words[sourceIndex])
-                      << "' is not a pointer.";
+    DIAG(source) << "OpCopyMemorySized Source <id> '"
+                 << module_.getIdName(inst->words[sourceIndex])
+                 << "' is not a pointer.";
     return false;
   }
   switch (size->opcode()) {
@@ -1387,9 +1383,9 @@
       auto sizeType = module_.FindDef(size->type_id());
       assert(sizeType);
       if (SpvOpTypeInt != sizeType->opcode()) {
-        DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '"
-                        << module_.getIdName(inst->words[sizeIndex])
-                        << "'s type is not an integer type.";
+        DIAG(size) << "OpCopyMemorySized Size <id> '"
+                   << module_.getIdName(inst->words[sizeIndex])
+                   << "'s type is not an integer type.";
         return false;
       }
     } break;
@@ -1398,16 +1394,16 @@
       assert(pointerType);
       auto sizeType = module_.FindDef(pointerType->type_id());
       if (!sizeType || SpvOpTypeInt != sizeType->opcode()) {
-        DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '"
-                        << module_.getIdName(inst->words[sizeIndex])
-                        << "'s variable type is not an integer type.";
+        DIAG(size) << "OpCopyMemorySized Size <id> '"
+                   << module_.getIdName(inst->words[sizeIndex])
+                   << "'s variable type is not an integer type.";
         return false;
       }
     } break;
     default:
-      DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '"
-                      << module_.getIdName(inst->words[sizeIndex])
-                      << "' is not a constant or variable.";
+      DIAG(size) << "OpCopyMemorySized Size <id> '"
+                 << module_.getIdName(inst->words[sizeIndex])
+                 << "' is not a constant or variable.";
       return false;
   }
   // TODO: Check that consant is a least size 1, see the same bug as above for
@@ -1425,7 +1421,7 @@
   auto resultTypeIndex = 1;
   auto resultTypeInstr = module_.FindDef(inst->words[resultTypeIndex]);
   if (SpvOpTypePointer != resultTypeInstr->opcode()) {
-    DIAG(resultTypeIndex) << "The Result Type of " << instr_name << " <id> '"
+    DIAG(resultTypeInstr) << "The Result Type of " << instr_name << " <id> '"
                           << module_.getIdName(inst->words[2])
                           << "' must be OpTypePointer. Found Op"
                           << spvOpcodeString(
@@ -1444,9 +1440,9 @@
   auto baseInstr = module_.FindDef(inst->words[baseIdIndex]);
   auto baseTypeInstr = module_.FindDef(baseInstr->type_id());
   if (!baseTypeInstr || SpvOpTypePointer != baseTypeInstr->opcode()) {
-    DIAG(baseIdIndex) << "The Base <id> '"
-                      << module_.getIdName(inst->words[baseIdIndex]) << "' in "
-                      << instr_name << " instruction must be a pointer.";
+    DIAG(baseInstr) << "The Base <id> '"
+                    << module_.getIdName(inst->words[baseIdIndex]) << "' in "
+                    << instr_name << " instruction must be a pointer.";
     return false;
   }
 
@@ -1455,7 +1451,7 @@
   auto resultTypeStorageClass = resultTypeInstr->word(2);
   auto baseTypeStorageClass = baseTypeInstr->word(2);
   if (resultTypeStorageClass != baseTypeStorageClass) {
-    DIAG(resultTypeIndex) << "The result pointer storage class and base "
+    DIAG(resultTypeInstr) << "The result pointer storage class and base "
                              "pointer storage class in "
                           << instr_name << " do not match.";
     return false;
@@ -1471,7 +1467,7 @@
   const size_t num_indexes_limit =
       module_.options()->universal_limits_.max_access_chain_indexes;
   if (num_indexes > num_indexes_limit) {
-    DIAG(resultTypeIndex) << "The number of indexes in " << instr_name
+    DIAG(resultTypeInstr) << "The number of indexes in " << instr_name
                           << " may not exceed " << num_indexes_limit
                           << ". Found " << num_indexes << " indexes.";
     return false;
@@ -1490,8 +1486,8 @@
     // The index must be a scalar integer type (See OpAccessChain in the Spec.)
     auto indexTypeInstr = module_.FindDef(cur_word_instr->type_id());
     if (!indexTypeInstr || SpvOpTypeInt != indexTypeInstr->opcode()) {
-      DIAG(i) << "Indexes passed to " << instr_name
-              << " must be of type integer.";
+      DIAG(module_.FindDef(cur_word))
+          << "Indexes passed to " << instr_name << " must be of type integer.";
       return false;
     }
     switch (typePointedTo->opcode()) {
@@ -1508,9 +1504,9 @@
         // In case of structures, there is an additional constraint on the
         // index: the index must be an OpConstant.
         if (SpvOpConstant != cur_word_instr->opcode()) {
-          DIAG(i) << "The <id> passed to " << instr_name
-                  << " to index into a "
-                     "structure must be an OpConstant.";
+          DIAG(cur_word_instr) << "The <id> passed to " << instr_name
+                               << " to index into a "
+                                  "structure must be an OpConstant.";
           return false;
         }
         // Get the index value from the OpConstant (word 3 of OpConstant).
@@ -1524,13 +1520,13 @@
         const uint32_t num_struct_members =
             static_cast<uint32_t>(typePointedTo->words().size() - 2);
         if (cur_index >= num_struct_members) {
-          DIAG(i) << "Index is out of bounds: " << instr_name
-                  << " can not find index " << cur_index
-                  << " into the structure <id> '"
-                  << module_.getIdName(typePointedTo->id())
-                  << "'. This structure has " << num_struct_members
-                  << " members. Largest valid index is "
-                  << num_struct_members - 1 << ".";
+          DIAG(cur_word_instr) << "Index is out of bounds: " << instr_name
+                               << " can not find index " << cur_index
+                               << " into the structure <id> '"
+                               << module_.getIdName(typePointedTo->id())
+                               << "'. This structure has " << num_struct_members
+                               << " members. Largest valid index is "
+                               << num_struct_members - 1 << ".";
           return false;
         }
         // Struct members IDs start at word 2 of OpTypeStruct.
@@ -1540,9 +1536,9 @@
       }
       default: {
         // Give an error. reached non-composite type while indexes still remain.
-        DIAG(i) << instr_name
-                << " reached non-composite type while indexes "
-                   "still remain to be traversed.";
+        DIAG(cur_word_instr) << instr_name
+                             << " reached non-composite type while indexes "
+                                "still remain to be traversed.";
         return false;
       }
     }
@@ -1550,7 +1546,7 @@
   // At this point, we have fully walked down from the base using the indeces.
   // The type being pointed to should be the same as the result type.
   if (typePointedTo->id() != resultTypePointedTo->id()) {
-    DIAG(resultTypeIndex)
+    DIAG(resultTypeInstr)
         << instr_name << " result type (Op"
         << spvOpcodeString(static_cast<SpvOp>(resultTypePointedTo->opcode()))
         << ") does not match the type that results from indexing into the base "
@@ -1623,7 +1619,7 @@
       for (auto model : *models) {
         std::string reason;
         if (!thisFunc->IsCompatibleWithExecutionModel(model, &reason)) {
-          DIAG(2)
+          DIAG(module_.FindDef(inst->words[2]))
               << "OpEntryPoint Entry Point <id> '" << module_.getIdName(entryId)
               << "'s callgraph contains function <id> "
               << module_.getIdName(thisInst->id())
@@ -1641,19 +1637,19 @@
   auto functionTypeIndex = 4;
   auto functionType = module_.FindDef(inst->words[functionTypeIndex]);
   if (!functionType || SpvOpTypeFunction != functionType->opcode()) {
-    DIAG(functionTypeIndex) << "OpFunction Function Type <id> '"
-                            << module_.getIdName(inst->words[functionTypeIndex])
-                            << "' is not a function type.";
+    DIAG(functionType) << "OpFunction Function Type <id> '"
+                       << module_.getIdName(inst->words[functionTypeIndex])
+                       << "' is not a function type.";
     return false;
   }
   auto returnType = module_.FindDef(functionType->words()[2]);
   assert(returnType);
   if (returnType->id() != resultType->id()) {
-    DIAG(resultTypeIndex) << "OpFunction Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' does not match the Function Type <id> '"
-                          << module_.getIdName(resultType->id())
-                          << "'s return type.";
+    DIAG(resultType) << "OpFunction Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' does not match the Function Type <id> '"
+                     << module_.getIdName(resultType->id())
+                     << "'s return type.";
     return false;
   }
   return true;
@@ -1678,18 +1674,19 @@
   auto functionType = module_.FindDef(inst->words[4]);
   assert(functionType);
   if (paramIndex >= functionType->words().size() - 3) {
-    DIAG(0) << "Too many OpFunctionParameters for " << inst->words[2]
-            << ": expected " << functionType->words().size() - 3
-            << " based on the function's type";
+    DIAG(module_.FindDef(inst->words[0]))
+        << "Too many OpFunctionParameters for " << inst->words[2]
+        << ": expected " << functionType->words().size() - 3
+        << " based on the function's type";
     return false;
   }
   auto paramType = module_.FindDef(functionType->words()[paramIndex + 3]);
   assert(paramType);
   if (resultType->id() != paramType->id()) {
-    DIAG(resultTypeIndex) << "OpFunctionParameter Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "' does not match the OpTypeFunction parameter "
-                             "type of the same index.";
+    DIAG(resultType) << "OpFunctionParameter Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "' does not match the OpTypeFunction parameter "
+                        "type of the same index.";
     return false;
   }
   return true;
@@ -1704,19 +1701,19 @@
   auto functionIndex = 3;
   auto function = module_.FindDef(inst->words[functionIndex]);
   if (!function || SpvOpFunction != function->opcode()) {
-    DIAG(functionIndex) << "OpFunctionCall Function <id> '"
-                        << module_.getIdName(inst->words[functionIndex])
-                        << "' is not a function.";
+    DIAG(function) << "OpFunctionCall Function <id> '"
+                   << module_.getIdName(inst->words[functionIndex])
+                   << "' is not a function.";
     return false;
   }
   auto returnType = module_.FindDef(function->type_id());
   assert(returnType);
   if (returnType->id() != resultType->id()) {
-    DIAG(resultTypeIndex) << "OpFunctionCall Result Type <id> '"
-                          << module_.getIdName(inst->words[resultTypeIndex])
-                          << "'s type does not match Function <id> '"
-                          << module_.getIdName(returnType->id())
-                          << "'s return type.";
+    DIAG(resultType) << "OpFunctionCall Result Type <id> '"
+                     << module_.getIdName(inst->words[resultTypeIndex])
+                     << "'s type does not match Function <id> '"
+                     << module_.getIdName(returnType->id())
+                     << "'s return type.";
     return false;
   }
   auto functionType = module_.FindDef(function->words()[4]);
@@ -1724,7 +1721,7 @@
   auto functionCallArgCount = inst->words.size() - 4;
   auto functionParamCount = functionType->words().size() - 3;
   if (functionParamCount != functionCallArgCount) {
-    DIAG(inst->words.size() - 1)
+    DIAG(module_.FindDef(inst->words.back()))
         << "OpFunctionCall Function <id>'s parameter count does not match "
            "the argument count.";
     return false;
@@ -1738,11 +1735,11 @@
     auto parameterType = module_.FindDef(functionType->words()[paramIndex]);
     assert(parameterType);
     if (argumentType->id() != parameterType->id()) {
-      DIAG(argumentIndex) << "OpFunctionCall Argument <id> '"
-                          << module_.getIdName(inst->words[argumentIndex])
-                          << "'s type does not match Function <id> '"
-                          << module_.getIdName(parameterType->id())
-                          << "'s parameter type.";
+      DIAG(argument) << "OpFunctionCall Argument <id> '"
+                     << module_.getIdName(inst->words[argumentIndex])
+                     << "'s type does not match Function <id> '"
+                     << module_.getIdName(parameterType->id())
+                     << "'s parameter type.";
       return false;
     }
   }
@@ -1762,11 +1759,11 @@
   auto resultTypeIndex = 1;
   auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
   if (!resultType || resultType->opcode() != SpvOpTypeVector) {
-    DIAG(resultTypeIndex) << "The Result Type of " << instr_name()
-                          << " must be OpTypeVector. Found Op"
-                          << spvOpcodeString(
-                                 static_cast<SpvOp>(resultType->opcode()))
-                          << ".";
+    DIAG(resultType) << "The Result Type of " << instr_name()
+                     << " must be OpTypeVector. Found Op"
+                     << spvOpcodeString(
+                            static_cast<SpvOp>(resultType->opcode()))
+                     << ".";
     return false;
   }
 
@@ -1776,7 +1773,7 @@
   auto vectorComponentCountIndex = 3;
   auto resultVectorDimension = resultType->words()[vectorComponentCountIndex];
   if (componentCount != resultVectorDimension) {
-    DIAG(inst->words.size() - 1)
+    DIAG(module_.FindDef(inst->words.back()))
         << instr_name()
         << " component literals count does not match "
            "Result Type <id> '"
@@ -1793,25 +1790,25 @@
   auto vector2Object = module_.FindDef(inst->words[vector2Index]);
   auto vector2Type = module_.FindDef(vector2Object->type_id());
   if (!vector1Type || vector1Type->opcode() != SpvOpTypeVector) {
-    DIAG(vector1Index) << "The type of Vector 1 must be OpTypeVector.";
+    DIAG(vector1Object) << "The type of Vector 1 must be OpTypeVector.";
     return false;
   }
   if (!vector2Type || vector2Type->opcode() != SpvOpTypeVector) {
-    DIAG(vector2Index) << "The type of Vector 2 must be OpTypeVector.";
+    DIAG(vector2Object) << "The type of Vector 2 must be OpTypeVector.";
     return false;
   }
   auto vectorComponentTypeIndex = 2;
   auto resultComponentType = resultType->words()[vectorComponentTypeIndex];
   auto vector1ComponentType = vector1Type->words()[vectorComponentTypeIndex];
   if (vector1ComponentType != resultComponentType) {
-    DIAG(vector1Index) << "The Component Type of Vector 1 must be the same "
-                          "as ResultType.";
+    DIAG(vector1Object) << "The Component Type of Vector 1 must be the same "
+                           "as ResultType.";
     return false;
   }
   auto vector2ComponentType = vector2Type->words()[vectorComponentTypeIndex];
   if (vector2ComponentType != resultComponentType) {
-    DIAG(vector2Index) << "The Component Type of Vector 2 must be the same "
-                          "as ResultType.";
+    DIAG(vector2Object) << "The Component Type of Vector 2 must be the same "
+                           "as ResultType.";
     return false;
   }
 
@@ -1823,8 +1820,9 @@
   for (size_t i = firstLiteralIndex; i < inst->words.size(); ++i) {
     auto literal = inst->words[i];
     if (literal != 0xFFFFFFFF && literal >= N) {
-      DIAG(i) << "Component literal value " << literal << " is greater than "
-              << N - 1 << ".";
+      DIAG(module_.FindDef(inst->words[i]))
+          << "Component literal value " << literal << " is greater than "
+          << N - 1 << ".";
       return false;
     }
   }
@@ -1838,16 +1836,18 @@
   auto thisInst = module_.FindDef(inst->words[2]);
   SpvOp typeOp = module_.GetIdOpcode(thisInst->type_id());
   if (!spvOpcodeGeneratesType(typeOp)) {
-    DIAG(0) << "OpPhi's type <id> " << module_.getIdName(thisInst->type_id())
-            << " is not a type instruction.";
+    DIAG(thisInst) << "OpPhi's type <id> "
+                   << module_.getIdName(thisInst->type_id())
+                   << " is not a type instruction.";
     return false;
   }
 
   auto block = thisInst->block();
   size_t numInOps = inst->words.size() - 3;
   if (numInOps % 2 != 0) {
-    DIAG(0) << "OpPhi does not have an equal number of incoming values and "
-               "basic blocks.";
+    DIAG(thisInst)
+        << "OpPhi does not have an equal number of incoming values and "
+           "basic blocks.";
     return false;
   }
 
@@ -1863,9 +1863,9 @@
 
   size_t numEdges = numInOps / 2;
   if (numEdges != predIds.size()) {
-    DIAG(0) << "OpPhi's number of incoming blocks (" << numEdges
-            << ") does not match block's predecessor count ("
-            << block->predecessors()->size() << ").";
+    DIAG(thisInst) << "OpPhi's number of incoming blocks (" << numEdges
+                   << ") does not match block's predecessor count ("
+                   << block->predecessors()->size() << ").";
     return false;
   }
 
@@ -1875,26 +1875,27 @@
       // Incoming value type must match the phi result type.
       auto incTypeId = module_.GetTypeId(incId);
       if (thisInst->type_id() != incTypeId) {
-        DIAG(i) << "OpPhi's result type <id> "
-                << module_.getIdName(thisInst->type_id())
-                << " does not match incoming value <id> "
-                << module_.getIdName(incId) << " type <id> "
-                << module_.getIdName(incTypeId) << ".";
+        DIAG(thisInst) << "OpPhi's result type <id> "
+                       << module_.getIdName(thisInst->type_id())
+                       << " does not match incoming value <id> "
+                       << module_.getIdName(incId) << " type <id> "
+                       << module_.getIdName(incTypeId) << ".";
         return false;
       }
     } else {
       if (module_.GetIdOpcode(incId) != SpvOpLabel) {
-        DIAG(i) << "OpPhi's incoming basic block <id> "
-                << module_.getIdName(incId) << " is not an OpLabel.";
+        DIAG(thisInst) << "OpPhi's incoming basic block <id> "
+                       << module_.getIdName(incId) << " is not an OpLabel.";
         return false;
       }
 
       // Incoming basic block must be an immediate predecessor of the phi's
       // block.
       if (!std::binary_search(predIds.begin(), predIds.end(), incId)) {
-        DIAG(i) << "OpPhi's incoming basic block <id> "
-                << module_.getIdName(incId) << " is not a predecessor of <id> "
-                << module_.getIdName(block->id()) << ".";
+        DIAG(thisInst) << "OpPhi's incoming basic block <id> "
+                       << module_.getIdName(incId)
+                       << " is not a predecessor of <id> "
+                       << module_.getIdName(block->id()) << ".";
         return false;
       }
     }
@@ -1926,7 +1927,8 @@
   // num_operands is either 3 or 5 --- if 5, the last two need to be literal
   // integers
   if (numOperands != 3 && numOperands != 5) {
-    DIAG(0) << "OpBranchConditional requires either 3 or 5 parameters";
+    libspirv::Instruction* fake_inst = nullptr;
+    DIAG(fake_inst) << "OpBranchConditional requires either 3 or 5 parameters";
     return false;
   }
 
@@ -1935,7 +1937,7 @@
   // grab the condition operand and check that it is a bool
   const auto condOp = module_.FindDef(inst->words[condOperandIndex]);
   if (!condOp || !module_.IsBoolScalarType(condOp->type_id())) {
-    DIAG(0)
+    DIAG(condOp)
         << "Condition operand for OpBranchConditional must be of boolean type";
     ret = false;
   }
@@ -1946,15 +1948,17 @@
   // PerformCfgChecks already checks for that
   const auto targetOpTrue = module_.FindDef(inst->words[targetTrueIndex]);
   if (!targetOpTrue || SpvOpLabel != targetOpTrue->opcode()) {
-    DIAG(0) << "The 'True Label' operand for OpBranchConditional must be the "
-               "ID of an OpLabel instruction";
+    DIAG(targetOpTrue)
+        << "The 'True Label' operand for OpBranchConditional must be the "
+           "ID of an OpLabel instruction";
     ret = false;
   }
 
   const auto targetOpFalse = module_.FindDef(inst->words[targetFalseIndex]);
   if (!targetOpFalse || SpvOpLabel != targetOpFalse->opcode()) {
-    DIAG(0) << "The 'False Label' operand for OpBranchConditional must be the "
-               "ID of an OpLabel instruction";
+    DIAG(targetOpFalse)
+        << "The 'False Label' operand for OpBranchConditional must be the "
+           "ID of an OpLabel instruction";
     ret = false;
   }
 
@@ -1973,16 +1977,16 @@
   auto valueIndex = 1;
   auto value = module_.FindDef(inst->words[valueIndex]);
   if (!value || !value->type_id()) {
-    DIAG(valueIndex) << "OpReturnValue Value <id> '"
-                     << module_.getIdName(inst->words[valueIndex])
-                     << "' does not represent a value.";
+    DIAG(value) << "OpReturnValue Value <id> '"
+                << module_.getIdName(inst->words[valueIndex])
+                << "' does not represent a value.";
     return false;
   }
   auto valueType = module_.FindDef(value->type_id());
   if (!valueType || SpvOpTypeVoid == valueType->opcode()) {
-    DIAG(valueIndex) << "OpReturnValue value's type <id> '"
-                     << module_.getIdName(value->type_id())
-                     << "' is missing or void.";
+    DIAG(value) << "OpReturnValue value's type <id> '"
+                << module_.getIdName(value->type_id())
+                << "' is missing or void.";
     return false;
   }
 
@@ -1993,7 +1997,7 @@
   if (addressingModel == SpvAddressingModelLogical &&
       SpvOpTypePointer == valueType->opcode() && !uses_variable_pointer &&
       !module_.options()->relax_logcial_pointer) {
-    DIAG(valueIndex)
+    DIAG(value)
         << "OpReturnValue value's type <id> '"
         << module_.getIdName(value->type_id())
         << "' is a pointer, which is invalid in the Logical addressing model.";
@@ -2007,14 +2011,14 @@
     function--;
   }
   if (SpvOpFunction != function->opcode) {
-    DIAG(valueIndex) << "OpReturnValue is not in a basic block.";
+    DIAG(value) << "OpReturnValue is not in a basic block.";
     return false;
   }
   auto returnType = module_.FindDef(function->words[1]);
   if (!returnType || returnType->id() != valueType->id()) {
-    DIAG(valueIndex) << "OpReturnValue Value <id> '"
-                     << module_.getIdName(inst->words[valueIndex])
-                     << "'s type does not match OpFunction's return type.";
+    DIAG(value) << "OpReturnValue Value <id> '"
+                << module_.getIdName(inst->words[valueIndex])
+                << "'s type does not match OpFunction's return type.";
     return false;
   }
   return true;
@@ -2805,7 +2809,6 @@
                   position, state.context()->consumer);
   for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
     if (!idUsage.isValid(&pInsts[instIndex])) return SPV_ERROR_INVALID_ID;
-    position->index += pInsts[instIndex].words.size();
   }
   return SPV_SUCCESS;
 }
diff --git a/test/c_interface_test.cpp b/test/c_interface_test.cpp
index 9260549..8ec4477 100644
--- a/test/c_interface_test.cpp
+++ b/test/c_interface_test.cpp
@@ -194,8 +194,10 @@
         // TODO(antiagainst): what validation reports is not a word offset here.
         // It is inconsistent with diassembler. Should be fixed.
         EXPECT_EQ(1u, position.index);
-        EXPECT_STREQ("Nop cannot appear before the memory model instruction",
-                     message);
+        EXPECT_STREQ(
+            "Nop cannot appear before the memory model instruction\n"
+            "  OpNop\n",
+            message);
       });
 
   spv_binary binary = nullptr;
@@ -287,8 +289,10 @@
   EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, &diagnostic));
 
   EXPECT_EQ(0, invocation);  // Consumer should not be invoked at all.
-  EXPECT_STREQ("Nop cannot appear before the memory model instruction",
-               diagnostic->error);
+  EXPECT_STREQ(
+      "Nop cannot appear before the memory model instruction\n"
+      "  OpNop\n",
+      diagnostic->error);
 
   spvDiagnosticDestroy(diagnostic);
   spvBinaryDestroy(binary);
diff --git a/test/diagnostic_test.cpp b/test/diagnostic_test.cpp
index 8b8dbbe..82414ab 100644
--- a/test/diagnostic_test.cpp
+++ b/test/diagnostic_test.cpp
@@ -68,16 +68,16 @@
 TEST(DiagnosticStream, ConversionToResultType) {
   // Check after the DiagnosticStream object is destroyed.
   spv_result_t value;
-  { value = DiagnosticStream({}, nullptr, SPV_ERROR_INVALID_TEXT); }
+  { value = DiagnosticStream({}, nullptr, "", SPV_ERROR_INVALID_TEXT); }
   EXPECT_EQ(SPV_ERROR_INVALID_TEXT, value);
 
   // Check implicit conversion via plain assignment.
-  value = DiagnosticStream({}, nullptr, SPV_SUCCESS);
+  value = DiagnosticStream({}, nullptr, "", SPV_SUCCESS);
   EXPECT_EQ(SPV_SUCCESS, value);
 
   // Check conversion via constructor.
   EXPECT_EQ(SPV_FAILED_MATCH,
-            spv_result_t(DiagnosticStream({}, nullptr, SPV_FAILED_MATCH)));
+            spv_result_t(DiagnosticStream({}, nullptr, "", SPV_FAILED_MATCH)));
 }
 
 TEST(
@@ -94,7 +94,7 @@
 
   // Enclose the DiagnosticStream variables in a scope to force destruction.
   {
-    DiagnosticStream ds0({}, consumer, SPV_ERROR_INVALID_BINARY);
+    DiagnosticStream ds0({}, consumer, "", SPV_ERROR_INVALID_BINARY);
     ds0 << "First";
     DiagnosticStream ds1(std::move(ds0));
     ds1 << "Second";
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index 9cb8cd3..2b119dc 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -382,7 +382,8 @@
   ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               MatchesRegex("Block .\\[cont\\] appears in the binary "
-                           "before its dominator .\\[branch\\]"));
+                           "before its dominator .\\[branch\\]\n"
+                           "  %branch = OpLabel\n"));
 }
 
 TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) {
@@ -413,7 +414,8 @@
     ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex("Block .\\[merge\\] is already a merge block "
-                             "for another header"));
+                             "for another header\n"
+                             "  %Main = OpFunction %void None %9\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   }
@@ -447,7 +449,8 @@
     ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex("Block .\\[merge\\] is already a merge block "
-                             "for another header"));
+                             "for another header\n"
+                             "  %Main = OpFunction %void None %9\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   }
@@ -470,7 +473,8 @@
   ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               MatchesRegex("First block .\\[entry\\] of function .\\[Main\\] "
-                           "is targeted by block .\\[bad\\]"));
+                           "is targeted by block .\\[bad\\]\n"
+                           "  %Main = OpFunction %void None %10\n"));
 }
 
 TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceValue) {
@@ -493,7 +497,8 @@
   EXPECT_THAT(
       getDiagnosticString(),
       MatchesRegex("Block\\(s\\) \\{.\\[Main\\]\\} are referenced but not "
-                   "defined in function .\\[Main\\]"))
+                   "defined in function .\\[Main\\]\n"
+                   "  %Main = OpFunction %void None %10\n"))
       << str;
 }
 
@@ -518,7 +523,8 @@
   ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               MatchesRegex("First block .\\[entry\\] of function .\\[Main\\] "
-                           "is targeted by block .\\[bad\\]"));
+                           "is targeted by block .\\[bad\\]\n"
+                           "  %Main = OpFunction %void None %10\n"));
 }
 
 TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) {
@@ -545,7 +551,8 @@
   ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               MatchesRegex("First block .\\[entry\\] of function .\\[Main\\] "
-                           "is targeted by block .\\[bad\\]"));
+                           "is targeted by block .\\[bad\\]\n"
+                           "  %Main = OpFunction %void None %10\n"));
 }
 
 TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) {
@@ -579,7 +586,8 @@
   ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               MatchesRegex("First block .\\[entry\\] of function .\\[Main\\] "
-                           "is targeted by block .\\[bad\\]"));
+                           "is targeted by block .\\[bad\\]\n"
+                           "  %Main = OpFunction %void None %10\n"));
 }
 
 TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) {
@@ -614,7 +622,8 @@
   EXPECT_THAT(
       getDiagnosticString(),
       MatchesRegex("Block\\(s\\) \\{.\\[middle2\\]\\} are referenced but not "
-                   "defined in function .\\[Main\\]"));
+                   "defined in function .\\[Main\\]\n"
+                   "  %Main = OpFunction %void None %9\n"));
 }
 
 TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) {
@@ -645,7 +654,7 @@
         getDiagnosticString(),
         MatchesRegex("The selection construct with the selection header "
                      ".\\[head\\] does not dominate the merge block "
-                     ".\\[merge\\]"));
+                     ".\\[merge\\]\n  %merge = OpLabel\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   }
@@ -677,7 +686,7 @@
         getDiagnosticString(),
         MatchesRegex("The selection construct with the selection header "
                      ".\\[head\\] does not strictly dominate the merge block "
-                     ".\\[head\\]"));
+                     ".\\[head\\]\n  %head = OpLabel\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
   }
@@ -920,7 +929,8 @@
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex("The continue construct with the continue target "
                              ".\\[loop2_merge\\] is not post dominated by the "
-                             "back-edge block .\\[be_block\\]"));
+                             "back-edge block .\\[be_block\\]\n"
+                             "  %be_block = OpLabel\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   }
@@ -953,7 +963,8 @@
     EXPECT_THAT(
         getDiagnosticString(),
         MatchesRegex("Back-edges \\(.\\[f\\] -> .\\[split\\]\\) can only "
-                     "be formed between a block and a loop header."));
+                     "be formed between a block and a loop header.\n"
+                     "  OpFunctionEnd\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   }
@@ -982,7 +993,8 @@
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex(
                     "Back-edges \\(.\\[split\\] -> .\\[split\\]\\) can only be "
-                    "formed between a block and a loop header."));
+                    "formed between a block and a loop header.\n"
+                    "  OpFunctionEnd\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   }
@@ -1015,7 +1027,8 @@
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex(
                     "Loop header .\\[loop\\] is targeted by 2 back-edge blocks "
-                    "but the standard requires exactly one"))
+                    "but the standard requires exactly one\n"
+                    "  %loop = OpLabel\n"))
         << str;
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -1051,7 +1064,8 @@
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex("The continue construct with the continue target "
                              ".\\[cheader\\] is not post dominated by the "
-                             "back-edge block .\\[be_block\\]"));
+                             "back-edge block .\\[be_block\\]\n"
+                             "  %be_block = OpLabel\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   }
@@ -1082,7 +1096,8 @@
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex("The continue construct with the continue target "
                              ".\\[loop\\] is not post dominated by the "
-                             "back-edge block .\\[cont\\]"))
+                             "back-edge block .\\[cont\\]\n"
+                             "  %cont = OpLabel\n"))
         << str;
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -1116,7 +1131,8 @@
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex("The continue construct with the continue target "
                              ".\\[loop\\] is not post dominated by the "
-                             "back-edge block .\\[cont\\]"));
+                             "back-edge block .\\[cont\\]\n"
+                             "  %cont = OpLabel\n"));
   } else {
     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
   }
@@ -1190,7 +1206,7 @@
       getDiagnosticString(),
       MatchesRegex("Loop header .\\[loop\\] is targeted by "
                    "0 back-edge blocks but the standard requires exactly "
-                   "one"));
+                   "one\n  %loop = OpLabel\n"));
 }
 
 TEST_F(ValidateCFG, LoopWithBackEdgeFromUnreachableContinueConstructGood) {
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp
index 19ad3ce..092f569 100644
--- a/test/val/val_layout_test.cpp
+++ b/test/val/val_layout_test.cpp
@@ -397,7 +397,8 @@
   CompileSuccessfully(s);
   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              StrEq("Missing OpFunctionEnd at end of module."));
+              StrEq("Missing OpFunctionEnd at end of module.\n"
+                    "  OpReturn\n"));
 }
 
 TEST_F(ValidateLayout, MissingFunctionEndForFunctionPrototype) {
@@ -413,7 +414,8 @@
   CompileSuccessfully(s);
   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              StrEq("Missing OpFunctionEnd at end of module."));
+              StrEq("Missing OpFunctionEnd at end of module.\n"
+                    "  %3 = OpFunction %void None %2\n"));
 }
 
 using ValidateOpFunctionParameter = spvtest::ValidateBase<int>;
diff --git a/test/val/val_ssa_test.cpp b/test/val/val_ssa_test.cpp
index f6a712f..1c3651f 100644
--- a/test/val/val_ssa_test.cpp
+++ b/test/val/val_ssa_test.cpp
@@ -1124,7 +1124,8 @@
   EXPECT_THAT(
       getDiagnosticString(),
       MatchesRegex("ID .\\[eleven\\] defined in block .\\[true_block\\] does "
-                   "not dominate its use in block .\\[false_block\\]"));
+                   "not dominate its use in block .\\[false_block\\]\n"
+                   "  OpFunctionEnd\n"));
 }
 
 TEST_F(ValidateSSA, PhiUseDoesntDominateDefinitionGood) {
@@ -1264,7 +1265,8 @@
   EXPECT_THAT(
       getDiagnosticString(),
       MatchesRegex("In OpPhi instruction .\\[phi\\], ID .\\[true_copy\\] "
-                   "definition does not dominate its parent .\\[if_false\\]"));
+                   "definition does not dominate its parent .\\[if_false\\]\n"
+                   "  OpFunctionEnd\n"));
 }
 
 TEST_F(ValidateSSA, PhiVariableDefDominatesButNotDefinedInParentBlock) {
@@ -1389,7 +1391,8 @@
   EXPECT_THAT(
       getDiagnosticString(),
       MatchesRegex("ID .\\[first\\] used in function .\\[func2\\] is used "
-                   "outside of it's defining function .\\[func\\]"));
+                   "outside of it's defining function .\\[func\\]\n"
+                   "  OpFunctionEnd\n"));
 }
 
 TEST_F(ValidateSSA, TypeForwardPointerForwardReference) {
diff --git a/test/val/val_state_test.cpp b/test/val/val_state_test.cpp
index c63a0c5..5602047 100644
--- a/test/val/val_state_test.cpp
+++ b/test/val/val_state_test.cpp
@@ -35,13 +35,16 @@
 using libspirv::ValidationState_t;
 using std::vector;
 
+// This is all we need for these tests.
+static uint32_t kFakeBinary[] = {0};
+
 // A test with a ValidationState_t member transparently.
 class ValidationStateTest : public testing::Test {
  public:
   ValidationStateTest()
       : context_(spvContextCreate(SPV_ENV_UNIVERSAL_1_0)),
         options_(spvValidatorOptionsCreate()),
-        state_(context_, options_) {}
+        state_(context_, options_, kFakeBinary, 0) {}
 
   ~ValidationStateTest() {
     spvContextDestroy(context_);
diff --git a/tools/io.h b/tools/io.h
index 690b5c3..637ae5b 100644
--- a/tools/io.h
+++ b/tools/io.h
@@ -39,7 +39,10 @@
       }
     } else {
       if (sizeof(T) != 1 && (ftell(fp) % sizeof(T))) {
-        fprintf(stderr, "error: corrupted word found in file '%s'\n", filename);
+        fprintf(
+            stderr,
+            "error: file size should be a multiple of %zd; file '%s' corrupt\n",
+            sizeof(T), filename);
         return false;
       }
     }
diff --git a/tools/val/val.cpp b/tools/val/val.cpp
index 7c215e4..ca16f2d 100644
--- a/tools/val/val.cpp
+++ b/tools/val/val.cpp
@@ -163,15 +163,16 @@
       case SPV_MSG_FATAL:
       case SPV_MSG_INTERNAL_ERROR:
       case SPV_MSG_ERROR:
-        std::cerr << "error: " << position.index << ": " << message
+        std::cerr << "error: line " << position.index << ": " << message
                   << std::endl;
         break;
       case SPV_MSG_WARNING:
-        std::cout << "warning: " << position.index << ": " << message
+        std::cout << "warning: line " << position.index << ": " << message
                   << std::endl;
         break;
       case SPV_MSG_INFO:
-        std::cout << "info: " << position.index << ": " << message << std::endl;
+        std::cout << "info: line " << position.index << ": " << message
+                  << std::endl;
         break;
       default:
         break;