Buggy code:
private Set < SegmentInfo > segmentsToOptimize = new HashSet < SegmentInfo > ( ) ; private int optimizeMaxNumSegments ; private Lock writeLock ; private final int termIndexInterval ; private boolean closed ; private boolean closing ; private HashSet < SegmentInfo > mergingSegments = new HashSet < SegmentInfo > ( ) ; private MergePolicy mergePolicy ; private final MergeScheduler mergeScheduler ; private LinkedList < MergePolicy . OneMerge > pendingMerges = new LinkedList < MergePolicy . OneMerge > ( ) ; private Set < MergePolicy . OneMerge > runningMerges = new HashSet < MergePolicy . OneMerge > ( ) ; private List < MergePolicy . OneMerge > mergeExceptions = new ArrayList < MergePolicy . OneMerge > ( ) ; private long mergeGen ; private boolean stopMerges ; private int flushCount ; private int flushDeletesCount ; final IndexWriter . ReaderPool readerPool = new IndexWriter . ReaderPool ( ) ; private volatile boolean poolReaders ; private final IndexWriterConfig config ; private PayloadProcessorProvider payloadProcessorProvider ; IndexReader getReader ( ) throws IOException { } class ReaderPool { private final Map < SegmentInfo , SegmentReader > readerMap = new HashMap < SegmentInfo , SegmentReader > ( ) ; synchronized void clear ( SegmentInfos infos ) throws IOException { } public synchronized boolean infoIsLive ( SegmentInfo info ) { } public synchronized SegmentInfo mapToLive ( SegmentInfo info ) { } public synchronized void release ( SegmentReader sr ) throws IOException { } public synchronized void release ( SegmentReader sr , boolean drop ) throws IOException { } synchronized void close ( ) throws IOException { } synchronized void commit ( ) throws IOException { } public synchronized SegmentReader getReadOnlyClone ( SegmentInfo info , boolean doOpenStores , int termInfosIndexDivisor ) throws IOException { } public synchronized SegmentReader get ( SegmentInfo info , boolean doOpenStores ) throws IOException { } public synchronized SegmentReader get ( SegmentInfo info , boolean doOpenStores , int readBufferSize , int termsIndexDivisor ) throws IOException { } public synchronized SegmentReader getIfExists ( SegmentInfo info ) throws IOException { } } public int numDeletedDocs ( SegmentInfo info ) throws IOException { } protected final void ensureOpen ( boolean includePendingClose ) throws AlreadyClosedException { } protected final void ensureOpen ( ) throws AlreadyClosedException { } public void message ( String message ) { } private synchronized void setMessageID ( PrintStream infoStream ) { } CodecProvider codecs ; public IndexWriter ( Directory d , IndexWriterConfig conf ) throws IOException , CorruptIndexException , LockObtainFailedException { } private FieldInfos getCurrentFieldInfos ( ) throws IOException { } private synchronized void setRollbackSegmentInfos ( SegmentInfos infos ) { } public IndexWriterConfig getConfig ( ) { } private void pushMaxBufferedDocs ( ) { } public static void setDefaultInfoStream ( PrintStream infoStream ) { } public static PrintStream getDefaultInfoStream ( ) { } public void setInfoStream ( PrintStream infoStream ) { } private void messageState ( ) { } public PrintStream getInfoStream ( ) { } public boolean verbose ( ) { } public void close ( ) throws IOException , CorruptIndexException { } public void close ( boolean waitForMerges ) throws IOException , CorruptIndexException { } private synchronized boolean shouldClose ( ) { } private void closeInternal ( boolean waitForMerges ) throws IOException , CorruptIndexException { } private synchronized boolean flushDocStores ( ) throws IOException { } public Directory getDirectory ( ) { } public Analyzer getAnalyzer ( ) { } public synchronized int maxDoc ( ) { } public synchronized int numDocs ( ) throws IOException { int count ; if ( ( docWriter ) != null ) count = docWriter . getNumDocsInRAM ( ) ; else count = 0 ; for ( int i = 0 ; i < ( segmentInfos . size ( ) ) ; i ++ ) { final SegmentInfo info = segmentInfos . info ( i ) ; <START_BUG> count += ( info . docCount ) - ( info . getDelCount ( ) ) ; <END_BUG> } return count ; } public synchronized boolean hasDeletions ( ) throws IOException { } private int maxFieldLength ; public void addDocument ( Document doc ) throws IOException , CorruptIndexException { } public void addDocument ( Document doc , Analyzer analyzer ) throws IOException , CorruptIndexException { } public void deleteDocuments ( Term term ) throws IOException , CorruptIndexException { } public void deleteDocuments ( Term ... terms ) throws IOException , CorruptIndexException { } public void deleteDocuments ( Query query ) throws IOException , CorruptIndexException { } public void deleteDocuments ( Query ... queries ) throws IOException , CorruptIndexException { } public void updateDocument ( Term term , Document doc ) throws IOException , CorruptIndexException { } public void updateDocument ( Term term , Document doc , Analyzer analyzer ) throws IOException , CorruptIndexException { } final synchronized int getSegmentCount ( ) { } final synchronized int getNumBufferedDocuments ( ) { } final synchronized int getDocCount ( int i ) { } final synchronized int getFlushCount ( ) { } final synchronized int getFlushDeletesCount ( ) { } final String newSegmentName ( ) { } private PrintStream infoStream = null ; private static PrintStream defaultInfoStream = null ; public void optimize ( ) throws IOException , CorruptIndexException { } public void optimize ( int maxNumSegments ) throws IOException , CorruptIndexException { } public void optimize ( boolean doWait ) throws IOException , CorruptIndexException { } public void optimize ( int maxNumSegments , boolean doWait ) throws IOException , CorruptIndexException { } private synchronized boolean optimizeMergesPending ( ) { } public void expungeDeletes ( boolean doWait ) throws IOException , CorruptIndexException { } public void expungeDeletes ( ) throws IOException , CorruptIndexException { } public final void maybeMerge ( ) throws IOException , CorruptIndexException { } private final void maybeMerge ( boolean optimize ) throws IOException , CorruptIndexException { } private final void maybeMerge ( int maxNumSegmentsOptimize , boolean optimize ) throws IOException , CorruptIndexException { } private synchronized void updatePendingMerges ( int maxNumSegmentsOptimize , boolean optimize ) throws IOException , CorruptIndexException { } synchronized OneMerge getNextMerge ( ) { }

Fix:
count += ( info . docCount ) - ( numDeletedDocs ( info ) ) ;