1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

Correctly resolving includes like "path/../file.h", bug 246129

This commit is contained in:
Markus Schorn 2008-11-27 09:23:35 +00:00
parent 0c73673ec2
commit 7b7875106f
14 changed files with 443 additions and 118 deletions

View file

@ -66,5 +66,4 @@ public class FileCodeReaderFactory implements ICodeReaderFactory {
public ICodeReaderCache getCodeReaderCache() {
return cache;
}
}

View file

@ -0,0 +1,256 @@
package org.eclipse.cdt.internal.index.tests;
import java.io.File;
import java.io.FileWriter;
import junit.framework.TestSuite;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.index.IIndexInclude;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IPathEntry;
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.internal.core.pdom.indexer.IndexerPreferences;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
public class Bug246129 extends IndexTestBase {
public static TestSuite suite() {
TestSuite suite = suite(Bug246129.class, "_");
// suite.addTest(new Bug246129("include ext/../type.h"));
return suite;
}
private ICProject fProject;
private IFile fSource;
private IFolder fWrapperIncludeFolder;
private IFolder fIncludeFolder;
private File fTmpDir;
private File fExternalWrapperIncludeFolder;
private File fExternalWrapperHeader;
private File fExternalIncludeFolder;
private File fExternalHeader;
private File fExternalExtFolder;
IIndex fIndex;
boolean fFalseFriendsAccepted;
public Bug246129(String name) {
super(name);
}
protected void setUp() throws Exception {
super.setUp();
if (fProject == null) {
// Populate workspace
fProject = createProject(true, "resources/indexTests/bug246129");
fSource = fProject.getProject().getFile("source.cpp");
fWrapperIncludeFolder = fProject.getProject().getFolder(
"wrapper_include");
fIncludeFolder = fProject.getProject().getFolder("include");
// Create header files external to the workspace.
fTmpDir = CProjectHelper.freshDir();
fExternalWrapperIncludeFolder = new File(fTmpDir,
"wrapper_include");
fExternalWrapperIncludeFolder.mkdir();
fExternalWrapperHeader = new File(
fExternalWrapperIncludeFolder, "external_type.h");
fExternalWrapperHeader.createNewFile();
FileWriter writer = new FileWriter(fExternalWrapperHeader);
writer.write("#ifndef EXTERNAL_WRAPPER_TYPE_H_\n");
writer.write("#define EXTERNAL_WRAPPER_TYPE_H_\n");
writer.write("#include <ext/../external_type.h>\n");
writer.write("class ExternalWrapper {\n");
writer.write("};\n");
writer.write("#endif\n");
writer.close();
fExternalIncludeFolder = new File(fTmpDir, "include");
fExternalIncludeFolder.mkdir();
fExternalExtFolder = new File(fExternalIncludeFolder, "ext");
fExternalExtFolder.mkdir();
fExternalHeader = new File(fExternalIncludeFolder,
"external_type.h");
fExternalHeader.createNewFile();
writer = new FileWriter(fExternalHeader);
writer.write("#ifndef EXTERNAL_TYPE_H_\n");
writer.write("#define EXTERNAL_TYPE_H_\n");
writer.write("class ExternalType {\n");
writer.write("};\n");
writer.write("#endif\n");
writer.close();
// The indexer needs non-empty build info in order to index
// source files if index-all-files is turned off.
IPathEntry[] entries = new IPathEntry[] { CoreModel
.newIncludeEntry(fProject.getPath(), null,
fWrapperIncludeFolder.getLocation()),
CoreModel.newIncludeEntry(fProject.getPath(), null,
fIncludeFolder.getLocation()) };
fProject.setRawPathEntries(entries, NPM);
// However, the scanner info provider used by the unit tests
// needs separate setup, and this one must be complete.
TestScannerProvider.sIncludes = new String[] {
fWrapperIncludeFolder.getLocation().toOSString(),
fIncludeFolder.getLocation().toOSString(),
fExternalWrapperIncludeFolder.getAbsolutePath(),
fExternalIncludeFolder.getAbsolutePath() };
IndexerPreferences.set(fProject.getProject(),
IndexerPreferences.KEY_INDEX_ALL_FILES, "false");
File falseFriendDirectory = new File(fWrapperIncludeFolder
.getLocation().toOSString()
+ "/ext/..");
fFalseFriendsAccepted = falseFriendDirectory.exists();
CCorePlugin.getIndexManager().reindex(fProject);
assertTrue(CCorePlugin.getIndexManager().joinIndexer(10000, NPM));
fIndex = CCorePlugin.getIndexManager().getIndex(fProject);
}
}
protected void tearDown() throws Exception {
fExternalWrapperHeader.delete();
fExternalWrapperIncludeFolder.delete();
fExternalHeader.delete();
fExternalExtFolder.delete();
fExternalIncludeFolder.delete();
fTmpDir.delete();
super.tearDown();
}
private void assertSymbolInIndex(String symbolName) throws Exception {
IIndexBinding[] bindings = fIndex.findBindings(
symbolName
.toCharArray(), false, IndexFilter.ALL, NPM);
assertTrue(bindings.length > 0);
}
public void testIndex() throws Exception {
try {
fIndex.acquireReadLock();
IIndexFile[] indexFiles = fIndex.getAllFiles();
// Check that all header files have been found, provided the
// File implementation does support it.
if (fFalseFriendsAccepted) {
assertEquals(3, indexFiles.length);
} else {
assertEquals(5, indexFiles.length);
}
// The wrapper classes are found regardless whether false friends
// are
// accepted or not.
assertSymbolInIndex("Wrapper");
assertSymbolInIndex("ExternalWrapper");
// The Type class is only known on platforms with a File
// implementation sorting out the false friends.
if (!fFalseFriendsAccepted) {
assertSymbolInIndex("Type");
assertSymbolInIndex("ExternalType");
}
// Check that all paths are normalized.
for (IIndexFile indexFile : indexFiles) {
IIndexInclude[] includes = indexFile.getIncludes();
for (IIndexInclude i : includes) {
IIndexFileLocation location = i.getIncludesLocation();
assertNotNull(location);
assertFalse(location.getURI().toASCIIString()
.contains(".."));
String fullPath = location.getFullPath();
if (fullPath != null) {
assertFalse(fullPath.contains(".."));
}
}
}
} finally {
fIndex.releaseReadLock();
}
}
private void assertSymbolInAst(IScope scope, String symbolName)
throws Exception {
IBinding[] bindings = scope.find(symbolName);
assertTrue(bindings.length > 0);
}
public void testAst() throws Exception {
ITranslationUnit tu = CoreModel.getDefault().createTranslationUnitFrom(
fProject, fSource.getLocation());
IASTTranslationUnit ast = tu.getAST();
// The wrapper classes are found regardless whether false friends
// are
// accepted or not.
IScope topLevel = ast.getScope();
assertSymbolInAst(topLevel, "Wrapper");
assertSymbolInAst(topLevel, "ExternalWrapper");
// The Type class is only known on platforms with a File
// implementation sorting out the false friends.
if (!fFalseFriendsAccepted) {
assertSymbolInAst(topLevel, "Type");
assertSymbolInAst(topLevel, "ExternalType");
}
// Check that all paths are normalized.
IASTPreprocessorIncludeStatement[] includes = ast
.getIncludeDirectives();
for (IASTPreprocessorIncludeStatement i : includes) {
String includedPath = i.getPath();
assertNotNull(includedPath);
assertFalse(includedPath.contains(".."));
}
}
}

