Skip to content

Commit 19be9a5

Browse files
authored
Merge pull request #1665 from ivangalkin/preprocessor_forced_defines
CxxPreprocessor: cache forced includes, forced & global defines
2 parents b11375a + 889b7c6 commit 19be9a5

File tree

4 files changed

+274
-61
lines changed

4 files changed

+274
-61
lines changed

cxx-squid/src/main/java/org/sonar/cxx/preprocessor/CxxPreprocessor.java

Lines changed: 180 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676

7777
public class CxxPreprocessor extends Preprocessor {
7878

79-
private static final String CPLUSPLUS = "__cplusplus";
8079
private static final String EVALUATED_TO_FALSE = "[{}:{}]: '{}' evaluated to false, skipping tokens that follow";
8180
private static final String MISSING_INCLUDE_MSG
8281
= "Preprocessor: {} include directive error(s). This is only relevant if parser creates syntax errors."
@@ -90,8 +89,55 @@ public class CxxPreprocessor extends Preprocessor {
9089
private File currentContextFile;
9190

9291
private final Parser<Grammar> pplineParser;
92+
/**
93+
* Contains a standard set of pre-compiled macros, which are defined for each
94+
* compilation unit. This map is used if there is no specific compilation unit
95+
* settings. The map contains
96+
* <ol>
97+
* <li>{@link Macro#STANDARD_MACROS}</li>
98+
* <li>{@link CxxConfiguration#getDefines()} and</li>
99+
* <li>forced includes (see
100+
* {@link CxxConfiguration#getForceIncludeFiles()})</li>
101+
* </ol>
102+
* All hi-prio macros are pre-parsed while construction of
103+
* {@link CxxPreprocessor}
104+
*/
93105
private final MapChain<String, Macro> fixedMacros = new MapChain<>();
106+
/**
107+
* If CxxConfiguration contains any compilation unit settings, this map is
108+
* filled with a set of pre-compiled macros. Those macros must be defined for
109+
* each compilation unit:
110+
* <ol>
111+
* <li>{@link Macro#UNIT_MACROS}</li>
112+
* <li>{@link CxxConfiguration#getDefines()} and</li>
113+
* <li>forced includes (see
114+
* {@link CxxConfiguration#getForceIncludeFiles()})</li>
115+
* </ol>
116+
* The map is immutable; macros are pre-parsed while construction of
117+
* {@link CxxPreprocessor}
118+
*/
119+
private final Map<String, Macro> predefinedUnitMacros;
120+
/**
121+
* If current processed file has some specific configuration settings, this
122+
* map will be filled with relevant macros and defines:
123+
* <ol>
124+
* <li>predefined compilation unit macros will be added (see
125+
* {@link CxxPreprocessor#predefinedUnitMacros}</li>
126+
* <li>specific unit settings will be parsed and added (see
127+
* {@link CxxConfiguration#getCompilationUnitSettings(String)}</li>
128+
* </ol>
129+
* Map is recalculated each time {@link CxxPreprocessor} is about to analyze a
130+
* new file (see {@link CxxPreprocessor#init()}.
131+
*
132+
* If current processed file has no specific configuration settings -
133+
* {@link CxxPreprocessor#fixedMacros} will be used.
134+
*/
94135
private MapChain<String, Macro> unitMacros;
136+
/**
137+
* Pre-parsed defines from the global compilation unit settings, see
138+
* {@link CxxConfiguration#getGlobalCompilationUnitSettings()}.
139+
*/
140+
private final Map<String, Macro> globalUnitMacros;
95141
private final Set<File> analysedFiles = new HashSet<>();
96142
private final SourceCodeProvider codeProvider;
97143
private SourceCodeProvider unitCodeProvider;
@@ -127,36 +173,82 @@ public CxxPreprocessor(SquidAstVisitorContext<Grammar> context,
127173

128174
pplineParser = CppParser.create(conf);
129175

176+
final Map<String, Macro> configuredMacros = parseConfiguredMacros();
177+
fillFixedMacros(configuredMacros);
178+
predefinedUnitMacros = parsePredefinedUnitMacros(configuredMacros);
179+
globalUnitMacros = parseGlobalUnitMacros();
180+
181+
ctorInProgress = false;
182+
}
183+
184+
private Map<String, Macro> parseConfiguredMacros() {
185+
final List<String> configuredDefines = conf.getDefines();
186+
if (configuredDefines.isEmpty()) {
187+
return Collections.emptyMap();
188+
}
189+
LOG.debug("parsing configured defines");
190+
return parseMacroDefinitions(configuredDefines);
191+
}
192+
193+
private void fillFixedMacros(Map<String, Macro> configuredMacros) {
194+
if (!ctorInProgress || (getMacros() != fixedMacros) || !fixedMacros.getHighPrioMap().isEmpty()) {
195+
throw new IllegalStateException("Preconditions for initial fill-out of fixedMacros were violated");
196+
}
197+
130198
try {
131199
getMacros().setHighPrio(true);
200+
getMacros().putAll(Macro.STANDARD_MACROS);
201+
getMacros().putAll(configuredMacros);
202+
parseForcedIncludes();
203+
} finally {
204+
getMacros().setHighPrio(false);
205+
}
206+
}
132207

133-
// parse the configured defines and store into the macro library
134-
for (String define : conf.getDefines()) {
135-
LOG.debug("parsing external macro: '{}'", define);
136-
if (!"".equals(define)) {
137-
Macro macro = parseMacroDefinition("#define " + define);
138-
LOG.debug("storing external macro: '{}'", macro);
139-
getMacros().put(macro.name, macro);
140-
}
141-
}
208+
/**
209+
* Create temporary unitMacros map; This map will be used as an active macros'
210+
* storage while parsing of forced includes. After parsing was over extract
211+
* resulting macros and destroy the unitMacros. fixedMacros will be set as
212+
* active macros again.
213+
*/
214+
private Map<String, Macro> parsePredefinedUnitMacros(Map<String, Macro> configuredMacros) {
215+
if (!ctorInProgress || (unitMacros != null)) {
216+
throw new IllegalStateException("Preconditions for initial fill-out of predefinedUnitMacros were violated");
217+
}
142218

143-
// set standard macros
144-
getMacros().putAll(Macro.STANDARD_MACROS);
219+
if (conf.getCompilationUnitSourceFiles().isEmpty() && (conf.getGlobalCompilationUnitSettings() == null)) {
220+
// configuration doesn't contain any settings for compilation units.
221+
// CxxPreprocessor will use fixedMacros only
222+
return Collections.emptyMap();
223+
}
145224

146-
// parse the configured force includes and store into the macro library
147-
for (String include : conf.getForceIncludeFiles()) {
148-
LOG.debug("parsing force include: '{}'", include);
149-
if (!"".equals(include)) {
150-
parseIncludeLine("#include \"" + include + "\"", "sonar." + this.language.getPropertiesKey()
151-
+ ".forceIncludes", conf.getEncoding());
152-
}
153-
}
225+
unitMacros = new MapChain<>();
226+
if (getMacros() != unitMacros) {
227+
throw new IllegalStateException("expected unitMacros as active macros map");
228+
}
229+
230+
try {
231+
getMacros().setHighPrio(true);
232+
getMacros().putAll(Macro.UNIT_MACROS);
233+
getMacros().putAll(configuredMacros);
234+
parseForcedIncludes();
235+
final HashMap<String, Macro> result = new HashMap<>(unitMacros.getHighPrioMap());
236+
return result;
154237
} finally {
155-
getMacros().setHighPrio(false);
156-
ctorInProgress = false;
238+
getMacros().setHighPrio(false); // just for the symmetry
239+
unitMacros = null; // remove unitMacros, switch getMacros() to fixedMacros
157240
}
158241
}
159242

243+
private Map<String, Macro> parseGlobalUnitMacros() {
244+
final CxxCompilationUnitSettings globalCUSettings = conf.getGlobalCompilationUnitSettings();
245+
if (globalCUSettings == null) {
246+
return Collections.emptyMap();
247+
}
248+
LOG.debug("parsing global compilation unit defines");
249+
return parseMacroDefinitions(globalCUSettings.getDefines());
250+
}
251+
160252
public static void finalReport() {
161253
if (missingIncludeFilesCounter != 0) {
162254
LOG.warn(MISSING_INCLUDE_MSG, missingIncludeFilesCounter);
@@ -457,14 +549,14 @@ public void init() {
457549
Objects.requireNonNull(context.getFile(), "SquidAstVisitorContext::getFile() must be non-null");
458550
compilationUnitSettings = conf.getCompilationUnitSettings(currentContextFile.getAbsolutePath());
459551

552+
boolean useGlobalCUSettings = false;
553+
460554
if (compilationUnitSettings != null) {
461555
LOG.debug("compilation unit settings for: '{}'", currentContextFile);
462-
} else {
556+
} else if (conf.getGlobalCompilationUnitSettings() != null) {
463557
compilationUnitSettings = conf.getGlobalCompilationUnitSettings();
464-
465-
if (compilationUnitSettings != null) {
466-
LOG.debug("global compilation unit settings for: '{}'", currentContextFile);
467-
}
558+
useGlobalCUSettings = true;
559+
LOG.debug("global compilation unit settings for: '{}'", currentContextFile);
468560
}
469561

470562
if (compilationUnitSettings != null) {
@@ -478,54 +570,32 @@ public void init() {
478570
// Treat all global defines as high prio
479571
getMacros().setHighPrio(true);
480572

481-
// parse the configured defines and store into the macro library
482-
for (String define : conf.getDefines()) {
483-
LOG.debug("parsing external macro to unit: '{}'", define);
484-
if (!"".equals(define)) {
485-
Macro macro = parseMacroDefinition("#define " + define);
486-
if (macro != null) {
487-
LOG.debug("storing external macro to unit: '{}'", macro);
488-
getMacros().put(macro.name, macro);
489-
}
490-
}
491-
}
492-
493-
// set standard macros
494-
// using smaller set of defines as rest is provides by compilation unit settings
495-
getMacros().putAll(Macro.UNIT_MACROS);
496-
497-
// parse the configured force includes and store into the macro library
498-
for (String include : conf.getForceIncludeFiles()) {
499-
LOG.debug("parsing force include to unit: '{}'", include);
500-
if (!"".equals(include)) {
501-
// TODO -> this needs to come from language
502-
parseIncludeLine("#include \"" + include + "\"", "sonar.cxx.forceIncludes", conf.getEncoding());
503-
}
504-
}
573+
// add macros which are predefined for each compilation unit
574+
getMacros().putAll(predefinedUnitMacros);
505575

506576
// rest of defines comes from compilation unit settings
507-
for (Map.Entry<String, String> entry : compilationUnitSettings.getDefines().entrySet()) {
508-
final String name = entry.getKey();
509-
final String body = entry.getValue();
510-
getMacros().put(name, new Macro(name, body));
577+
if (useGlobalCUSettings) {
578+
getMacros().putAll(globalUnitMacros);
579+
} else {
580+
getMacros().putAll(parseMacroDefinitions(compilationUnitSettings.getDefines()));
511581
}
512582
} finally {
513583
getMacros().setHighPrio(false);
514584
}
515585

516-
if (getMacro(CPLUSPLUS) == null) {
586+
if (getMacro(Macro.CPLUSPLUS) == null) {
517587
//Create macros to replace C++ keywords when parsing C files
518588
getMacros().putAll(Macro.COMPATIBILITY_MACROS);
519589
}
520590
} else {
521591
// Use global settings
522592
LOG.debug("global settings for: '{}'", currentContextFile);
523-
if (isCFile(currentContextFile.getAbsolutePath())) {
593+
if (isCFile(currentContextFile.getName())) {
524594
//Create macros to replace C++ keywords when parsing C files
525595
getMacros().putAll(Macro.COMPATIBILITY_MACROS);
526-
fixedMacros.disable(CPLUSPLUS);
596+
getMacros().disable(Macro.CPLUSPLUS);
527597
} else {
528-
fixedMacros.enable(CPLUSPLUS);
598+
getMacros().enable(Macro.CPLUSPLUS);
529599
}
530600
}
531601
}
@@ -710,6 +780,21 @@ private int expandFunctionLikeMacro(String macroName, List<Token> restTokens, Li
710780
return tokensConsumedMatchingArgs;
711781
}
712782

783+
/**
784+
* Parse the configured forced includes and store into the macro library.
785+
* Current macro library depends on the return value of
786+
* CxxPreprocessor#getMacros()
787+
*/
788+
private void parseForcedIncludes() {
789+
for (String include : conf.getForceIncludeFiles()) {
790+
if (!include.isEmpty()) {
791+
LOG.debug("parsing force include: '{}'", include);
792+
parseIncludeLine("#include \"" + include + "\"", "sonar." + this.language.getPropertiesKey() + ".forceIncludes",
793+
conf.getEncoding());
794+
}
795+
}
796+
}
797+
713798
private List<Token> expandMacro(String macroName, String macroExpression) {
714799
// C++ standard 16.3.4/2 Macro Replacement - Rescanning and further replacement
715800
List<Token> tokens = null;
@@ -855,6 +940,42 @@ private Macro parseMacroDefinition(String macroDef) {
855940
.getFirstDescendant(CppGrammar.defineLine));
856941
}
857942

943+
/**
944+
* Parse defines spited into key-value format
945+
* (sonar.cxx.jsonCompilationDatabase)
946+
*/
947+
private Map<String, Macro> parseMacroDefinitions(Map<String, String> defines) {
948+
final List<String> margedDefines = defines.entrySet().stream().map(e -> e.getKey() + " " + e.getValue())
949+
.collect(Collectors.toList());
950+
return parseMacroDefinitions(margedDefines);
951+
}
952+
953+
/**
954+
* Parse defines, which are merged into one string (see sonar.cxx.defines)
955+
*/
956+
private Map<String, Macro> parseMacroDefinitions(List<String> defines) {
957+
final Map<String, Macro> result = new HashMap<>();
958+
959+
for (String define : defines) {
960+
if (define.isEmpty()) {
961+
continue;
962+
}
963+
964+
final String defineString = "#define " + define;
965+
966+
LOG.debug("parsing external macro: '{}'", defineString);
967+
final Macro macro = parseMacroDefinition(defineString);
968+
969+
if (macro != null) {
970+
if (LOG.isDebugEnabled()) {
971+
LOG.debug("storing external macro: '{}'", macro);
972+
}
973+
result.put(macro.name, macro);
974+
}
975+
}
976+
return result;
977+
}
978+
858979
private File findIncludedFile(AstNode ast, Token token, String currFileName) {
859980
String includedFileName = null;
860981
boolean quoted = false;

cxx-squid/src/main/java/org/sonar/cxx/preprocessor/Macro.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public Macro(String name, @Nullable List<Token> params, @Nullable List<Token> bo
6161
* @param name
6262
* @param body
6363
*/
64-
public Macro(String name, String body) {
64+
private Macro(String name, String body) {
6565
this.name = name;
6666
this.params = null;
6767
this.body = Collections.singletonList(Token.builder()
@@ -96,6 +96,8 @@ private static void add(Map<String, Macro> map, String name, String body) {
9696
map.put(name, new Macro(name, body));
9797
}
9898

99+
public static final String CPLUSPLUS = "__cplusplus";
100+
99101
/**
100102
* This is a collection of standard macros according to
101103
* http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
@@ -111,7 +113,7 @@ private static void add(Map<String, Macro> map, String name, String body) {
111113
add(STANDARD_MACROS_IMPL, "__TIME__", "\"??:??:??\"");
112114
add(STANDARD_MACROS_IMPL, "__STDC__", "1");
113115
add(STANDARD_MACROS_IMPL, "__STDC_HOSTED__", "1");
114-
add(STANDARD_MACROS_IMPL, "__cplusplus", "201103L");
116+
add(STANDARD_MACROS_IMPL, CPLUSPLUS, "201103L");
115117
// __has_include support (C++17)
116118
add(STANDARD_MACROS_IMPL, "__has_include", "1");
117119
}

cxx-squid/src/main/java/org/sonar/cxx/preprocessor/MapChain.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
package org.sonar.cxx.preprocessor;
2121

22+
import java.util.Collections;
2223
import java.util.HashMap;
2324
import java.util.Map;
2425

@@ -120,4 +121,8 @@ private void move(K key, Map<K, V> from, Map<K, V> to) {
120121
}
121122
}
122123

124+
public Map<K, V> getHighPrioMap() {
125+
return Collections.unmodifiableMap(highPrioMap);
126+
}
127+
123128
}

0 commit comments

Comments
 (0)