Index: lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java	(revision 1294591)
+++ lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java	(working copy)
@@ -17,31 +17,31 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.CheckHits;
 import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
-import org.apache.lucene.search.CheckHits;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.similarities.DefaultSimilarity;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.store.Directory;
-import org.apache.lucene.analysis.MockAnalyzer;
-import org.apache.lucene.index.AtomicReaderContext;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.index.IndexReaderContext;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.StringField;
-import org.apache.lucene.document.TextField;
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.ReaderUtil;
 
-import java.io.IOException;
-
 public class TestSpans extends LuceneTestCase {
   private IndexSearcher searcher;
   private IndexReader reader;
Index: lucene/core/src/java/org/apache/lucene/codecs/BlockTermsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/BlockTermsReader.java	(revision 1294591)
+++ lucene/core/src/java/org/apache/lucene/codecs/BlockTermsReader.java	(working copy)
@@ -197,6 +197,7 @@
 
   @Override
   public Terms terms(String field) throws IOException {
+    assert field != null;
     return fields.get(field);
   }
 
Index: lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java	(revision 1294591)
+++ lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java	(working copy)
@@ -212,6 +212,7 @@
 
   @Override
   public Terms terms(String field) throws IOException {
+    assert field != null;
     return fields.get(field);
   }
 
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java	(revision 1294591)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java	(working copy)
@@ -57,9 +57,12 @@
       termContexts.put(term, state);
       i++;
     }
-    stats = similarity.computeWeight(query.getBoost(), 
-        searcher.collectionStatistics(query.getField()), 
-        termStats);
+    final String field = query.getField();
+    if (field != null) {
+      stats = similarity.computeWeight(query.getBoost(), 
+                                       searcher.collectionStatistics(query.getField()), 
+                                       termStats);
+    }
   }
 
   @Override
@@ -67,18 +70,24 @@
 
   @Override
   public float getValueForNormalization() throws IOException {
-    return stats.getValueForNormalization();
+    return stats == null ? 1.0f : stats.getValueForNormalization();
   }
 
   @Override
   public void normalize(float queryNorm, float topLevelBoost) {
-    stats.normalize(queryNorm, topLevelBoost);
+    if (stats != null) {
+      stats.normalize(queryNorm, topLevelBoost);
+    }
   }
 
   @Override
   public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
       boolean topScorer, Bits acceptDocs) throws IOException {
-    return new SpanScorer(query.getSpans(context, acceptDocs, termContexts), this, similarity.sloppySimScorer(stats, context));
+    if (stats == null) {
+      return null;
+    } else {
+      return new SpanScorer(query.getSpans(context, acceptDocs, termContexts), this, similarity.sloppySimScorer(stats, context));
+    }
   }
 
   @Override
Index: lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java	(revision 1294591)
+++ lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java	(working copy)
@@ -584,8 +584,9 @@
     Weight weight = query.createWeight(this);
     float v = weight.getValueForNormalization();
     float norm = getSimilarity().queryNorm(v);
-    if (Float.isInfinite(norm) || Float.isNaN(norm))
+    if (Float.isInfinite(norm) || Float.isNaN(norm)) {
       norm = 1.0f;
+    }
     weight.normalize(norm, 1.0f);
     return weight;
   }
@@ -812,6 +813,8 @@
     final int docCount;
     final long sumTotalTermFreq;
     final long sumDocFreq;
+
+    assert field != null;
     
     Terms terms = MultiFields.getTerms(reader, field);
     if (terms == null) {
Index: lucene/contrib/CHANGES.txt
===================================================================
--- lucene/contrib/CHANGES.txt	(revision 1294591)
+++ lucene/contrib/CHANGES.txt	(working copy)
@@ -103,6 +103,10 @@
  * LUCENE-3045: fixed QueryNodeImpl.containsTag(String key) that was
    not lowercasing the key before checking for the tag (Adriano Crestani)
 
+ * LUCENE-3831: avoid NPE if the SpanQuery has a null field (eg a
+   SpanOrQuery with no clauses added).  (Alan Woodward via Mike
+   McCandless).
+
 Optimizations
 
 * LUCENE-3468: Replaced last() and remove() with pollLast() in
Index: lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java
===================================================================
--- lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java	(revision 1294591)
+++ lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java	(working copy)
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.StringReader;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -39,11 +40,16 @@
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.queryparser.classic.QueryParser;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.RegexpQuery;
 import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper;
+import org.apache.lucene.search.spans.SpanOrQuery;
+import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util._TestUtil;
@@ -224,4 +230,28 @@
     assertTrue(disi.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     reader.close();
   }
+
+  // LUCENE-3831
+  public void testNullPointerException() throws IOException {
+    RegexpQuery regex = new RegexpQuery(new Term("field", "worl."));
+    SpanQuery wrappedquery = new SpanMultiTermQueryWrapper<RegexpQuery>(regex);
+        
+    MemoryIndex mindex = new MemoryIndex();
+    mindex.addField("field", new MockAnalyzer(random).tokenStream("field", new StringReader("hello there")));
+
+    // This throws an NPE
+    assertEquals(0, mindex.search(wrappedquery), 0.00001f);
+  }
+    
+  // LUCENE-3831
+  public void testPassesIfWrapped() throws IOException {
+    RegexpQuery regex = new RegexpQuery(new Term("field", "worl."));
+    SpanQuery wrappedquery = new SpanOrQuery(new SpanMultiTermQueryWrapper<RegexpQuery>(regex));
+
+    MemoryIndex mindex = new MemoryIndex();
+    mindex.addField("field", new MockAnalyzer(random).tokenStream("field", new StringReader("hello there")));
+
+    // This passes though
+    assertEquals(0, mindex.search(wrappedquery), 0.00001f);
+  }
 }
