From c1f0069b9725e77b67a9ed3f7c9f9238bec82c34 Mon Sep 17 00:00:00 2001
From: Markus Schorn <markus.schorn@windriver.com>
Date: Fri, 23 Dec 2011 10:54:58 +0100
Subject: [PATCH] Bug 367315: Incremental update for headers parsed in context.

---
 .../internal/index/tests/IndexBugsTests.java  | 41 +++++++++-
 .../index/IndexBasedFileContentProvider.java  | 17 ++---
 .../core/parser/scanner/CPreprocessor.java    | 11 ++-
 .../scanner/InternalFileContentProvider.java  | 13 +++-
 .../core/pdom/AbstractIndexerTask.java        | 74 ++++++++++++-------
 5 files changed, 113 insertions(+), 43 deletions(-)

diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexBugsTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexBugsTests.java
index 293cbc8ae73..86963579eaf 100644
--- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexBugsTests.java
+++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexBugsTests.java
@@ -2394,7 +2394,8 @@ public class IndexBugsTests extends BaseTestCase {
 	    final Set<IFolder> folders = new HashSet<IFolder>();
 	    folders.add(root);
     	root.accept(new IResourceVisitor() {
-    		public boolean visit(final IResource resource) throws CoreException {
+    		@Override
+			public boolean visit(final IResource resource) throws CoreException {
     			if (resource instanceof IFile) {
     				files.add((IFile) resource);
     			} else if (resource instanceof IFolder) {
@@ -2431,4 +2432,42 @@ public class IndexBugsTests extends BaseTestCase {
 			index.releaseReadLock();
 		}
 	}
+	
+	//	// context.c
+	//	#define A B
+	//	#include "b.h" // file name is important to reproduce the issue
+	//	#include "a.h" // file name is important to reproduce the issue
+
+	//	// a.h and b.h
+	//	int A;
+	public void testUpdatingHeaderInContext_367315() throws Exception {
+		String[] contents= getContentsForTest(2);
+		final IIndexManager indexManager = CCorePlugin.getIndexManager();
+		TestSourceReader.createFile(fCProject.getProject(), "context.c", contents[0]);
+		IFile ah= TestSourceReader.createFile(fCProject.getProject(), "a.h", contents[1]);
+		IFile bh= TestSourceReader.createFile(fCProject.getProject(), "b.h", contents[1]);
+		indexManager.reindex(fCProject);
+		waitForIndexer();
+		fIndex.acquireReadLock();
+		try {
+			IIndexBinding[] vars = fIndex.findBindings("B".toCharArray(), IndexFilter.ALL_DECLARED, new NullProgressMonitor());
+			assertEquals(1, vars.length);
+			assertEquals(2, fIndex.findDefinitions(vars[0]).length);
+		} finally {
+			fIndex.releaseReadLock();
+		}
+		
+		final CoreModel coreModel = CCorePlugin.getDefault().getCoreModel();
+		ICElement[] selection = new ICElement[] {coreModel.create(ah), coreModel.create(bh)};
+		indexManager.update(selection, IIndexManager.UPDATE_ALL);
+		waitForIndexer();
+		fIndex.acquireReadLock();
+		try {
+			IIndexBinding[] vars = fIndex.findBindings("B".toCharArray(), IndexFilter.ALL_DECLARED, new NullProgressMonitor());
+			assertEquals(1, vars.length);
+			assertEquals(2, fIndex.findDefinitions(vars[0]).length);
+		} finally {
+			fIndex.releaseReadLock();
+		}
+	}
 }
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexBasedFileContentProvider.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexBasedFileContentProvider.java
index ccfc664eed6..944e6cc9817 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexBasedFileContentProvider.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexBasedFileContentProvider.java
@@ -47,7 +47,6 @@ import org.eclipse.core.runtime.CoreException;
  * Code reader factory, that fakes code readers for header files already stored in the index.
  */
 public final class IndexBasedFileContentProvider extends InternalFileContentProvider {
-	private static final class NeedToParseException extends Exception {}
 	private static final String GAP = "__gap__"; //$NON-NLS-1$
 
 	private final IIndex fIndex;
@@ -135,7 +134,7 @@ public final class IndexBasedFileContentProvider extends InternalFileContentProv
 					// Report pragma once inclusions, only if no exception was thrown.
 					fPragmaOnce.putAll(newPragmaOnce);
 					return new InternalFileContent(path, macros, directives, files, toList(preLoaded));
-				} catch (NeedToParseException e) {
+				} catch (DependsOnOutdatedFileException e) {
 				}
 			} 
 		} catch (CoreException e) {
@@ -188,7 +187,7 @@ public final class IndexBasedFileContentProvider extends InternalFileContentProv
 			Map<IIndexFileLocation, IFileNomination> newPragmaOnce,
 			LinkedHashSet<IIndexFile> preLoaded, List<IIndexFile> files,
 			List<IIndexMacro> macros, List<ICPPUsingDirective> usingDirectives,
-			Set<IIndexFile> preventRecursion) throws CoreException, NeedToParseException {
+			Set<IIndexFile> preventRecursion) throws CoreException, DependsOnOutdatedFileException {
 		if (file.equals(stopAt))
 			return true;
 		
@@ -211,8 +210,6 @@ public final class IndexBasedFileContentProvider extends InternalFileContentProv
 		final Object[] pds;
 		if (fRelatedIndexerTask != null) {
 			IndexFileContent content= fRelatedIndexerTask.getFileContent(fLinkage, ifl, file);
-			if (content == null) 
-				throw new NeedToParseException();
 			uds= content.getUsingDirectives();
 			pds= content.getPreprocessingDirectives();
 		} else {
@@ -252,7 +249,7 @@ public final class IndexBasedFileContentProvider extends InternalFileContentProv
 
 	@Override
 	public InternalFileContent getContentForContextToHeaderGap(String path,
-			IMacroDictionary macroDictionary) {
+			IMacroDictionary macroDictionary) throws DependsOnOutdatedFileException {
 		if (fContextToHeaderGap == null) {
 			return null;
 		}
@@ -268,12 +265,8 @@ public final class IndexBasedFileContentProvider extends InternalFileContentProv
 			ArrayList<IIndexMacro> macros= new ArrayList<IIndexMacro>();
 			ArrayList<ICPPUsingDirective> directives= new ArrayList<ICPPUsingDirective>();
 			LinkedHashSet<IIndexFile> preLoaded= new LinkedHashSet<IIndexFile>();
-			try {
-				if (!collectFileContent(contextFile, targetFile, newPragmaOnce, preLoaded,
-						filesIncluded, macros, directives, new HashSet<IIndexFile>())) {
-					return null;
-				}
-			} catch (NeedToParseException e) {
+			if (!collectFileContent(contextFile, targetFile, newPragmaOnce, preLoaded,
+					filesIncluded, macros, directives, new HashSet<IIndexFile>())) {
 				return null;
 			}
 
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java
index c84bd8ff4da..7f216f7b0a6 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java
@@ -57,6 +57,7 @@ import org.eclipse.cdt.internal.core.parser.IMacroDictionary;
 import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator.EvalException;
 import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent.FileVersion;
 import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent.InclusionKind;
+import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContentProvider.DependsOnOutdatedFileException;
 import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
 import org.eclipse.cdt.internal.core.parser.scanner.MacroDefinitionParser.InvalidMacroDefinitionException;
 import org.eclipse.cdt.internal.core.parser.scanner.ScannerContext.BranchKind;
@@ -465,8 +466,14 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable {
 			fPreIncludedFiles= null;
 		}
         final String location = fLocationMap.getTranslationUnitPath();
-		InternalFileContent content= fFileContentProvider.getContentForContextToHeaderGap(location,
-				fMacroDictionaryFacade);
+		InternalFileContent content;
+		try {
+			content = fFileContentProvider.getContentForContextToHeaderGap(location,
+					fMacroDictionaryFacade);
+		} catch (DependsOnOutdatedFileException e) {
+			// Abort the parser, handled by the abstract indexer task.
+			throw new RuntimeException(e);
+		}
 		if (content != null && content.getKind() == InclusionKind.FOUND_IN_INDEX) {
 			processInclusionFromIndex(0, content, false);
 		}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/InternalFileContentProvider.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/InternalFileContentProvider.java
index 82668c6293b..ed41e88c356 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/InternalFileContentProvider.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/InternalFileContentProvider.java
@@ -25,6 +25,7 @@ import org.eclipse.cdt.core.index.IIndexFileLocation;
 import org.eclipse.cdt.core.parser.ISignificantMacros;
 import org.eclipse.cdt.core.parser.IncludeFileContentProvider;
 import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics;
+import org.eclipse.cdt.internal.core.index.IIndexFragmentFile;
 import org.eclipse.cdt.internal.core.parser.IMacroDictionary;
 import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent.InclusionKind;
 
@@ -32,6 +33,15 @@ import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent.Inclusio
  * Internal implementation of the file content providers
  */
 public abstract class InternalFileContentProvider extends IncludeFileContentProvider {
+	public static final class DependsOnOutdatedFileException extends Exception {
+		public final Object fTu;
+		public final IIndexFragmentFile fIndexFile;
+		public DependsOnOutdatedFileException(Object tu, IIndexFragmentFile file) {
+			fTu= tu;
+			fIndexFile= file;
+		}
+	}
+
 	private IIncludeFileResolutionHeuristics fIncludeResolutionHeuristics;
     private final Map<String, IFileNomination> fPragmaOnce= new HashMap<String, IFileNomination>();
     private final Map<String, List<ISignificantMacros>> fLoadedVersions= new HashMap<String, List<ISignificantMacros>>();
@@ -64,9 +74,10 @@ public abstract class InternalFileContentProvider extends IncludeFileContentProv
 	 * or <code>null</code> if this cannot be done.
 	 * @param filePath the absolute location of the file.
 	 * @param macroDictionary macros defined at the inclusion point.
+	 * @throws DependsOnOutdatedFileException 
 	 */
 	public InternalFileContent getContentForContextToHeaderGap(String filePath,
-			IMacroDictionary macroDictionary) {
+			IMacroDictionary macroDictionary) throws DependsOnOutdatedFileException {
 		return null;
 	}
 
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java
index 841882a5e13..ffc4d33176f 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java
@@ -24,6 +24,7 @@ import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.eclipse.cdt.core.CCorePlugin;
 import org.eclipse.cdt.core.dom.IPDOMIndexerTask;
@@ -54,6 +55,7 @@ import org.eclipse.cdt.internal.core.index.IWritableIndex;
 import org.eclipse.cdt.internal.core.index.IndexBasedFileContentProvider;
 import org.eclipse.cdt.internal.core.parser.IMacroDictionary;
 import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContentProvider;
+import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContentProvider.DependsOnOutdatedFileException;
 import org.eclipse.cdt.internal.core.parser.util.LRUCache;
 import org.eclipse.cdt.utils.EFSExtensionManager;
 import org.eclipse.core.runtime.CoreException;
@@ -783,7 +785,7 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 					return;
 				final Object tu = locTask.fTu;
 				final IScannerInfo scannerInfo= fResolver.getBuildConfiguration(linkageID, tu);
-				parseFile(tu, linkageID, ifl, scannerInfo, null, monitor);
+				parseFile(tu, getLanguage(tu, linkageID), ifl, scannerInfo, null, monitor);
 			}
 		}
 		
@@ -817,7 +819,7 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 						return;
 					final Object tu = locTask.fTu;
 					final IScannerInfo scannerInfo= fResolver.getBuildConfiguration(linkageID, tu);
-					parseFile(tu, linkageID, ifl, scannerInfo, null, monitor);
+					parseFile(tu, getLanguage(tu, linkageID), ifl, scannerInfo, null, monitor);
 					if (locTask.isCompleted())
 						it.remove();
 					
@@ -870,8 +872,24 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 				return;
 			
 			final IScannerInfo scannerInfo= fResolver.getBuildConfiguration(linkageID, contextTu);
-			FileContext ctx= new FileContext(ctxFile, headerFile);
-			parseFile(tu, linkageID, ifl, scannerInfo, ctx, monitor);
+			final AbstractLanguage language = getLanguage(contextTu, linkageID);
+			final FileContext ctx= new FileContext(ctxFile, headerFile);
+			Set<IIndexFile> dependencies= null;
+			boolean done= false;
+			while (!done) {
+				done= true;
+				DependsOnOutdatedFileException d= parseFile(tu, language, ifl, scannerInfo, ctx, monitor);
+				if (d != null) {
+					// File was not parsed, because there is a dependency that needs to be
+					// handled before
+					if (dependencies == null)
+						dependencies= new HashSet<IIndexFile>();
+					if (dependencies.add(d.fIndexFile)) {
+						if (parseFile(d.fTu, language, d.fIndexFile.getLocation(), scannerInfo, new FileContext(ctxFile, d.fIndexFile), monitor) == null)
+							done= false;
+					}
+				} 
+			} 
 			if (!ctx.fLostPragmaOnceSemantics) 
 				return;
 
@@ -929,21 +947,9 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 	}
 
 
-	private void parseFile(Object tu, int linkageID, IIndexFileLocation ifl, IScannerInfo scanInfo,
+	private DependsOnOutdatedFileException parseFile(Object tu, AbstractLanguage lang, IIndexFileLocation ifl, IScannerInfo scanInfo,
 			FileContext ctx, IProgressMonitor pm) throws CoreException, InterruptedException {
 		IPath path= getLabel(ifl);
-		AbstractLanguage[] langs= fResolver.getLanguages(tu, true);
-		AbstractLanguage lang= null;
-		for (AbstractLanguage lang2 : langs) {
-			if (lang2.getLinkageID() == linkageID) {
-				lang= lang2;
-				break;
-			}
-		}
-		if (lang == null) {
-			return;
-		}
-		
 		Throwable th= null;
 		try {
 			if (fShowActivity) {
@@ -951,18 +957,21 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 			}
 			pm.subTask(getMessage(MessageKind.parsingFileTask,
 					path.lastSegment(), path.removeLastSegments(1).toString()));
-			long start= System.currentTimeMillis();
 			FileContent codeReader= fResolver.getCodeReader(tu);
-			IIndexFile[] ctxFiles = ctx == null ? null : new IIndexFile[] {ctx.fContext, ctx.fOldFile};
+			final boolean isSource = fResolver.isSourceUnit(tu);
 
-			IASTTranslationUnit ast= createAST(tu, lang, codeReader, scanInfo, fASTOptions, ctxFiles, pm);
+			long start= System.currentTimeMillis();
+			IASTTranslationUnit ast= createAST(lang, codeReader, scanInfo, isSource, fASTOptions, ctx, pm);
 			fStatistics.fParsingTime += System.currentTimeMillis() - start;
 			if (ast != null) {
-				writeToIndex(linkageID, ast, codeReader.getContentsHash(), ctx, pm);
+				writeToIndex(lang.getLinkageID(), ast, codeReader.getContentsHash(), ctx, pm);
 			}
 		} catch (CoreException e) {
 			th= e;
 		} catch (RuntimeException e) {
+			final Throwable cause = e.getCause();
+			if (cause instanceof DependsOnOutdatedFileException)
+				return (DependsOnOutdatedFileException) cause;
 			th= e;
 		} catch (StackOverflowError e) {
 			th= e;
@@ -976,6 +985,16 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 		if (th != null) {
 			swallowError(path, th);
 		}
+		return null;
+	}
+
+	private AbstractLanguage getLanguage(Object tu, int linkageID) {
+		for (AbstractLanguage language : fResolver.getLanguages(tu, true)) {
+			if (language.getLinkageID() == linkageID) {
+				return language;
+			}
+		}
+		return null;
 	}
 	
 	private IPath getLabel(IIndexFileLocation ifl) {
@@ -1033,13 +1052,13 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 		return e;
 	}
 
-	private final IASTTranslationUnit createAST(Object tu, AbstractLanguage language,
-			FileContent codeReader, IScannerInfo scanInfo, int options,
-			IIndexFile[] ctx2header, IProgressMonitor pm) throws CoreException {
+	private final IASTTranslationUnit createAST(AbstractLanguage language, FileContent codeReader,
+			IScannerInfo scanInfo, boolean isSource, int options,
+			FileContext ctx, IProgressMonitor pm) throws CoreException {
 		if (codeReader == null) {
 			return null;
 		}
-		if (fResolver.isSourceUnit(tu)) {
+		if (isSource) {
 			options |= ILanguage.OPTION_IS_SOURCE_UNIT;
 		}
 		if (fFileSizeLimit > 0 && fResolver.getFileSize(codeReader.getFileLocation()) > fFileSizeLimit) {
@@ -1048,6 +1067,7 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 			}
 			return null;
 		}
+		final IIndexFile[] ctx2header = ctx == null ? null : new IIndexFile[] {ctx.fContext, ctx.fOldFile};
 		if (fCodeReaderFactory == null) {
 			InternalFileContentProvider fileContentProvider = createInternalFileContentProvider();
 			if (fIsFastIndexer) {
@@ -1163,14 +1183,14 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
 	}
 
 	public final IndexFileContent getFileContent(int linkageID, IIndexFileLocation ifl,
-			IIndexFile file) throws CoreException {
+			IIndexFile file) throws CoreException, DependsOnOutdatedFileException {
 		LinkageTask map = findRequestMap(linkageID);
 		if (map != null) {
 			LocationTask request= map.find(ifl);
 			if (request != null) {
 				FileVersionTask task= request.findVersion(file);
 				if (task != null && task.fOutdated)
-					return null;
+					throw new DependsOnOutdatedFileException(request.fTu, task.fIndexFile);
 			}
 		}
 		IndexFileContent fc= fIndexContentCache.get(file);