Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc-Andre Laperle2020-09-08 05:00:47 +0000
committerMarc-Andre Laperle2020-09-27 19:12:52 +0000
commitcb0797481faaace81a46ce2989fc319d160ba1ef (patch)
tree80de84f5c80430b20650029f4bf7969eb5e9c42a /core/org.eclipse.cdt.core.tests
parent2015e9b00901b785bce222d58f02f7009f943a1b (diff)
downloadorg.eclipse.cdt-cb0797481faaace81a46ce2989fc319d160ba1ef.tar.gz
org.eclipse.cdt-cb0797481faaace81a46ce2989fc319d160ba1ef.tar.xz
org.eclipse.cdt-cb0797481faaace81a46ce2989fc319d160ba1ef.zip
Bug 566918 - [C++17] Support for __has_include (standard) and
__has_include_next (extension) __has_include evaluates whether of the header name passed as parameter exists. This can only be evaluated as part of a #if directive. Interestingly, it also has to be reported as defined, i.e. #if defined(__has_include) or #ifdef. In order to report this as defined, this implementation adds it as a macro but during macro expansion, it's actually converted as a dedicated token type. Then this token gets evaluated during normal preprocessor expression evaluation. In order to parse header names, there were several options. The main problem is that header tokens (tQUOTE_HEADER_NAME, tSYSTEM_HEADER_NAME) are actually produced by the Lexer as part of a special mode (setInsideIncludeDirective) set during the handling of #include. For expression evaluation, the tokens are already generated without setInsideIncludeDirective therefore we only have plain string and < > tokens. One approach would be to generate header tokens "earlier" than executing we need to track a new state while fetching token to configure the Lexer (setInsideIncludeDirective) when in the context of an __has_include. There are also complications due to macro expansion within the __has_include where after one expansion, we don't have a lexer in the context anymore, introducing more changes. Another approach would be to remove the Header token creation from the Lexer itself and let the preprocessor assemble the tokens into an header string, in both cases of #include and __has_include. This mostly works and is the approach used in Clang, but the problem is that whereas Clang keeps track of leading spaces of tokens, CDT doesn't. This means with such change that CDT would now allow #include < iostream > (notice the white space). I think this is too big of a downside and also too big of a change to introduce this handling of whitespace at the token level. The approach used here is more conservative and isolated but also shares less common logic with #include processing. The non-header token (string, <, etc) are assembled into a header string only in the case of a __has_include. So a downside will be that #include and __has_include will be inconsistent in regards of leading/trailing space parsing but I feel like this is better than making #include more permissive. Change-Id: I5b9f5c616c8d999e0c916a85b41f96e20037b651 Signed-off-by: Marc-Andre Laperle <malaperle@gmail.com>
Diffstat (limited to 'core/org.eclipse.cdt.core.tests')
-rw-r--r--core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java133
1 files changed, 133 insertions, 0 deletions
diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java
index 1f81668a075..882a9b89077 100644
--- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java
+++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java
@@ -16,10 +16,13 @@
package org.eclipse.cdt.core.parser.tests.scanner;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.parser.ExtendedScannerInfo;
import org.eclipse.cdt.core.parser.FileContent;
+import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.ParserLanguage;
@@ -261,6 +264,136 @@ public class InclusionTests extends PreprocessorTestsBase {
validateEOF();
}
+ // #if __has_include("does_not_exist.h")
+ // inactive
+ // #endif
+ // #if !__has_include("does_not_exist.h")
+ // identifier
+ // #endif
+ //
+ // #if __has_include("test.h")
+ // identifier2
+ // #endif
+ // #if !__has_include("test.h")
+ // inactive
+ // #endif
+ //
+ // #if __has_include(<test.h>)
+ // identifier3
+ // #endif
+ //
+ // #define MACRO __has_include("test.h")
+ // #if MACRO
+ // identifier4
+ // #endif
+ //
+ // #define MACRO2 __has_include(<test.h>)
+ // #if MACRO2
+ // identifier5
+ // #endif
+ //
+ // #define HEADER_NAME "test.h"
+ // #define MACRO3 __has_include(HEADER_NAME)
+ // #if MACRO3
+ // identifier6
+ // #endif
+ //
+ // //Note: This one works with Clang and MSVC but not GCC.
+ // #define HEADER_NAME2 ("test.h")
+ // #define MACRO4 __has_include HEADER_NAME2
+ // #if MACRO4
+ // identifier7
+ // #endif
+ //
+ // #ifdef __has_include
+ // identifier8
+ // #endif
+ //
+ // #if defined(__has_include)
+ // identifier9
+ // #endif
+ public void testHasInclude() throws Exception {
+ importFile("test.h", "");
+ IFile base = importFile("test.cpp", getAboveComment());
+ IScannerInfo scannerInfo = new ExtendedScannerInfo(Collections.EMPTY_MAP,
+ new String[] { fProject.getProject().getLocation().toOSString() }, new String[] {}, null);
+ FileContent reader = FileContent.create(base);
+ initializeScanner(reader, ParserLanguage.CPP, ParserMode.COMPLETE_PARSE, scannerInfo);
+ validateIdentifier("identifier");
+ validateIdentifier("identifier2");
+ validateIdentifier("identifier3");
+ validateIdentifier("identifier4");
+ validateIdentifier("identifier5");
+ validateIdentifier("identifier6");
+ validateIdentifier("identifier7");
+ validateIdentifier("identifier8");
+ validateIdentifier("identifier9");
+ validateEOF();
+ }
+
+ // #include "foo.h"
+ //
+ // #ifdef __has_include_next
+ // identifier4
+ // #endif
+ //
+ // #if defined(__has_include_next)
+ // identifier5
+ // #endif
+
+ // identifier
+ // #include "intermed.h"
+
+ // identifier2
+ // #if __has_include_next(<foo.h>)
+ // #include_next <foo.h>
+ // #endif
+
+ // identifier3
+ public void testHasIncludeNext() throws Exception {
+ StringBuilder[] sections = getTestContent(4);
+ String baseFile = sections[0].toString(); //$NON-NLS-1$
+ String foo1 = sections[1].toString(); //$NON-NLS-1$
+ String intermed = sections[2].toString(); //$NON-NLS-1$
+ String foo2 = sections[3].toString(); //$NON-NLS-1$
+
+ IFolder one = importFolder("one"); //$NON-NLS-1$
+ IFolder two = importFolder("two"); //$NON-NLS-1$
+ IFile base = importFile("base.cpp", baseFile); //$NON-NLS-1$
+ importFile("one/foo.h", foo1); //$NON-NLS-1$
+ importFile("one/intermed.h", intermed); //$NON-NLS-1$
+ importFile("two/foo.h", foo2); //$NON-NLS-1$
+
+ String[] path = new String[2];
+ path[0] = one.getLocation().toOSString();
+ path[1] = two.getLocation().toOSString();
+
+ Map<String, String> definedSymbols = new HashMap<>();
+ definedSymbols.put("__GNUC__", "5");
+ definedSymbols.put("__GNUC_MINOR__", "0");
+
+ IScannerInfo scannerInfo = new ExtendedScannerInfo(definedSymbols, path, new String[] {}, null);
+ FileContent reader = FileContent.create(base);
+ initializeScanner(reader, ParserLanguage.CPP, ParserMode.COMPLETE_PARSE, scannerInfo);
+ validateIdentifier("identifier");
+ validateIdentifier("identifier2");
+ validateIdentifier("identifier3");
+ validateIdentifier("identifier4");
+ validateIdentifier("identifier5");
+ }
+
+ // void foo() {
+ // __has_include;
+ // }
+ public void testHasIncludeProblem() throws Exception {
+ IFile base = importFile("test.cpp", getAboveComment());
+ IScannerInfo scannerInfo = new ExtendedScannerInfo(Collections.EMPTY_MAP, null, new String[] {}, null);
+ FileContent reader = FileContent.create(base);
+ initializeScanner(reader, ParserLanguage.CPP, ParserMode.COMPLETE_PARSE, scannerInfo);
+ fullyTokenize();
+ validateProblem(0, IProblem.PREPROCESSOR_INVALID_USE_OUTSIDE_PREPROCESSOR_DIRECTIVE, "__has_include");
+ }
+
// #include <inc/test.h>
public void testRelativeIncludes_243170() throws Exception {
String content = getAboveComment();

Back to the top