View file

@ -9,7 +9,6 @@
* Markus Schorn - initial API and implementation
* Andrew Ferguson (Symbian)
*******************************************************************************/
package org.eclipse.cdt.internal.index.tests;
import java.io.ByteArrayInputStream;
@ -81,7 +80,9 @@ public class IndexBugsTests extends BaseTestCase {
}
public static TestSuite suite() {
return suite(IndexBugsTests.class);
final TestSuite ts = suite(IndexBugsTests.class);
ts.addTest(Bug246129.suite());
return ts;
}
@Override

View file

@ -0,0 +1,3 @@
#ifndef DUMMY_H_
#define DUMMY_H_
#endif

View file

@ -0,0 +1,5 @@
#ifndef TYPE_H_
#define TYPE_H_
class Type {
};
#endif

View file

@ -0,0 +1,9 @@
#include <type.h>
#include <external_type.h>
void check()
{
Type t;
Wrapper w;
ExternalType et;
ExternalWrapper ew;
}

View file

@ -0,0 +1,6 @@
#ifndef WRAPPER_TYPE_H_
#define WRAPPER_TYPE_H_
#include <ext/../type.h>
class Wrapper {
};
#endif

View file

@ -69,6 +69,7 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.internal.core.dom.NullCodeReaderFactory;
import org.eclipse.cdt.internal.core.dom.SavedCodeReaderFactory;
import org.eclipse.cdt.internal.core.index.IndexBasedCodeReaderFactory;
import org.eclipse.cdt.internal.core.parser.InternalParserUtil;
import org.eclipse.cdt.internal.core.parser.ParserLogService;
import org.eclipse.cdt.internal.core.pdom.indexer.ProjectIndexerIncludeResolutionHeuristics;
import org.eclipse.cdt.internal.core.pdom.indexer.ProjectIndexerInputAdapter;
@ -930,20 +931,25 @@ public class TranslationUnit extends Openable implements ITranslationUnit {
}
public CodeReader getCodeReader() {
CodeReader reader;
IPath location= getLocation();
if (isWorkingCopy() || location == null) {
if (location == null) {
reader= new CodeReader(getContents());
if (location == null)
return new CodeReader(getContents());
if (isWorkingCopy()) {
return new CodeReader(location.toOSString(), getContents());
}
else {
reader= new CodeReader(location.toString(), getContents());
IResource res= getResource();
try {
if (res instanceof IFile)
return InternalParserUtil.createWorkspaceFileReader(location.toOSString(), (IFile) res);
else
return InternalParserUtil.createExternalFileReader(location.toOSString());
} catch (CoreException e) {
CCorePlugin.log(e);
} catch (IOException e) {
CCorePlugin.log(e);
}
}
else {
reader= ParserUtil.createReader(location.toString(), null);
}
return reader;
return null;
}
public IScannerInfo getScannerInfo(boolean force) {

View file

@ -10,12 +10,10 @@
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser;
import java.util.Iterator;
import java.io.IOException;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.core.parser.CodeReader;
import org.eclipse.cdt.core.parser.ICodeReaderCache;
import org.eclipse.cdt.internal.core.parser.InternalParserUtil;
/**
* This is an empty implementation of the ICodeReaderCache interface. It is used to implement a
@ -32,18 +30,11 @@ public class EmptyCodeReaderCache implements ICodeReaderCache {
* Creates a new CodeReader for the given file location.
*/
public CodeReader get(String location) {
CodeReader ret = null;
ret = InternalParserUtil.createFileReader(location);
return ret;
try {
return new CodeReader(location);
} catch (IOException e) {
}
/**
* This provides support for PartialWorkingCopyCodeReaderFactory.
* @param finalPath
* @param workingCopies
*/
public CodeReader createReader(String finalPath, Iterator<IWorkingCopy> workingCopies ) {
return InternalParserUtil.createFileReader(finalPath);
return null;
}
/**

View file

@ -14,6 +14,7 @@
*******************************************************************************/
package org.eclipse.cdt.internal.core.index;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -36,6 +37,7 @@ import org.eclipse.cdt.core.parser.ICodeReaderCache;
import org.eclipse.cdt.core.parser.ParserUtil;
import org.eclipse.cdt.internal.core.dom.AbstractCodeReaderFactory;
import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics;
import org.eclipse.cdt.internal.core.parser.InternalParserUtil;
import org.eclipse.cdt.internal.core.parser.scanner.IIndexBasedCodeReaderFactory;
import org.eclipse.cdt.internal.core.parser.scanner.IncludeFileContent;
import org.eclipse.cdt.internal.core.parser.scanner.IncludeFileContent.InclusionKind;
@ -162,10 +164,16 @@ public final class IndexBasedCodeReaderFactory extends AbstractCodeReaderFactory
CCorePlugin.log(e);
}
CodeReader codeReader= createCodeReaderForInclusion(path);
try {
CodeReader codeReader= InternalParserUtil.createCodeReader(ifl);
if (codeReader != null) {
return new IncludeFileContent(codeReader);
}
} catch (CoreException e) {
CCorePlugin.log(e);
} catch (IOException e) {
CCorePlugin.log(e);
}
return null;
}

View file

@ -6,32 +6,80 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* John Camelon (IBM Corporation) - initial API and implementation
* Markus Schorn (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.parser.CodeReader;
import org.eclipse.cdt.core.parser.ParserFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
/**
* @author jcamelon
* Utility for creating code readers
*/
public class InternalParserUtil extends ParserFactory {
public static CodeReader createFileReader(String finalPath) {
File includeFile = new File(finalPath);
/**
* Normalizes the path by using the location of the file, if possible.
*/
public static String normalizePath(String path, IFile file) {
IPath loc= file.getLocation();
if (loc != null) {
path= loc.toOSString();
}
return path;
}
/**
* Creates a code reader for an external location, normalizing path to
* canonical path.
*/
public static CodeReader createExternalFileReader(String externalLocation) throws IOException {
File includeFile = new File(externalLocation);
if (includeFile.isFile()) {
try {
//use the canonical path so that in case of non-case-sensitive OSs
//the CodeReader always has the same name as the file on disk with
//no differences in case.
return new CodeReader(includeFile.getCanonicalPath());
} catch (IOException e) {
}
}
return null;
}
/**
* Creates a code reader for an external location, normalizing path to
* canonical path.
*/
public static CodeReader createWorkspaceFileReader(String path, IFile file) throws CoreException, IOException{
path = normalizePath(path, file);
InputStream in= file.getContents();
try {
return new CodeReader(path, file.getCharset(), in);
} finally {
try {
in.close();
} catch (IOException e) {
}
}
}
public static CodeReader createCodeReader(IIndexFileLocation ifl) throws CoreException, IOException {
String fullPath= ifl.getFullPath();
if (fullPath != null) {
IResource res= ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(fullPath));
if (res instanceof IFile)
return createWorkspaceFileReader(ifl.getURI().getPath(), (IFile) res);
}
return createExternalFileReader(ifl.getURI().getPath());
}
}

View file

@ -30,7 +30,19 @@ public class ScannerUtility {
* - replace multiple separators by single one
* - skip "/./"
* - skip quotes
* - process "/../" (skip previous directory level)
*
* Note: "/../" is left untouched on purpose in order to work properly under
* circumstances such as this:
*
* header file at include_1/vector:
* // Is supposed to find the STL vector header:
* #include <ext/../vector>
*
* GCC include tree
* include_gcc/ext/...
* /vector
*
* (ls include_1/ext/../vector does not work either).
*
* @param originalPath - path to process
* @return - reconciled path
@ -87,30 +99,13 @@ public class ScannerUtility {
aus[j++] = DOT;
aus[j++] = c;
}
// we found "/.." sequence. Look ahead.
else {
// we found "/../" (or "/.." is at the end of string)
// we should delete previous segment of output path
if (i == len1 || ein[i+2] == SLASH || ein[i+2] == BSLASH) {
i+=2;
noSepBefore = false;
if (j > 1) { // there is at least 1 segment before
int k = j - 2;
while ( k >= 0 ) {
if (aus[k] == File.separatorChar) break;
k--;
}
j = k + 1; // set index to previous segment or to 0
}
}
// Case "/..blabla" processed as usual
// Processed as usual
else {
i++;
noSepBefore = true;
aus[j++] = DOT;
aus[j++] = DOT;
}
}
} else
{} // do nothing when "." is last symbol
}

View file

@ -13,11 +13,10 @@
package org.eclipse.cdt.core.parser;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.io.IOException;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.internal.core.parser.InternalParserUtil;
import org.eclipse.cdt.internal.core.util.ILRUCacheable;
import org.eclipse.cdt.internal.core.util.OverflowingLRUCache;
import org.eclipse.core.resources.IFile;
@ -27,6 +26,7 @@ import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
@ -140,25 +140,44 @@ public class CodeReaderCache implements ICodeReaderCache {
* @param key the path of the CodeReader to retrieve
*/
public synchronized CodeReader get(String key) {
CodeReader ret = null;
CodeReader result = null;
if (cache.getSpaceLimit() > 0)
ret = cache.get(key);
result= cache.get(key);
if (result != null)
return result;
// not in the cache
if (ret == null) {
// for efficiency: check File.exists before ParserUtil#createReader()
// bug 100947 fix: don't want to attempt to create a code reader if there is no file for the key
if (!(new File(key).exists()))
final File jfile = new File(key);
if (!(jfile.exists()))
return null;
final List<IWorkingCopy> emptyList= Collections.emptyList();
ret = ParserUtil.createReader(key, emptyList.iterator());
try {
IResource file = ParserUtil.getResourceForFilename(key);
if (file instanceof IFile) {
key= InternalParserUtil.normalizePath(key, (IFile) file);
result= cache.get(key);
if (result != null)
return result;
if (cache.getSpaceLimit() > 0)
put(ret);
result= InternalParserUtil.createWorkspaceFileReader(key, (IFile) file);
}
key= jfile.getCanonicalPath();
result= cache.get(key);
if (result != null)
return result;
return ret;
result= InternalParserUtil.createExternalFileReader(key);
if (cache.getSpaceLimit() > 0)
put(result);
return result;
} catch (CoreException ce) {
} catch (IOException e) {
} catch (IllegalStateException e) {
}
return null;
}
/**

View file

@ -12,7 +12,6 @@
package org.eclipse.cdt.core.parser;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import org.eclipse.cdt.core.model.IWorkingCopy;
@ -62,48 +61,28 @@ public class ParserUtil
return null;
}
public static CodeReader createReader( String finalPath, Iterator<IWorkingCopy> workingCopies )
{
public static CodeReader createReader(String path, Iterator<IWorkingCopy> workingCopies) {
// check to see if the file which this path points to points to an
// IResource in the workspace
try
{
IResource resultingResource = getResourceForFilename(finalPath);
if( resultingResource != null && resultingResource.getType() == IResource.FILE )
{
// this is the file for sure
// check the working copy
if( workingCopies != null && workingCopies.hasNext() )
{
char[] buffer = findWorkingCopy( resultingResource, workingCopies );
try {
IResource file = getResourceForFilename(path);
if (file instanceof IFile) {
// check for a working copy
if (workingCopies != null && workingCopies.hasNext()) {
char[] buffer = findWorkingCopy(file, workingCopies);
if (buffer != null)
return new CodeReader(finalPath, buffer);
return new CodeReader(InternalParserUtil.normalizePath(path, (IFile) file), buffer);
}
InputStream in = null;
try
{
in = ((IFile)resultingResource).getContents();
return new CodeReader(finalPath, ((IFile)resultingResource).getCharset(), in);
} finally {
if (in != null)
{
in.close();
return InternalParserUtil.createWorkspaceFileReader(path, (IFile) file);
}
return InternalParserUtil.createExternalFileReader(path);
} catch (CoreException ce) {
} catch (IOException e) {
} catch (IllegalStateException e) {
}
return null;
}
}
catch( CoreException ce )
{
}
catch( IOException e )
{
}
catch( IllegalStateException e )
{
}
return InternalParserUtil.createFileReader(finalPath);
}
public static IResource getResourceForFilename(String finalPath) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();