From 9d7a23cde51a78cfc4e44eaca91cf2068d146405 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Fri, 2 May 2014 10:31:19 -0700 Subject: [PATCH] Bug 414692 - Organize Includes has to honor @headername{
} in doxygen comments --- .../cdt/core/testplugin/TestScannerInfo.java | 14 ++- .../core/testplugin/TestScannerProvider.java | 9 +- .../eclipse/cdt/core/index/IIndexFile.java | 17 +++ .../cdt/internal/core/index/CIndex.java | 106 +++++++++-------- .../core/pdom/AbstractIndexerTask.java | 107 +++++++++++++++++- .../eclipse/cdt/internal/core/pdom/PDOM.java | 7 +- .../cdt/internal/core/pdom/PDOMWriter.java | 30 +++-- .../cdt/internal/core/pdom/dom/PDOMFile.java | 31 ++++- .../core/pdom/indexer/PDOMIndexerTask.java | 17 ++- .../cdt/core/CCorePreferenceConstants.java | 20 +++- .../core/CCorePreferenceInitializer.java | 3 +- .../includes/IncludeOrganizerTest.java | 53 ++++++++- .../includes/IncludesTestBase.java | 13 ++- .../ui/preferences/IncludePragmasBlock.java | 7 +- .../ui/preferences/PreferencesMessages.java | 3 +- .../PreferencesMessages.properties | 9 +- .../includes/HeaderSubstitutor.java | 55 ++++++++- 17 files changed, 396 insertions(+), 105 deletions(-) diff --git a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/TestScannerInfo.java b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/TestScannerInfo.java index 5732bf1ecb3..60c58ee6efb 100644 --- a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/TestScannerInfo.java +++ b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/TestScannerInfo.java @@ -19,13 +19,15 @@ import org.eclipse.cdt.core.parser.ExtendedScannerInfo; public class TestScannerInfo extends ExtendedScannerInfo { private static final String[] EMPTY = {}; private String[] fIncludes; + private String[] fLocalIncludes; private String[] fIncludeFiles; private String[] fMacroFiles; private Map fDefinedSymbols; - public TestScannerInfo(String[] includes, String[] macroFiles, String[] includeFiles, - Map definedSymbols) { + public TestScannerInfo(String[] includes, String[] localIncludes, String[] macroFiles, + String[] includeFiles, Map definedSymbols) { fIncludes= includes; + fLocalIncludes= localIncludes; fIncludeFiles= includeFiles; fMacroFiles= macroFiles; fDefinedSymbols= definedSymbols; @@ -42,13 +44,13 @@ public class TestScannerInfo extends ExtendedScannerInfo { } @Override - public String[] getIncludeFiles() { - return fIncludeFiles == null ? EMPTY: fIncludeFiles; + public String[] getLocalIncludePath() { + return fLocalIncludes; } @Override - public String[] getLocalIncludePath() { - return null; + public String[] getIncludeFiles() { + return fIncludeFiles == null ? EMPTY: fIncludeFiles; } @Override diff --git a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/TestScannerProvider.java b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/TestScannerProvider.java index f0d8008e6e5..bd02a94ee23 100644 --- a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/TestScannerProvider.java +++ b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/TestScannerProvider.java @@ -22,19 +22,20 @@ import org.eclipse.core.resources.IResource; public class TestScannerProvider extends AbstractCExtension implements IScannerInfoProvider { public static String[] sIncludes; + public static String[] sLocalIncludes; public static String[] sIncludeFiles; public static String[] sMacroFiles; public static Map sDefinedSymbols = new HashMap<>(); public final static String SCANNER_ID = CTestPlugin.PLUGIN_ID + ".TestScanner"; - + public static void clear() { - sIncludes= sIncludeFiles= sMacroFiles= null; + sIncludes= sLocalIncludes= sIncludeFiles= sMacroFiles= null; sDefinedSymbols.clear(); } - + @Override public IScannerInfo getScannerInformation(IResource resource) { - return new TestScannerInfo(sIncludes, sMacroFiles, sIncludeFiles, sDefinedSymbols); + return new TestScannerInfo(sIncludes, sLocalIncludes, sMacroFiles, sIncludeFiles, sDefinedSymbols); } @Override diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/index/IIndexFile.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/index/IIndexFile.java index eb63038b785..61ba0c48103 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/index/IIndexFile.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/index/IIndexFile.java @@ -107,6 +107,23 @@ public interface IIndexFile extends IFileNomination { */ int getLinkageID() throws CoreException; + /** + * Returns the name of the replacement header obtained from @headername{header} or + * from {@code IWYU pragma: private, include "header"}. Returns an empty string if the file + * contained {@code IWYU pragma: private} without a replacement header. Returns {@code null} if + * the file does not contain @headername{header} or {@code IWYU pragma: private}. + * @since 5.7 + */ + String getReplacementHeader() throws CoreException; + + /** + * Sets the name of the replacement header. + * @param replacementHeader the name of the replacement header, may be {@code null} or an empty + * string + * @since 5.7 + */ + void setReplacementHeader(String replacementHeader) throws CoreException; + /** * Returns detailed information about the file. For debugging only. * @since 5.4 diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/CIndex.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/CIndex.java index 70272400702..c3cbbbd6050 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/CIndex.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/CIndex.java @@ -15,12 +15,13 @@ *******************************************************************************/ package org.eclipse.cdt.internal.core.index; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.regex.Pattern; @@ -314,37 +315,35 @@ public class CIndex implements IIndex { @Override public IIndexInclude[] findIncludedBy(IIndexFile file, int depth) throws CoreException { List result= new ArrayList<>(); - findIncludedBy(file.getLinkageID(), Collections.singletonList(file), result, depth, - new HashSet()); - return result.toArray(new IIndexInclude[result.size()]); - } - - public void findIncludedBy(int linkageID, List in, List out, int depth, - HashSet handled) throws CoreException { - List nextLevel= depth != 0 ? new LinkedList() : null; - for (IIndexFile iIndexFile : in) { - IIndexFragmentFile file = (IIndexFragmentFile) iIndexFile; - for (IIndexFragment fragment : fFragments) { - IIndexInclude[] includedBy= fragment.findIncludedBy(file); - for (IIndexInclude include : includedBy) { - final IIndexFile includer = include.getIncludedBy(); - FileContentKey key= new FileContentKey(linkageID, includer.getLocation(), includer.getSignificantMacros()); - if (handled.add(key)) { - out.add(include); - if (nextLevel != null) { - nextLevel.add(includer); + Collection in = Collections.singletonList(file); + Set handled = new HashSet<>(); + while (true) { + Collection nextLevel= depth != 0 ? new ArrayDeque() : null; + for (IIndexFile indexFile : in) { + IIndexFragmentFile file1 = (IIndexFragmentFile) indexFile; + for (IIndexFragment fragment : fFragments) { + IIndexInclude[] includedBy= fragment.findIncludedBy(file1); + for (IIndexInclude include : includedBy) { + final IIndexFile includer = include.getIncludedBy(); + FileContentKey key= new FileContentKey(file.getLinkageID(), includer.getLocation(), includer.getSignificantMacros()); + if (handled.add(key)) { + result.add(include); + if (nextLevel != null) { + nextLevel.add(includer); + } } } } } + if (nextLevel == null || nextLevel.isEmpty()) { + break; + } + if (depth > 0) { + depth--; + } + in = nextLevel; } - if (nextLevel == null || nextLevel.isEmpty()) { - return; - } - if (depth > 0) { - depth--; - } - findIncludedBy(linkageID, nextLevel, out, depth, handled); + return result.toArray(new IIndexInclude[result.size()]); } @Override @@ -355,37 +354,36 @@ public class CIndex implements IIndex { @Override public IIndexInclude[] findIncludes(IIndexFile file, int depth) throws CoreException { List result= new ArrayList<>(); - findIncludes(Collections.singletonList(file), result, depth, new HashSet<>()); - return result.toArray(new IIndexInclude[result.size()]); - } - - private void findIncludes(List in, List out, int depth, - HashSet handled) throws CoreException { - List nextLevel= depth != 0 ? new LinkedList() : null; - for (IIndexFile iIndexFile : in) { - IIndexFragmentFile file = (IIndexFragmentFile) iIndexFile; - IIndexInclude[] includes= file.getIncludes(); - for (IIndexInclude include : includes) { - IIndexFileLocation target= include.getIncludesLocation(); - Object key= target != null ? (Object) target : include.getFullName(); - if (handled.add(key)) { - out.add(include); - if (nextLevel != null) { - IIndexFile includedByFile= resolveInclude(include); - if (includedByFile != null) { - nextLevel.add(includedByFile); + Collection in = Collections.singletonList(file); + Set handled = new HashSet<>(); + while (true) { + Collection nextLevel= depth != 0 ? new ArrayDeque() : null; + for (IIndexFile indexFile : in) { + IIndexFragmentFile file1 = (IIndexFragmentFile) indexFile; + IIndexInclude[] includes= file1.getIncludes(); + for (IIndexInclude include : includes) { + IIndexFileLocation target= include.getIncludesLocation(); + Object key= target != null ? (Object) target : include.getFullName(); + if (handled.add(key)) { + result.add(include); + if (nextLevel != null) { + IIndexFile includedByFile= resolveInclude(include); + if (includedByFile != null) { + nextLevel.add(includedByFile); + } } } } } + if (nextLevel == null || nextLevel.isEmpty()) { + break; + } + if (depth > 0) { + depth--; + } + in = nextLevel; } - if (nextLevel == null || nextLevel.isEmpty()) { - return; - } - if (depth > 0) { - depth--; - } - findIncludes(nextLevel, out, depth, handled); + return result.toArray(new IIndexInclude[result.size()]); } @Override @@ -398,7 +396,7 @@ public class CIndex implements IIndex { } } finally { if (i < fFragments.length) { - // rollback + // Rollback. fReadLock--; while (--i >= 0) { fFragments[i].releaseReadLock(); 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 54899757905..394b428f23c 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 @@ -27,10 +27,14 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.ILinkage; import org.eclipse.cdt.core.dom.IPDOMIndexerTask; +import org.eclipse.cdt.core.dom.ast.IASTComment; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit.IDependencyTree; @@ -87,6 +91,8 @@ public abstract class AbstractIndexerTask extends PDOMWriter { // Order of constants is important. Stronger update types have to precede the weaker ones. private static enum UpdateKind { REQUIRED_SOURCE, REQUIRED_HEADER, ONE_LINKAGE_HEADER, OTHER_HEADER } + private static final Pattern HEADERNAME_PATTERN = Pattern.compile("@headername\\{(?
[^\\}]+)\\}"); //$NON-NLS-1$ + private static class LinkageTask { final int fLinkageID; private final Map fLocationTasks; @@ -298,6 +304,7 @@ public abstract class AbstractIndexerTask extends PDOMWriter { private boolean fIndexFilesWithoutConfiguration= true; private boolean fIndexAllHeaderVersions = false; private Set fHeadersToIndexAllVersions = Collections.emptySet(); + private Pattern fPragmaPrivatePattern; private List fRequestsPerLinkage= new ArrayList<>(); private Map fIndexContentCache= new LRUCache<>(500); private Map fIndexFilesCache= new LRUCache<>(5000); @@ -371,6 +378,10 @@ public abstract class AbstractIndexerTask extends PDOMWriter { fHeadersToIndexAllVersions = headers; } + public void setPragmaPrivatePattern(Pattern pattern) { + fPragmaPrivatePattern = pattern; + } + /** * @see IPDOMIndexerTask#acceptUrgentTask(IPDOMIndexerTask) */ @@ -1233,15 +1244,28 @@ public abstract class AbstractIndexerTask extends PDOMWriter { // The default processing is handled by the indexer task. PDOMWriter.Data data = new PDOMWriter.Data(ast, fileKeys, fIndex); int storageLinkageID = process(ast, data); - if (storageLinkageID != ILinkage.NO_LINKAGE_ID) - addSymbols(data, storageLinkageID, ctx, fTodoTaskUpdater, pm); + if (storageLinkageID != ILinkage.NO_LINKAGE_ID) { + IASTComment[] comments = ast.getComments(); + data.fReplacementHeaders = extractReplacementHeaders(comments, pm); + + addSymbols(data, storageLinkageID, ctx, pm); + + // Update task markers. + if (fTodoTaskUpdater != null) { + Set locations= new HashSet<>(); + for (FileInAST file : data.fSelectedFiles) { + locations.add(file.fileContentKey.getLocation()); + } + fTodoTaskUpdater.updateTasks(comments, locations.toArray(new IIndexFileLocation[locations.size()])); + } + } // Contributed processors now have an opportunity to examine the AST. for (IPDOMASTProcessor processor : PDOMASTProcessorManager.getProcessors(ast)) { data = new PDOMWriter.Data(ast, fileKeys, fIndex); storageLinkageID = processor.process(ast, data); if (storageLinkageID != ILinkage.NO_LINKAGE_ID) - addSymbols(data, storageLinkageID, ctx, fTodoTaskUpdater, pm); + addSymbols(data, storageLinkageID, ctx, pm); } } catch (CoreException | RuntimeException | Error e) { // Avoid parsing files again, that caused an exception to be thrown. @@ -1289,6 +1313,83 @@ public abstract class AbstractIndexerTask extends PDOMWriter { } } + /** + * Parses comments to extract replacement headers from @headername{header} and + * {@code IWYU pragma: private}. + * + * @return replacement headers keyed by file paths + */ + private Map extractReplacementHeaders(IASTComment[] comments, IProgressMonitor pm) { + Map replacementHeaders = new HashMap<>(); + StringBuilder text = new StringBuilder(); + IASTFileLocation carryoverLocation = null; + for (int i = 0; i < comments.length; i++) { + IASTComment comment = comments[i]; + IASTFileLocation location = comment.getFileLocation(); + if (location == null) + continue; + String fileName = location.getFileName(); + if (replacementHeaders.containsKey(fileName)) + continue; + char[] commentChars = comment.getComment(); + if (commentChars.length <= 2) + continue; + if (carryoverLocation == null || + !location.getFileName().equals(carryoverLocation.getFileName()) || + location.getStartingLineNumber() != carryoverLocation.getEndingLineNumber() + 1) { + text.delete(0, text.length()); + } + carryoverLocation = null; + text.append(commentChars, 2, commentChars.length - 2); + // Look for @headername{header}. + Matcher matcher = HEADERNAME_PATTERN.matcher(text); + if (matcher.find()) { + String header = matcher.group("header"); //$NON-NLS-1$ + if (header == null) { + header = ""; //$NON-NLS-1$ + } else { + // Normalize the header list. + header = header.replace(" or ", ",").replace(" ", ""); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ + } + replacementHeaders.put(fileName, header); + continue; + } + if (fPragmaPrivatePattern != null) { + // Look for IWYU pragma: private + matcher = fPragmaPrivatePattern.matcher(text); + if (matcher.find()) { + if (!isWhitespace(text, 0, matcher.start())) + continue; // Extraneous text before the pragma. + if (isWhitespace(text, matcher.end(), text.length())) { + String header = matcher.group("header"); //$NON-NLS-1$ + if (header == null) + header = ""; //$NON-NLS-1$ + replacementHeaders.put(fileName, header); + continue; + } + // Handle the case when a IWYU pragma is split between two comment lines as: + // IWYU pragma: private, + // include "header" + if (text.charAt(matcher.end()) == ',' && + isWhitespace(text, matcher.end() + 1, text.length())) { + // Defer processing until the next comment, which will be appended to this + // one. + carryoverLocation = location; + } + } + } + } + return replacementHeaders; + } + + private boolean isWhitespace(CharSequence text, int start, int end) { + while (start < end) { + if (text.charAt(start++) > ' ') + return false; + } + return true; + } + public final IndexFileContent getFileContent(int linkageID, IIndexFileLocation ifl, IIndexFile file) throws CoreException, DependsOnOutdatedFileException { LinkageTask map = findRequestMap(linkageID); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java index f89ab42a6c0..8e37de1ff32 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java @@ -250,10 +250,11 @@ public class PDOM extends PlatformObject implements IPDOM { * * CDT 8.4 development (versions not supported on the 8.3.x branch) * 170.0 - Unconditionally store arguments of EvalTypeId, bug 430230. + * 171.0 - Replacement headers for Organize Includes, bug 414692. */ - private static final int MIN_SUPPORTED_VERSION= version(170, 0); - private static final int MAX_SUPPORTED_VERSION= version(170, Short.MAX_VALUE); - private static final int DEFAULT_VERSION = version(170, 0); + private static final int MIN_SUPPORTED_VERSION= version(171, 0); + private static final int MAX_SUPPORTED_VERSION= version(171, Short.MAX_VALUE); + private static final int DEFAULT_VERSION = version(171, 0); private static int version(int major, int minor) { return (major << 16) + minor; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java index 9458226a88b..20e570d82b6 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java @@ -53,6 +53,7 @@ import org.eclipse.cdt.core.index.IIndexFileLocation; import org.eclipse.cdt.core.index.IIndexInclude; import org.eclipse.cdt.core.index.IIndexSymbols; import org.eclipse.cdt.core.index.IPDOMASTProcessor; +import org.eclipse.cdt.core.index.IndexLocationFactory; import org.eclipse.cdt.core.parser.FileContent; import org.eclipse.cdt.core.parser.IProblem; import org.eclipse.cdt.core.parser.ISignificantMacros; @@ -77,7 +78,7 @@ import org.eclipse.osgi.util.NLS; * Abstract class to write information from AST. * @since 4.0 */ -abstract public class PDOMWriter implements IPDOMASTProcessor { +public abstract class PDOMWriter implements IPDOMASTProcessor { private static final boolean REPORT_UNKNOWN_BUILTINS = false; public static class FileInAST { @@ -146,7 +147,8 @@ abstract public class PDOMWriter implements IPDOMASTProcessor { final IWritableIndex fIndex; final Map fSymbolMap = new HashMap<>(); final Set fContextIncludes = new HashSet<>(); - final List fStatuses= new ArrayList<>(); + final List fStatuses = new ArrayList<>(); + Map fReplacementHeaders; // Replacement headers keyed by file paths. public Data(IASTTranslationUnit ast, FileInAST[] selectedFiles, IWritableIndex index) { fAST= ast; @@ -254,8 +256,7 @@ abstract public class PDOMWriter implements IPDOMASTProcessor { * the index after your last write operation. */ final protected void addSymbols(Data data, int storageLinkageID, FileContext ctx, - ITodoTaskUpdater taskUpdater, IProgressMonitor pm) - throws InterruptedException, CoreException { + IProgressMonitor pm) throws InterruptedException, CoreException { if (data.isEmpty() || storageLinkageID == ILinkage.NO_LINKAGE_ID) return; @@ -271,16 +272,8 @@ abstract public class PDOMWriter implements IPDOMASTProcessor { // Index update. storeSymbolsInIndex(data, storageLinkageID, ctx, pm); - // Tasks update. - if (taskUpdater != null) { - Set locations= new HashSet<>(); - for (FileInAST file : data.fSelectedFiles) { - locations.add(file.fileContentKey.getLocation()); - } - taskUpdater.updateTasks(data.fAST.getComments(), locations.toArray(new IIndexFileLocation[locations.size()])); - } if (!data.fStatuses.isEmpty()) { - List stati = data.fStatuses; + List statuses = data.fStatuses; String path= null; if (data.fSelectedFiles.length > 0) { path= data.fSelectedFiles[data.fSelectedFiles.length - 1].fileContentKey.getLocation().getURI().getPath(); @@ -288,8 +281,8 @@ abstract public class PDOMWriter implements IPDOMASTProcessor { path= data.fAST.getFilePath().toString(); } String msg= NLS.bind(Messages.PDOMWriter_errorWhileParsing, path); - if (stati.size() == 1) { - IStatus status= stati.get(0); + if (statuses.size() == 1) { + IStatus status= statuses.get(0); if (msg.equals(status.getMessage())) { throw new CoreException(status); } @@ -297,7 +290,7 @@ abstract public class PDOMWriter implements IPDOMASTProcessor { msg + ':' + status.getMessage(), status.getException())); } throw new CoreException(new MultiStatus(CCorePlugin.PLUGIN_ID, 0, - stati.toArray(new IStatus[stati.size()]), msg, null)); + statuses.toArray(new IStatus[statuses.size()]), msg, null)); } } @@ -583,6 +576,11 @@ abstract public class PDOMWriter implements IPDOMASTProcessor { boolean pragmaOnce= owner != null ? owner.hasPragmaOnceSemantics() : data.fAST.hasPragmaOnceSemantics(); file.setPragmaOnceSemantics(pragmaOnce); + String headerKey = IndexLocationFactory.getAbsolutePath(location).toOSString(); + String replacementHeader = data.fReplacementHeaders.get(headerKey); + if (replacementHeader != null) + file.setReplacementHeader(replacementHeader); + Symbols lists= data.fSymbolMap.get(owner); if (lists != null) { IASTPreprocessorStatement[] macros= lists.fMacros.toArray(new IASTPreprocessorStatement[lists.fMacros.size()]); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMFile.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMFile.java index e44b2170e72..3ce0081a1df 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMFile.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMFile.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2011 QNX Software Systems and others. + * Copyright (c) 2005, 2014 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -79,7 +79,8 @@ public class PDOMFile implements IIndexFragmentFile { private static final int LAST_USING_DIRECTIVE= SIZE_AND_ENCODING_HASH + 4; private static final int FIRST_MACRO_REFERENCE= LAST_USING_DIRECTIVE + Database.PTR_SIZE; private static final int SIGNIFICANT_MACROS= FIRST_MACRO_REFERENCE + Database.PTR_SIZE; - private static final int RECORD_SIZE= SIGNIFICANT_MACROS + Database.PTR_SIZE; // 8*PTR_SIZE + 3+1+8+8+8+4 = 64 + private static final int REPLACEMENT_HEADER = SIGNIFICANT_MACROS + Database.PTR_SIZE; + private static final int RECORD_SIZE= REPLACEMENT_HEADER + Database.PTR_SIZE; // 9*PTR_SIZE + 3+1+8+8+8+4 = 68 private static final int FLAG_PRAGMA_ONCE_SEMANTICS = 0x01; @@ -213,7 +214,11 @@ public class PDOMFile implements IIndexFragmentFile { // Transfer the flags. Database db= fLinkage.getDB(); db.putByte(record + FLAGS, db.getByte(sourceFile.record + FLAGS)); - + + // Transfer the replacement header. + db.putRecPtr(record + REPLACEMENT_HEADER, db.getRecPtr(sourceFile.record + REPLACEMENT_HEADER)); + db.putRecPtr(sourceFile.record + REPLACEMENT_HEADER, 0); + // Delete the source file sourceFile.delete(); } @@ -600,6 +605,9 @@ public class PDOMFile implements IIndexFragmentFile { if (locRecord != 0) db.getString(locRecord).delete(); locRecord = db.getRecPtr(record + SIGNIFICANT_MACROS); + if (locRecord != 0) + db.getString(locRecord).delete(); + locRecord = db.getRecPtr(record + REPLACEMENT_HEADER); if (locRecord != 0) db.getString(locRecord).delete(); @@ -950,6 +958,23 @@ public class PDOMFile implements IIndexFragmentFile { return fLinkage.getUsingDirectives(this); } + @Override + public String getReplacementHeader() throws CoreException { + Database db = fLinkage.getDB(); + long rec = db.getRecPtr(record + REPLACEMENT_HEADER); + return rec == 0 ? null : db.getString(rec).getString(); + } + + @Override + public void setReplacementHeader(String replacementHeader) throws CoreException { + Database db = fLinkage.getDB(); + long oldRecord = db.getRecPtr(record + REPLACEMENT_HEADER); + if (oldRecord != 0) + db.getString(oldRecord).delete(); + long newRecord = replacementHeader == null ? 0 : db.newString(replacementHeader).getRecord(); + db.putRecPtr(record + REPLACEMENT_HEADER, newRecord); + } + // Required because we cannot reference CCorePlugin in order for StandaloneIndexer to work private static IStatus createStatus(String msg) { return new Status(IStatus.ERROR, "org.eclipse.cdt.core", msg, null); //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java index b110d784313..aa5c4ccb79c 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2013 Wind River Systems, Inc. and others. + * Copyright (c) 2006, 2014 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -18,6 +18,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CCorePreferenceConstants; @@ -103,6 +105,17 @@ public abstract class PDOMIndexerTask extends AbstractIndexerTask implements IPD } setUpdateFlags(IIndexManager.UPDATE_CHECK_TIMESTAMPS | IIndexManager.UPDATE_CHECK_CONTENTS_HASH); setForceFirstFiles(forceFiles.length); + + ICProject project = getCProject(); + String privatePattern = CCorePreferenceConstants.getPreference( + CCorePreferenceConstants.INCLUDE_PRIVATE_PATTERN, project, null); + if (privatePattern != null) { + try { + setPragmaPrivatePattern(Pattern.compile(privatePattern)); + } catch (PatternSyntaxException e) { + CCorePlugin.log(e); + } + } } private static ITranslationUnit[] concat(ITranslationUnit[] added, ITranslationUnit[] changed) { @@ -128,7 +141,7 @@ public abstract class PDOMIndexerTask extends AbstractIndexerTask implements IPD if (cmp != 0) return cmp; } - int cmp = s1.length-s2.length; + int cmp = s1.length - s2.length; if (cmp != 0) return cmp; return s1[max].compareTo(s2[max]); diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java index 02f8b5f78f6..09254a4bc8c 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 QNX Software Systems and others. + * Copyright (c) 2000, 2014 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -181,7 +181,7 @@ public class CCorePreferenceConstants { /** * Preference key for the regular expression pattern that, when appears in a comment on the same - * line as include statement, indicates the the included header file is exported. + * line as include statement, indicates that the included header file is exported. * @see "https://code.google.com/p/include-what-you-use/wiki/IWYUPragmas" * * @since 5.5 @@ -218,6 +218,22 @@ public class CCorePreferenceConstants { */ public static final String INCLUDE_END_EXPORTS_PATTERN = "includes.endExportsPattern"; //$NON-NLS-1$ + /** + * Default value for {@link #INCLUDE_PRIVATE_PATTERN}. + * @since 5.7 + */ + public static final String DEFAULT_INCLUDE_PRIVATE_PATTERN = "IWYU\\s+(pragma:?\\s+)?private(,\\s+include\\s+(?
\\S+))?"; //$NON-NLS-1$ + + /** + * Preference key for the regular expression pattern that, when appears in a comment on the same + * line as include statement, indicates that the included header file is private and that + * another header file should be included instead. + * @see "https://code.google.com/p/include-what-you-use/wiki/IWYUPragmas" + * + * @since 5.7 + */ + public static final String INCLUDE_PRIVATE_PATTERN = "includes.privatePattern"; //$NON-NLS-1$ + /** * A named preference that controls whether the parser should skip trivial expressions in initializer lists. *

diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java index 4a5ac51711d..4450214ee65 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 QNX Software Systems and others. + * Copyright (c) 2000, 2014 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -67,6 +67,7 @@ public class CCorePreferenceInitializer extends AbstractPreferenceInitializer { defaultPreferences.put(CCorePreferenceConstants.INCLUDE_EXPORT_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_EXPORT_PATTERN); defaultPreferences.put(CCorePreferenceConstants.INCLUDE_BEGIN_EXPORTS_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_BEGIN_EXPORTS_PATTERN); defaultPreferences.put(CCorePreferenceConstants.INCLUDE_END_EXPORTS_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_END_EXPORTS_PATTERN); + defaultPreferences.put(CCorePreferenceConstants.INCLUDE_PRIVATE_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_PRIVATE_PATTERN); // Scalability defaults. defaultPreferences.putBoolean(CCorePreferenceConstants.SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS, CCorePreferenceConstants.DEFAULT_SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS); diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java index 98a517cddff..239d0217f38 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 Google, Inc and others. + * Copyright (c) 2013, 2014 Google, Inc and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -352,6 +352,57 @@ public class IncludeOrganizerTest extends IncludesTestBase { assertExpectedResults(); } + //dir1/private1.h + ///** @file dir1/private1.h + // * This is an internal header file, included by other library headers. + // * Do not attempt to use it directly. @headername{dir1/public1.h} + // */ + //class A {}; + + //dir1/public1.h + //#include "private1.h" + + //dir1/private2.h + //// IWYU pragma: private, + //// include "dir1/public2.h" + //class B {}; + + //dir1/public2.h + //#include "private2.h" + + //dir1/private3.h + //// IWYU pragma: private + //class C {}; + + //dir1/public3.h + //#include "private3.h" + + //dir2/private4.h + //// IWYU pragma: private, include "dir2/public4.h" + //class D {}; + + //dir2/public4.h + //#include "private4.h" + + //dir2/source.cpp + //A a; + //B b; + //C c; + //D d; + //==================== + //#include "dir1/public1.h" + //#include "dir1/public2.h" + //#include "dir1/public3.h" + //#include "dir2/private4.h" + // + //A a; + //B b; + //C c; + //D d; + public void testPrivateHeaders() throws Exception { + assertExpectedResults(); + } + //h1.h //class A {}; diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java index 05a0131d8b3..f6442e8e30e 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java @@ -17,9 +17,11 @@ import java.util.LinkedHashSet; import java.util.Set; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.QualifiedName; import org.eclipse.jface.preference.IPreferenceStore; import org.osgi.framework.Bundle; @@ -31,6 +33,7 @@ import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.core.testplugin.TestScannerProvider; import org.eclipse.cdt.core.testplugin.util.BaseTestCase; import org.eclipse.cdt.core.testplugin.util.TestSourceReader; import org.eclipse.cdt.ui.CUIPlugin; @@ -44,6 +47,8 @@ import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; */ public abstract class IncludesTestBase extends BaseTestCase { protected final String LINE_DELIMITER = "\n"; + // Same as in CCorePlugin#SCANNER_INFO_PROVIDER2_NAME. + private static final String SCANNER_INFO_PROVIDER2_NAME = "ScannerInfoProvider2"; //$NON-NLS-1$ protected static class FirstHeaderChooser implements IHeaderChooser { @Override @@ -78,9 +83,15 @@ public abstract class IncludesTestBase extends BaseTestCase { public void setUp() throws Exception { super.setUp(); resetPreferences(); + cproject = cpp ? CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) : CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER); + IProject project = cproject.getProject(); + TestScannerProvider.sLocalIncludes = new String[] { project.getLocation().toOSString() }; + QualifiedName scannerInfoProviderName = new QualifiedName(CCorePlugin.PLUGIN_ID, SCANNER_INFO_PROVIDER2_NAME); + project.setSessionProperty(scannerInfoProviderName, new TestScannerProvider()); + Bundle bundle = CTestPlugin.getDefault().getBundle(); CharSequence[] testData = TestSourceReader.getContentsForTest(bundle, "ui", getClass(), getName(), 0); @@ -105,7 +116,7 @@ public abstract class IncludesTestBase extends BaseTestCase { } reader.close(); - sourceFile = TestSourceReader.createFile(cproject.getProject(), new Path(testFile.getName()), + sourceFile = TestSourceReader.createFile(project, new Path(testFile.getName()), testFile.getSource()); testFiles.add(testFile); selectedFile = testFile; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludePragmasBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludePragmasBlock.java index 418469b5029..5c5c9493275 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludePragmasBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludePragmasBlock.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 Google, Inc and others. + * Copyright (c) 2013, 2014 Google, Inc and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -44,11 +44,13 @@ public class IncludePragmasBlock extends OptionsConfigurationBlock { private static final Key KEY_EXPORT_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_EXPORT_PATTERN); private static final Key KEY_BEGIN_EXPORTS_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_BEGIN_EXPORTS_PATTERN); private static final Key KEY_END_EXPORTS_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_END_EXPORTS_PATTERN); + private static final Key KEY_PRIVATE_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_PRIVATE_PATTERN); private static Key[] ALL_KEYS = { KEY_EXPORT_PATTERN, KEY_BEGIN_EXPORTS_PATTERN, KEY_END_EXPORTS_PATTERN, + KEY_PRIVATE_PATTERN, }; private PixelConverter pixelConverter; @@ -81,6 +83,9 @@ public class IncludePragmasBlock extends OptionsConfigurationBlock { control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_end_exports_pattern, KEY_END_EXPORTS_PATTERN, 0, pixelConverter.convertWidthInCharsToPixels(40)); LayoutUtil.setHorizontalGrabbing(control, true); + control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_end_exports_pattern, + KEY_PRIVATE_PATTERN, 0, pixelConverter.convertWidthInCharsToPixels(40)); + LayoutUtil.setHorizontalGrabbing(control, true); updateControls(); return composite; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java index 6d0de34088e..8ad83c8d930 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 IBM Corporation and others. + * Copyright (c) 2000, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -459,6 +459,7 @@ public final class PreferencesMessages extends NLS { public static String IncludePragmasBlock_export_pattern; public static String IncludePragmasBlock_begin_exports_pattern; public static String IncludePragmasBlock_end_exports_pattern; + public static String IncludePragmasBlock_private_pattern; public static String NameStylePreferencePage_title; public static String NameStyleBlock_code_node; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties index b5bafa444b4..bd46639f9c5 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2013 IBM Corporation and others. +# Copyright (c) 2000, 2014 IBM Corporation and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at @@ -514,9 +514,10 @@ IncludeOrderBlock_order_of_includes= O&rder of Include Statements: IncludePragmasPreferencePage_title= Include Pragmas IncludePragmasBlock_description=Include pragmas are special comments that affect behavior of Organize Includes command. A description of include pragmas can be found in IWYU Pragmas. Include patterns can be customized by defining regular expressions matching each of the pragmas. IncludePragmasBlock_link_tooltip=Wiki page describing include-what-you-use pragmas -IncludePragmasBlock_export_pattern= Export: -IncludePragmasBlock_begin_exports_pattern= Begin Exports: -IncludePragmasBlock_end_exports_pattern= End Exports: +IncludePragmasBlock_export_pattern= &Export: +IncludePragmasBlock_begin_exports_pattern= &Begin Exports: +IncludePragmasBlock_end_exports_pattern= En&d Exports: +IncludePragmasBlock_private_pattern= &Private: NameStylePreferencePage_title=Name Style NameStyleBlock_code_node=Code diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java index 7500f189e8f..cb7f0fab22c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2013 Google, Inc and others. + * Copyright (c) 2012, 2014 Google, Inc and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -25,6 +25,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.IPreferencesService; import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndexFile; import org.eclipse.cdt.core.index.IIndexInclude; import org.eclipse.cdt.core.index.IndexLocationFactory; @@ -54,9 +55,10 @@ public class HeaderSubstitutor { fIncludeMaps[1].addAllMappings(map.getOptionalSubstitutionMap()); } } - fIncludeMaps[0].transitivelyClose(); - fIncludeMaps[1].transitivelyClose(); } + addHeaderDerivedMappings(); + fIncludeMaps[0].transitivelyClose(); + fIncludeMaps[1].transitivelyClose(); fSymbolExportMap = new SymbolExportMap(); str = preferences.getString(CUIPlugin.PLUGIN_ID, @@ -69,6 +71,53 @@ public class HeaderSubstitutor { } } + private void addHeaderDerivedMappings() { + try { + for (IIndexFile file : fContext.getIndex().getAllFiles()) { + String replacement = file.getReplacementHeader(); + if (replacement != null) { + IPath path = IndexLocationFactory.getAbsolutePath(file.getLocation()); + if (fContext.getCurrentDirectory().isPrefixOf(path)) { + // "IWYU pragma: private" does not affect inclusion from files under + // the directory where the header is located. + continue; + } + + IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(path); + if (includeInfo == null) + continue; + + if (replacement.isEmpty()) { + IIndexInclude[] includedBy = fContext.getIndex().findIncludedBy(file, IIndex.DEPTH_ZERO); + for (IIndexInclude include : includedBy) { + IPath includer = IndexLocationFactory.getAbsolutePath(include.getIncludedByLocation()); + IncludeInfo replacementInfo = fContext.getIncludeForHeaderFile(includer); + if (replacementInfo != null) { + fIncludeMaps[0].addMapping(includeInfo, replacementInfo); + } + } + } else { + String[] headers = replacement.split(","); //$NON-NLS-1$ + for (String header : headers) { + if (!header.isEmpty()) { + char firstChar = header.charAt(0); + IncludeInfo replacementInfo; + if (firstChar == '"' || firstChar == '<') { + replacementInfo = new IncludeInfo(header); + } else { + replacementInfo = new IncludeInfo(header, includeInfo.isSystem()); + } + fIncludeMaps[0].addMapping(includeInfo, replacementInfo); + } + } + } + } + } + } catch (CoreException e) { + CUIPlugin.log(e); + } + } + /** * Selects the header file to be used in an {@code #include} statement given the header file * that needs to be included. Returns absolute path of the header if it can be uniquely