| /******************************************************************************* |
| * Copyright (c) 2007, 2013 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 |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Markus Schorn - Initial API and implementation |
| * Sergey Prigogin (Google) |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.core.parser.scanner; |
| |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| |
| import org.eclipse.cdt.core.dom.IName; |
| import org.eclipse.cdt.core.dom.ast.IASTComment; |
| import org.eclipse.cdt.core.dom.ast.IASTFileLocation; |
| import org.eclipse.cdt.core.dom.ast.IASTImageLocation; |
| import org.eclipse.cdt.core.dom.ast.IASTName; |
| import org.eclipse.cdt.core.dom.ast.IASTNode; |
| import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; |
| import org.eclipse.cdt.core.dom.ast.IASTProblem; |
| import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; |
| import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit.IDependencyTree; |
| import org.eclipse.cdt.core.dom.ast.IBinding; |
| import org.eclipse.cdt.core.dom.ast.IFileNomination; |
| import org.eclipse.cdt.core.dom.ast.IMacroBinding; |
| import org.eclipse.cdt.core.parser.ISignificantMacros; |
| import org.eclipse.cdt.core.parser.IncludeExportPatterns; |
| import org.eclipse.cdt.core.parser.util.CharArrayObjectMap; |
| import org.eclipse.cdt.core.parser.util.CharArrayUtils; |
| import org.eclipse.cdt.internal.core.dom.parser.ASTNode; |
| import org.eclipse.cdt.internal.core.dom.parser.ASTNodeSpecification; |
| import org.eclipse.cdt.internal.core.dom.parser.ASTProblem; |
| import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; |
| |
| /** |
| * Converts the offsets relative to various contexts to the global sequence number. Also creates |
| * and stores objects that are needed to conform with the IAST... interfaces. |
| * @since 5.0 |
| */ |
| public class LocationMap implements ILocationResolver { |
| private final LexerOptions fLexerOptions; |
| private String fTranslationUnitPath; |
| private IASTTranslationUnit fTranslationUnit; |
| |
| private ArrayList<ASTPreprocessorNode> fDirectives= new ArrayList<>(); |
| private ArrayList<ASTProblem> fProblems= new ArrayList<>(); |
| private ArrayList<ASTComment> fComments= new ArrayList<>(); |
| private ArrayList<ASTMacroDefinition> fBuiltinMacros= new ArrayList<>(); |
| private ArrayList<ASTPreprocessorName> fMacroReferences= new ArrayList<>(); |
| |
| private LocationCtxFile fRootContext; |
| private LocationCtx fCurrentContext; |
| private int fLastChildInsertionOffset; |
| |
| // Stuff computed on demand |
| private IdentityHashMap<IBinding, IASTPreprocessorMacroDefinition> fMacroDefinitionMap; |
| private List<ISkippedIndexedFilesListener> fSkippedFilesListeners= new ArrayList<>(); |
| |
| public LocationMap(LexerOptions lexOptions) { |
| fLexerOptions= lexOptions; |
| } |
| |
| @Override |
| public LexerOptions getLexerOptions() { |
| return fLexerOptions; |
| } |
| |
| public void registerPredefinedMacro(IMacroBinding macro) { |
| registerPredefinedMacro(macro, null, -1); |
| } |
| |
| public void registerMacroFromIndex(IMacroBinding macro, IName originalDefinition, int expansionOffset) { |
| registerPredefinedMacro(macro, originalDefinition, expansionOffset); |
| } |
| |
| private void registerPredefinedMacro(IMacroBinding macro, IName originalDefinition, int expansionOffset) { |
| ASTMacroDefinition astMacro; |
| if (macro.isFunctionStyle()) { |
| astMacro= new ASTFunctionStyleMacroDefinition(fTranslationUnit, macro, originalDefinition, expansionOffset); |
| } else { |
| astMacro= new ASTMacroDefinition(fTranslationUnit, macro, originalDefinition, expansionOffset); |
| } |
| fBuiltinMacros.add(astMacro); |
| } |
| |
| /** |
| * The outermost context must be a translation unit. You must call this method exactly once and before |
| * creating any other context. |
| */ |
| public ILocationCtx pushTranslationUnit(String filename, AbstractCharArray buffer) { |
| assert fCurrentContext == null; |
| fTranslationUnitPath= filename; |
| fCurrentContext= fRootContext= new LocationCtxFile(null, filename, buffer, 0, 0, 0, null, true); |
| fLastChildInsertionOffset= 0; |
| return fCurrentContext; |
| } |
| |
| /** |
| * Starts an artificial context that can be used to include files without having a source that contains |
| * the include directives. |
| * @param buffer a buffer containing the include directives. |
| * @param isMacroFile whether the context is used for running the preprocessor, only. |
| */ |
| public ILocationCtx pushPreInclusion(AbstractCharArray buffer, int offset, boolean isMacroFile) { |
| assert fCurrentContext instanceof LocationCtxContainer; |
| int sequenceNumber= getSequenceNumberForOffset(offset); |
| fCurrentContext= new LocationCtxContainer((LocationCtxContainer) fCurrentContext, buffer, offset, offset, sequenceNumber); |
| fLastChildInsertionOffset= 0; |
| return fCurrentContext; |
| } |
| |
| /** |
| * Starts a context for an included file. |
| * @param buffer the buffer containing the content of the inclusion. |
| * @param filename the filename of the included file |
| * @param startOffset offset in the current context. |
| * @param nameOffset offset in the current context. |
| * @param endOffset offset in the current context |
| * @param name name of the include without delimiters ("" or <>) |
| * @param userInclude <code>true</code> when specified with double-quotes. |
| */ |
| public ILocationCtx pushInclusion(int startOffset, int nameOffset, int nameEndOffset, |
| int endOffset, AbstractCharArray buffer, String filename, char[] name, |
| boolean userInclude, boolean heuristic, boolean isSource) { |
| assert fCurrentContext instanceof LocationCtxContainer; |
| int startNumber= getSequenceNumberForOffset(startOffset); |
| int nameNumber= getSequenceNumberForOffset(nameOffset); |
| int nameEndNumber= getSequenceNumberForOffset(nameEndOffset); |
| int endNumber= getSequenceNumberForOffset(endOffset); |
| boolean exported = isExportedIncludeAt(endOffset); |
| final ASTInclusionStatement inclusionStatement= |
| new ASTInclusionStatement(fTranslationUnit, startNumber, nameNumber, nameEndNumber, |
| endNumber, name, filename, userInclude, true, heuristic, exported, null); |
| fDirectives.add(inclusionStatement); |
| fCurrentContext= new LocationCtxFile((LocationCtxContainer) fCurrentContext, filename, buffer, |
| startOffset, endOffset, endNumber, inclusionStatement, isSource); |
| fLastChildInsertionOffset= 0; |
| return fCurrentContext; |
| } |
| |
| /** |
| * Creates a name representing an implicit macro expansion. The returned name can be fed into |
| * {@link #pushMacroExpansion(int, int, int, int, IMacroBinding, IASTName[], ImageLocationInfo[])} |
| * @param macro the macro that has been expanded |
| * @param imageLocationInfo the image-location for the name of the macro. |
| */ |
| public IASTName encounterImplicitMacroExpansion(IMacroBinding macro, ImageLocationInfo imageLocationInfo) { |
| return new ASTMacroReferenceName(null, IASTPreprocessorMacroExpansion.NESTED_EXPANSION_NAME, 0, 0, macro, imageLocationInfo); |
| } |
| |
| /** |
| * Creates a name representing a macro in a defined-expression. The returned name can be fed into |
| * {@link #encounterPoundIf(int, int, int, int, boolean, IASTName[])}. |
| */ |
| public IASTName encounterDefinedExpression(IMacroBinding macro, int startOffset, int endOffset) { |
| int startNumber= getSequenceNumberForOffset(startOffset); |
| int endNumber= getSequenceNumberForOffset(endOffset); |
| return new ASTMacroReferenceName(null, IASTPreprocessorStatement.MACRO_NAME, startNumber, endNumber, macro, null); |
| } |
| |
| /** |
| * Creates a new context for the result of a (recursive) macro-expansion. |
| * @param nameOffset offset within the current context where the name for the macro-expansion starts. |
| * @param nameEndOffset offset within the current context where the name for the macro-expansion ends. |
| * @param endOffset offset within the current context where the entire macro-expansion ends. |
| * @param macro the outermost macro that got expanded. |
| * @param implicitMacroReferences an array of implicit macro-expansions. |
| * @param imageLocations an array of image-locations for the new context. |
| */ |
| public ILocationCtx pushMacroExpansion(int nameOffset, int nameEndOffset, int endOffset, int contextLength, |
| IMacroBinding macro, IASTName[] implicitMacroReferences, ImageLocationInfo[] imageLocations) { |
| assert fCurrentContext instanceof LocationCtxContainer; |
| |
| int nameNumber= getSequenceNumberForOffset(nameOffset); |
| int nameEndNumber= getSequenceNumberForOffset(nameEndOffset); |
| int endNumber= getSequenceNumberForOffset(endOffset); |
| final int length= endNumber - nameNumber; |
| |
| ASTMacroExpansion expansion= new ASTMacroExpansion(fTranslationUnit, nameNumber, endNumber); |
| ASTMacroReferenceName explicitRef= new ASTMacroReferenceName(expansion, |
| IASTPreprocessorMacroExpansion.EXPANSION_NAME, nameNumber, nameEndNumber, macro, null); |
| addMacroReference(explicitRef); |
| for (IASTName implicitMacroReference : implicitMacroReferences) { |
| ASTMacroReferenceName name = (ASTMacroReferenceName) implicitMacroReference; |
| name.setParent(expansion); |
| name.setOffsetAndLength(nameNumber, length); |
| addMacroReference(name); |
| } |
| |
| LocationCtxMacroExpansion expansionCtx= new LocationCtxMacroExpansion(this, |
| (LocationCtxContainer) fCurrentContext, nameOffset, endOffset, endNumber, |
| contextLength, imageLocations, explicitRef); |
| expansion.setContext(expansionCtx); |
| fCurrentContext= expansionCtx; |
| fLastChildInsertionOffset= 0; |
| return fCurrentContext; |
| } |
| |
| private void addMacroReference(ASTPreprocessorName name) { |
| if (name != null) { |
| fMacroReferences.add(name); |
| } |
| } |
| |
| /** |
| * Ends the current context. |
| * @param locationCtx the current context, used to check whether caller and location map are still in sync. |
| */ |
| public void popContext(ILocationCtx locationCtx) { |
| assert fCurrentContext == locationCtx; |
| final LocationCtx child= fCurrentContext; |
| final LocationCtx parent= (LocationCtx) fCurrentContext.getParent(); |
| if (parent != null) { |
| fCurrentContext= parent; |
| fLastChildInsertionOffset= child.fEndOffsetInParent; |
| parent.addChildSequenceLength(child.getSequenceLength()); |
| } |
| } |
| |
| /** |
| * Reports an inclusion that is not performed. |
| * @param startOffset offset in the current context. |
| * @param nameOffset offset in the current context. |
| * @param endOffset offset in the current context |
| * @param name name of the include without delimiters ("" or <>) |
| * @param filename the filename of the included file |
| * @param userInclude <code>true</code> when specified with double-quotes. |
| * @param active <code>true</code> when include appears in active code. |
| */ |
| public ASTInclusionStatement encounterPoundInclude(int startOffset, int nameOffset, int nameEndOffset, |
| int endOffset, char[] name, String filename, boolean userInclude, boolean active, |
| boolean heuristic, IFileNomination nominationDelegate) { |
| boolean exported = isExportedIncludeAt(endOffset); |
| startOffset= getSequenceNumberForOffset(startOffset); |
| nameOffset= getSequenceNumberForOffset(nameOffset); |
| nameEndOffset= getSequenceNumberForOffset(nameEndOffset); |
| endOffset= getSequenceNumberForOffset(endOffset); |
| final ASTInclusionStatement inc = new ASTInclusionStatement(fTranslationUnit, startOffset, |
| nameOffset, nameEndOffset, endOffset, name, filename, userInclude, active, heuristic, |
| exported, nominationDelegate); |
| fDirectives.add(inc); |
| return inc; |
| } |
| |
| private boolean isExportedIncludeAt(int includeEndOffset) { |
| boolean exported = false; |
| if (fLexerOptions.fIncludeExportPatterns != null && fCurrentContext instanceof LocationCtxFile) { |
| LocationCtxFile context = (LocationCtxFile) fCurrentContext; |
| exported = context.isInsideIncludeExportBlock(); |
| if (!exported) { |
| int distance = context.getOffsetOfIncludeExport() - includeEndOffset; |
| if (distance >= 0 && |
| CharArrayUtils.indexOf('\n', context.getSource(includeEndOffset, distance)) < 0) { |
| exported = true; |
| } |
| } |
| } |
| return exported; |
| } |
| |
| public void encounteredComment(int offset, int endOffset, boolean isBlockComment, AbstractCharArray input) { |
| ASTComment comment = new ASTComment(fTranslationUnit, getCurrentFilePath(), offset, endOffset, isBlockComment); |
| if (fLexerOptions.fIncludeExportPatterns != null && fCurrentContext instanceof LocationCtxFile) { |
| CharSequence text = getTrimmedCommentText(input.subSequence(offset, endOffset), isBlockComment); |
| IncludeExportPatterns patterns = fLexerOptions.fIncludeExportPatterns; |
| if (patterns.getIncludeExportPattern() != null |
| && patterns.getIncludeExportPattern().matcher(text).matches()) { |
| ((LocationCtxFile) fCurrentContext).setOffsetOfIncludeExport(offset); |
| } else if (patterns.getIncludeBeginExportsPattern() != null |
| && patterns.getIncludeBeginExportsPattern().matcher(text).matches()) { |
| ((LocationCtxFile) fCurrentContext).setInsideIncludeExportBlock(true); |
| } else if (patterns.getIncludeEndExportsPattern() != null |
| && patterns.getIncludeEndExportsPattern().matcher(text).matches()) { |
| ((LocationCtxFile) fCurrentContext).setInsideIncludeExportBlock(false); |
| } |
| } |
| fComments.add(comment); |
| } |
| |
| private CharSequence getTrimmedCommentText(CharSequence comment, boolean isBlockComment) { |
| int end = comment.length() - (isBlockComment ? 2 : 0); |
| int begin; |
| for (begin = 2; begin < end; begin++) { |
| if (!Character.isWhitespace(comment.charAt(begin))) |
| break; |
| } |
| if (end <= begin) |
| return ""; //$NON-NLS-1$ |
| while (--end >= begin) { |
| if (!Character.isWhitespace(comment.charAt(end))) |
| break; |
| } |
| return comment.subSequence(begin, end + 1); |
| } |
| |
| public void encounterProblem(int id, char[] arg, int offset, int endOffset) { |
| offset= getSequenceNumberForOffset(offset); |
| endOffset= getSequenceNumberForOffset(endOffset); |
| ASTProblem problem = new ASTProblem(fTranslationUnit, IASTTranslationUnit.SCANNER_PROBLEM, |
| id, arg, false, offset, endOffset); |
| fProblems.add(problem); |
| } |
| |
| public ASTElse encounterPoundElse(int startOffset, int endOffset, boolean isActive) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| endOffset= getSequenceNumberForOffset(endOffset); |
| final ASTElse astElse = new ASTElse(fTranslationUnit, startOffset, endOffset, isActive); |
| fDirectives.add(astElse); |
| return astElse; |
| } |
| |
| public ASTElif encounterPoundElif(int startOffset, int condOffset, int condEndOffset, int endOffset, boolean taken, |
| IASTName[] macrosInDefinedExpression) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| condOffset= getSequenceNumberForOffset(condOffset); |
| condEndOffset= getSequenceNumberForOffset(condEndOffset); |
| // compatible with 4.0: endOffset= getSequenceNumberForOffset(endOffset); |
| final ASTElif elif = new ASTElif(fTranslationUnit, startOffset, condOffset, condEndOffset, taken); |
| fDirectives.add(elif); |
| |
| for (IASTName element : macrosInDefinedExpression) { |
| ASTMacroReferenceName name = (ASTMacroReferenceName) element; |
| name.setParent(elif); |
| name.setPropertyInParent(IASTPreprocessorStatement.MACRO_NAME); |
| addMacroReference(name); |
| } |
| return elif; |
| } |
| |
| public ASTEndif encounterPoundEndIf(int startOffset, int endOffset) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| endOffset= getSequenceNumberForOffset(endOffset); |
| final ASTEndif stmt = new ASTEndif(fTranslationUnit, startOffset, endOffset); |
| fDirectives.add(stmt); |
| return stmt; |
| } |
| |
| public void encounterPoundError(int startOffset, int condOffset, int condEndOffset, int endOffset) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| condOffset= getSequenceNumberForOffset(condOffset); |
| condEndOffset= getSequenceNumberForOffset(condEndOffset); |
| // not using endOffset, compatible with 4.0: endOffset= getSequenceNumberForOffset(endOffset); |
| fDirectives.add(new ASTError(fTranslationUnit, startOffset, condOffset, condEndOffset)); |
| } |
| |
| public void encounterPoundPragma(int startOffset, int condOffset, int condEndOffset, int endOffset) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| condOffset= getSequenceNumberForOffset(condOffset); |
| condEndOffset= getSequenceNumberForOffset(condEndOffset); |
| // not using endOffset, compatible with 4.0: endOffset= getSequenceNumberForOffset(endOffset); |
| fDirectives.add(new ASTPragma(fTranslationUnit, startOffset, condOffset, condEndOffset)); |
| } |
| |
| public void encounterPragmaOperator(int startNumber, int condNumber, int condEndNumber, int endNumber) { |
| fDirectives.add(new ASTPragmaOperator(fTranslationUnit, startNumber, condNumber, condEndNumber, endNumber)); |
| } |
| |
| public ASTIfdef encounterPoundIfdef(int startOffset, int condOffset, int condEndOffset, |
| int endOffset, boolean taken, IMacroBinding macro) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| condOffset= getSequenceNumberForOffset(condOffset); |
| condEndOffset= getSequenceNumberForOffset(condEndOffset); |
| // not using endOffset, compatible with 4.0: endOffset= getSequenceNumberForOffset(endOffset); |
| final ASTIfdef ifdef = new ASTIfdef(fTranslationUnit, startOffset, condOffset, condEndOffset, taken, macro); |
| fDirectives.add(ifdef); |
| addMacroReference(ifdef.getMacroReference()); |
| return ifdef; |
| } |
| |
| public ASTIfndef encounterPoundIfndef(int startOffset, int condOffset, int condEndOffset, |
| int endOffset, boolean taken, IMacroBinding macro) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| condOffset= getSequenceNumberForOffset(condOffset); |
| condEndOffset= getSequenceNumberForOffset(condEndOffset); |
| // not using endOffset, compatible with 4.0: endOffset= getSequenceNumberForOffset(endOffset); |
| final ASTIfndef ifndef = new ASTIfndef(fTranslationUnit, startOffset, condOffset, condEndOffset, taken, macro); |
| fDirectives.add(ifndef); |
| addMacroReference(ifndef.getMacroReference()); |
| return ifndef; |
| } |
| |
| public ASTIf encounterPoundIf(int startOffset, int condOffset, int condEndOffset, int endOffset, |
| boolean taken, IASTName[] macrosInDefinedExpression) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| condOffset= getSequenceNumberForOffset(condOffset); |
| condEndOffset= getSequenceNumberForOffset(condEndOffset); |
| // not using endOffset, compatible with 4.0: endOffset= getSequenceNumberForOffset(endOffset); |
| final ASTIf astif = new ASTIf(fTranslationUnit, startOffset, condOffset, condEndOffset, taken); |
| fDirectives.add(astif); |
| for (IASTName element : macrosInDefinedExpression) { |
| ASTMacroReferenceName name = (ASTMacroReferenceName) element; |
| name.setParent(astif); |
| name.setPropertyInParent(IASTPreprocessorStatement.MACRO_NAME); |
| addMacroReference(name); |
| } |
| return astif; |
| } |
| |
| public void encounterPoundDefine(int startOffset, int nameOffset, int nameEndOffset, |
| int expansionOffset, int endOffset, boolean isActive, IMacroBinding macrodef) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| nameOffset= getSequenceNumberForOffset(nameOffset); |
| nameEndOffset= getSequenceNumberForOffset(nameEndOffset); |
| expansionOffset= getSequenceNumberForOffset(expansionOffset); |
| endOffset= getSequenceNumberForOffset(endOffset); |
| ASTPreprocessorNode astMacro; |
| if (!macrodef.isFunctionStyle()) { |
| astMacro= new ASTMacroDefinition(fTranslationUnit, macrodef, startOffset, nameOffset, |
| nameEndOffset, expansionOffset, endOffset, isActive); |
| } else { |
| astMacro= new ASTFunctionStyleMacroDefinition(fTranslationUnit, macrodef, startOffset, |
| nameOffset, nameEndOffset, expansionOffset, endOffset, isActive); |
| } |
| fDirectives.add(astMacro); |
| } |
| |
| public void encounterPoundUndef(IMacroBinding definition, int startOffset, int nameOffset, |
| int nameEndOffset, int endOffset, char[] name, boolean isActive) { |
| startOffset= getSequenceNumberForOffset(startOffset); |
| nameOffset= getSequenceNumberForOffset(nameOffset); |
| nameEndOffset= getSequenceNumberForOffset(nameEndOffset); |
| // not using endOffset, compatible with 4.0: endOffset= getSequenceNumberForOffset(endOffset); |
| final ASTUndef undef = new ASTUndef(fTranslationUnit, name, startOffset, nameOffset, |
| nameEndOffset, definition, isActive); |
| fDirectives.add(undef); |
| addMacroReference(undef.getMacroName()); |
| } |
| |
| @Override |
| public void setRootNode(IASTTranslationUnit root) { |
| fTranslationUnit= root; |
| if (fTranslationUnit instanceof ISkippedIndexedFilesListener) { |
| fSkippedFilesListeners.add((ISkippedIndexedFilesListener) root); |
| } |
| } |
| |
| @Override |
| public String getTranslationUnitPath() { |
| return fTranslationUnitPath; |
| } |
| |
| /** |
| * Line number of offset in current context. |
| * @param offset in current context. |
| */ |
| public int getCurrentLineNumber(int offset) { |
| return fCurrentContext.getLineNumber(offset); |
| } |
| |
| /** |
| * Returns the filename of the current context. If the context is a macro-expansion the filename of |
| * the enclosing file is returned. |
| */ |
| public String getCurrentFilePath() { |
| return fCurrentContext.getFilePath(); |
| } |
| |
| /** |
| * Returns the sequence number corresponding to the offset in the current context. |
| * <p> |
| * You must insert all child contexts before the given offset before conversion. |
| */ |
| int getSequenceNumberForOffset(int offset) { |
| return fCurrentContext.getSequenceNumberForOffset(offset, offset < fLastChildInsertionOffset); |
| } |
| |
| @Override |
| public String getContainingFilePath(int sequenceNumber) { |
| LocationCtx ctx= fRootContext.findSurroundingContext(sequenceNumber, 1); |
| return new String(ctx.getFilePath()); |
| } |
| |
| @Override |
| public boolean isPartOfSourceFile(int sequenceNumber) { |
| LocationCtx ctx= fRootContext.findSurroundingContext(sequenceNumber, 1); |
| if (ctx == fRootContext && fTranslationUnit != null) |
| return !fTranslationUnit.isHeaderUnit(); |
| |
| return ctx.isSourceFile(); |
| } |
| |
| @Override |
| public ASTFileLocation getMappedFileLocation(int sequenceNumber, int length) { |
| return fRootContext.findMappedFileLocation(sequenceNumber, length); |
| } |
| |
| @Override |
| public int convertToSequenceEndNumber(int sequenceNumber) { |
| return fRootContext.convertToSequenceEndNumber(sequenceNumber); |
| } |
| |
| @Override |
| public char[] getUnpreprocessedSignature(IASTFileLocation loc) { |
| ASTFileLocation floc= convertFileLocation(loc); |
| if (floc == null) { |
| return CharArrayUtils.EMPTY; |
| } |
| return floc.getSource(); |
| } |
| |
| @Override |
| public IASTPreprocessorMacroExpansion[] getMacroExpansions(IASTFileLocation loc) { |
| ASTFileLocation floc= convertFileLocation(loc); |
| if (floc == null) { |
| return IASTPreprocessorMacroExpansion.EMPTY_ARRAY; |
| } |
| |
| LocationCtxFile ctx= floc.getLocationContext(); |
| ArrayList<IASTPreprocessorMacroExpansion> list= new ArrayList<>(); |
| |
| ctx.collectMacroExpansions(floc.getNodeOffset(), floc.getNodeLength(), list); |
| return list.toArray(new IASTPreprocessorMacroExpansion[list.size()]); |
| } |
| |
| private ASTFileLocation convertFileLocation(IASTFileLocation loc) { |
| if (loc == null) { |
| return null; |
| } |
| if (loc instanceof ASTFileLocation) { |
| return (ASTFileLocation) loc; |
| } |
| final String fileName = loc.getFileName(); |
| final int nodeOffset = loc.getNodeOffset(); |
| final int nodeLength = loc.getNodeLength(); |
| int sequenceNumber= getSequenceNumberForFileOffset(fileName, nodeOffset); |
| if (sequenceNumber < 0) { |
| return null; |
| } |
| |
| int length= 0; |
| if (nodeLength > 0) { |
| length= getSequenceNumberForFileOffset(fileName, nodeOffset + nodeLength - 1) + 1 - sequenceNumber; |
| if (length < 0) { |
| return null; |
| } |
| } |
| return getMappedFileLocation(sequenceNumber, length); |
| } |
| |
| @Override |
| public IASTNodeLocation[] getLocations(int sequenceNumber, int length) { |
| ArrayList<IASTNodeLocation> result= new ArrayList<>(); |
| fRootContext.collectLocations(sequenceNumber, length, result); |
| return result.toArray(new IASTNodeLocation[result.size()]); |
| } |
| |
| @Override |
| public boolean isPartOfTranslationUnitFile(int sequenceNumber) { |
| return fRootContext.isThisFile(sequenceNumber); |
| } |
| |
| @Override |
| public IASTImageLocation getImageLocation(int sequenceNumber, int length) { |
| ArrayList<IASTNodeLocation> result= new ArrayList<>(); |
| fRootContext.collectLocations(sequenceNumber, length, result); |
| if (result.size() != 1) { |
| return null; |
| } |
| IASTNodeLocation loc= result.get(0); |
| if (loc instanceof IASTFileLocation) { |
| IASTFileLocation floc= (IASTFileLocation) loc; |
| return new ASTImageLocation(IASTImageLocation.REGULAR_CODE, |
| floc.getFileName(), floc.getNodeOffset(), floc.getNodeLength()); |
| } |
| if (loc instanceof ASTMacroExpansionLocation) { |
| ASTMacroExpansionLocation mel= (ASTMacroExpansionLocation) loc; |
| return mel.getImageLocation(); |
| } |
| return null; |
| } |
| |
| @Override |
| public void findPreprocessorNode(ASTNodeSpecification<?> nodeSpec) { |
| final int sequenceStart= nodeSpec.getSequenceStart(); |
| final int sequenceEnd= nodeSpec.getSequenceEnd(); |
| |
| // check directives |
| int from= findLastNodeBefore(fDirectives, sequenceStart); |
| for (int i= from + 1; i < fDirectives.size(); i++) { |
| ASTPreprocessorNode directive= fDirectives.get(i); |
| if (directive.getOffset() > sequenceEnd) { |
| break; |
| } |
| directive.findNode(nodeSpec); |
| } |
| |
| // check macro references and expansions |
| from= findLastMacroReferenceBefore(fMacroReferences, sequenceStart); |
| for (int i= from + 1; i < fMacroReferences.size(); i++) { |
| ASTPreprocessorNode macroRef= fMacroReferences.get(i); |
| if (macroRef.getOffset() > sequenceEnd) { |
| break; |
| } |
| if (macroRef.getPropertyInParent() == IASTPreprocessorMacroExpansion.NESTED_EXPANSION_NAME) { |
| continue; |
| } |
| nodeSpec.visit(macroRef); |
| IASTNode parent= macroRef.getParent(); |
| if (parent instanceof ASTMacroExpansion) { |
| ASTMacroExpansion expansion= (ASTMacroExpansion) parent; |
| assert expansion.getMacroReference() == macroRef; |
| |
| if (nodeSpec.canContainMatches(expansion)) { |
| nodeSpec.visit(expansion); |
| if (!nodeSpec.requiresClass(IASTPreprocessorMacroExpansion.class)) { |
| LocationCtxMacroExpansion ctx= expansion.getContext(); |
| if (fTranslationUnit != null) { |
| FindNodeByImageLocation visitor= new FindNodeByImageLocation(ctx.fSequenceNumber, ctx.getSequenceLength(), nodeSpec); |
| fTranslationUnit.accept(visitor); |
| } |
| ASTPreprocessorName[] nestedMacros= expansion.getNestedMacroReferences(); |
| for (ASTPreprocessorName nested : nestedMacros) { |
| IASTImageLocation imgLoc= nested.getImageLocation(); |
| if (imgLoc != null && imgLoc.getLocationKind() == IASTImageLocation.ARGUMENT_TO_MACRO_EXPANSION) { |
| nodeSpec.visit(nested, imgLoc); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private int findLastNodeBefore(ArrayList<? extends ASTPreprocessorNode> nodes, int sequenceStart) { |
| int lower= -1; |
| int upper= nodes.size() - 1; |
| while (lower < upper) { |
| int middle= (lower + upper + 1) / 2; |
| ASTPreprocessorNode candidate= nodes.get(middle); |
| if (candidate.getOffset() + candidate.getLength() >= sequenceStart) { |
| upper= middle - 1; |
| } else { |
| lower= middle; |
| } |
| } |
| return lower; |
| } |
| |
| private int findLastMacroReferenceBefore(ArrayList<? extends ASTPreprocessorName> nodes, int sequenceStart) { |
| int lower= -1; |
| int upper= nodes.size() - 1; |
| while (lower < upper) { |
| int middle= (lower + upper + 1) / 2; |
| ASTPreprocessorNode candidate= nodes.get(middle); |
| IASTNode parent= candidate.getParent(); |
| if (parent instanceof ASTMacroExpansion) { |
| candidate= (ASTMacroExpansion) parent; |
| } |
| if (candidate.getOffset() + candidate.getLength() >= sequenceStart) { |
| upper= middle - 1; |
| } else { |
| lower= middle; |
| } |
| } |
| return lower; |
| } |
| |
| @Override |
| public int getSequenceNumberForFileOffset(String filePath, int fileOffset) { |
| LocationCtx ctx= fRootContext; |
| if (filePath != null) { |
| ArrayDeque<LocationCtx> contexts= new ArrayDeque<>(); |
| while (ctx != null) { |
| if (ctx instanceof LocationCtxFile) { |
| if (filePath.equals(ctx.getFilePath())) { |
| break; |
| } |
| } |
| contexts.addAll(ctx.getChildren()); |
| ctx= contexts.pollFirst(); |
| } |
| } |
| if (ctx != null) { |
| return ctx.getSequenceNumberForOffset(fileOffset, true); |
| } |
| return -1; |
| } |
| |
| @Override |
| public IASTFileLocation flattenLocations(IASTNodeLocation[] locations) { |
| if (locations.length == 0) { |
| return null; |
| } |
| IASTFileLocation from= locations[0].asFileLocation(); |
| IASTFileLocation to= locations[locations.length - 1].asFileLocation(); |
| if (from == to) { |
| return from; |
| } |
| if (from instanceof ASTFileLocation && to instanceof ASTFileLocation) { |
| int sequenceNumber= ((ASTFileLocation) from).getSequenceNumber(); |
| int length= ((ASTFileLocation) from).getSequenceEndNumber() - sequenceNumber; |
| if (length > 0) { |
| return getMappedFileLocation(sequenceNumber, length); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public IASTPreprocessorMacroDefinition[] getMacroDefinitions() { |
| ArrayList<IASTPreprocessorMacroDefinition> result= new ArrayList<>(); |
| for (ASTPreprocessorNode directive : fDirectives) { |
| if (directive instanceof IASTPreprocessorMacroDefinition) { |
| result.add((IASTPreprocessorMacroDefinition) directive); |
| } |
| } |
| return result.toArray(new IASTPreprocessorMacroDefinition[result.size()]); |
| } |
| |
| @Override |
| public IASTPreprocessorIncludeStatement[] getIncludeDirectives() { |
| ArrayList<IASTPreprocessorIncludeStatement> result= new ArrayList<>(); |
| for (ASTPreprocessorNode directive : fDirectives) { |
| if (directive instanceof IASTPreprocessorIncludeStatement) { |
| result.add((IASTPreprocessorIncludeStatement) directive); |
| } |
| } |
| return result.toArray(new IASTPreprocessorIncludeStatement[result.size()]); |
| } |
| |
| @Override |
| public IASTComment[] getComments() { |
| return fComments.toArray(new IASTComment[fComments.size()]); |
| } |
| |
| @Override |
| public IASTPreprocessorStatement[] getAllPreprocessorStatements() { |
| return fDirectives.toArray(new IASTPreprocessorStatement[fDirectives.size()]); |
| } |
| |
| @Override |
| public IASTPreprocessorMacroDefinition[] getBuiltinMacroDefinitions() { |
| return fBuiltinMacros.toArray(new IASTPreprocessorMacroDefinition[fBuiltinMacros.size()]); |
| } |
| |
| @Override |
| public IASTProblem[] getScannerProblems() { |
| return fProblems.toArray(new IASTProblem[fProblems.size()]); |
| } |
| |
| @Override |
| public int getScannerProblemsCount() { |
| return fProblems.size(); |
| } |
| |
| @Override |
| public IASTName[] getDeclarations(IMacroBinding binding) { |
| IASTPreprocessorMacroDefinition def = getMacroDefinition(binding); |
| return def == null ? IASTName.EMPTY_NAME_ARRAY: new IASTName[] { def.getName() }; |
| } |
| |
| IASTPreprocessorMacroDefinition getMacroDefinition(IMacroBinding binding) { |
| if (fMacroDefinitionMap == null) { |
| fMacroDefinitionMap= new IdentityHashMap<>(); |
| for (int i = 0; i < fBuiltinMacros.size(); i++) { |
| final IASTPreprocessorMacroDefinition def = fBuiltinMacros.get(i); |
| final IASTName name = def.getName(); |
| if (name != null) { |
| fMacroDefinitionMap.put(name.getBinding(), def); |
| } |
| } |
| IASTPreprocessorMacroDefinition[] defs= getMacroDefinitions(); |
| for (final IASTPreprocessorMacroDefinition def : defs) { |
| final IASTName name = def.getName(); |
| if (name != null) { |
| fMacroDefinitionMap.put(name.getBinding(), def); |
| } |
| } |
| } |
| return fMacroDefinitionMap.get(binding); |
| } |
| |
| @Override |
| public IASTName[] getReferences(IMacroBinding binding) { |
| List<IASTName> result= new ArrayList<>(); |
| for (IASTName name : fMacroReferences) { |
| if (name.getBinding() == binding) { |
| result.add(name); |
| } |
| } |
| return result.toArray(new IASTName[result.size()]); |
| } |
| |
| public IASTName[] getMacroReferences() { |
| return fMacroReferences.toArray(new IASTName[fMacroReferences.size()]); |
| } |
| |
| public ASTPreprocessorName[] getNestedMacroReferences(ASTMacroExpansion expansion) { |
| final IASTName explicitRef= expansion.getMacroReference(); |
| List<ASTPreprocessorName> result= new ArrayList<>(); |
| for (ASTPreprocessorName name : fMacroReferences) { |
| if (name.getParent() == expansion && name != explicitRef) { |
| result.add(name); |
| } |
| } |
| return result.toArray(new ASTPreprocessorName[result.size()]); |
| } |
| |
| @Override |
| public IDependencyTree getDependencyTree() { |
| return new DependencyTree(fRootContext); |
| } |
| |
| public void cleanup() { |
| } |
| |
| public void skippedFile(int sequenceNumber, InternalFileContent fi) { |
| for (ISkippedIndexedFilesListener l : fSkippedFilesListeners) { |
| l.skippedFile(sequenceNumber, fi); |
| } |
| } |
| |
| public void parsingFile(InternalFileContentProvider fileContentProvider, |
| InternalFileContent fileContent) { |
| for (ISkippedIndexedFilesListener l : fSkippedFilesListeners) { |
| l.parsingFile(fileContentProvider, fileContent); |
| } |
| } |
| |
| public IFileNomination reportPragmaOnceSemantics(ILocationCtx locationCtx) { |
| if (locationCtx == fRootContext) { |
| if (fTranslationUnit != null) { |
| fTranslationUnit.setPragmaOnceSemantics(true); |
| } |
| return fTranslationUnit; |
| } else if (locationCtx instanceof LocationCtxFile) { |
| ASTInclusionStatement stmt = ((LocationCtxFile) locationCtx).getInclusionStatement(); |
| if (stmt != null) { |
| stmt.setPragamOnceSemantics(true); |
| } |
| return stmt; |
| } |
| return null; |
| } |
| |
| public void endTranslationUnit(int endOffset, CharArrayObjectMap<char[]> sigMacros) { |
| if (fTranslationUnit != null) { |
| int offset= getSequenceNumberForOffset(endOffset); |
| ((ASTNode) fTranslationUnit).setLength(offset); |
| |
| if (sigMacros != null) { |
| ISignificantMacros sig = sigMacros.isEmpty() ? |
| ISignificantMacros.NONE : new SignificantMacros(sigMacros); |
| fTranslationUnit.setSignificantMacros(sig); |
| } |
| } |
| } |
| } |