1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-22 14:12:10 +02:00

Bug 414692 - Organize Includes has to honor @headername{<header>} in

doxygen comments
This commit is contained in:
Sergey Prigogin 2014-05-02 10:31:19 -07:00
parent 7e686b6744
commit 9d7a23cde5
17 changed files with 396 additions and 105 deletions

View file

@ -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<String, String> fDefinedSymbols;
public TestScannerInfo(String[] includes, String[] macroFiles, String[] includeFiles,
Map<String, String> definedSymbols) {
public TestScannerInfo(String[] includes, String[] localIncludes, String[] macroFiles,
String[] includeFiles, Map<String, String> 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

View file

@ -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<String, String> 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

View file

@ -107,6 +107,23 @@ public interface IIndexFile extends IFileNomination {
*/
int getLinkageID() throws CoreException;
/**
* Returns the name of the replacement header obtained from <code>@headername{header}</code> 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 <code>@headername{header}</code> 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

View file

@ -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<IIndexInclude> result= new ArrayList<>();
findIncludedBy(file.getLinkageID(), Collections.singletonList(file), result, depth,
new HashSet<FileContentKey>());
return result.toArray(new IIndexInclude[result.size()]);
}
public void findIncludedBy(int linkageID, List<IIndexFile> in, List<IIndexInclude> out, int depth,
HashSet<FileContentKey> handled) throws CoreException {
List<IIndexFile> nextLevel= depth != 0 ? new LinkedList<IIndexFile>() : 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<IIndexFile> in = Collections.singletonList(file);
Set<FileContentKey> handled = new HashSet<>();
while (true) {
Collection<IIndexFile> nextLevel= depth != 0 ? new ArrayDeque<IIndexFile>() : 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<IIndexInclude> result= new ArrayList<>();
findIncludes(Collections.singletonList(file), result, depth, new HashSet<>());
return result.toArray(new IIndexInclude[result.size()]);
}
private void findIncludes(List<IIndexFile> in, List<IIndexInclude> out, int depth,
HashSet<Object> handled) throws CoreException {
List<IIndexFile> nextLevel= depth != 0 ? new LinkedList<IIndexFile>() : 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<IIndexFile> in = Collections.singletonList(file);
Set<Object> handled = new HashSet<>();
while (true) {
Collection<IIndexFile> nextLevel= depth != 0 ? new ArrayDeque<IIndexFile>() : 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();

View file

@ -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\\{(?<header>[^\\}]+)\\}"); //$NON-NLS-1$
private static class LinkageTask {
final int fLinkageID;
private final Map<IIndexFileLocation, LocationTask> fLocationTasks;
@ -298,6 +304,7 @@ public abstract class AbstractIndexerTask extends PDOMWriter {
private boolean fIndexFilesWithoutConfiguration= true;
private boolean fIndexAllHeaderVersions = false;
private Set<String> fHeadersToIndexAllVersions = Collections.emptySet();
private Pattern fPragmaPrivatePattern;
private List<LinkageTask> fRequestsPerLinkage= new ArrayList<>();
private Map<IIndexFile, IndexFileContent> fIndexContentCache= new LRUCache<>(500);
private Map<IIndexFileLocation, IIndexFragmentFile[]> 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<IIndexFileLocation> 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 <code>@headername{header}</code> and
* {@code IWYU pragma: private}.
*
* @return replacement headers keyed by file paths
*/
private Map<String, String> extractReplacementHeaders(IASTComment[] comments, IProgressMonitor pm) {
Map<String, String> 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);

View file

@ -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;

View file

@ -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<IASTPreprocessorIncludeStatement, Symbols> fSymbolMap = new HashMap<>();
final Set<IASTPreprocessorIncludeStatement> fContextIncludes = new HashSet<>();
final List<IStatus> fStatuses= new ArrayList<>();
final List<IStatus> fStatuses = new ArrayList<>();
Map<String, String> 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<IIndexFileLocation> 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<IStatus> stati = data.fStatuses;
List<IStatus> 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()]);

View file

@ -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;
@ -214,6 +215,10 @@ public class PDOMFile implements IIndexFragmentFile {
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$

View file

@ -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]);

View file

@ -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+(?<header>\\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.
* <p>

View file

@ -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);

View file

@ -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 {};

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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 <a href="https://code.google.com/p/include-what-you-use/wiki/IWYUPragmas">IWYU Pragmas</a>. 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

View file

@ -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