Skip to content

Commit

Permalink
preprocessor refactoring
Browse files Browse the repository at this point in the history
- support Microsoft Quoted form search (close SonarOpenCommunity#1225):
1) In the same directory as the file that contains the #include statement.
2) In the directories of the currently opened include files, in the reverse order in which they were opened. The search begins in the directory  of the parent include file and continues upward through the directories of any grandparent include files.
3) fallback to use include paths instead of local folder (same as Angle-bracket form)

- add #include unit tests
- test for compilation database settings propagation (close SonarOpenCommunity#1489)
- refactoring
  • Loading branch information
guwirth committed Dec 2, 2020
1 parent 652f5e9 commit fd28034
Show file tree
Hide file tree
Showing 14 changed files with 505 additions and 451 deletions.
538 changes: 189 additions & 349 deletions cxx-squid/src/main/java/org/sonar/cxx/preprocessor/CxxPreprocessor.java

Large diffs are not rendered by default.

50 changes: 16 additions & 34 deletions cxx-squid/src/main/java/org/sonar/cxx/preprocessor/MapChain.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,8 @@
*/
public class MapChain<K, V> {

private final Map<K, V> highPrioMap = new HashMap<>();
private final Map<K, V> lowPrioMap = new HashMap<>();
private final Map<K, V> highPrioDisabled = new HashMap<>();
private final Map<K, V> lowPrioDisabled = new HashMap<>();
private boolean isHighPrioEnabled = false;
private final Map<K, V> enabled = new HashMap<>();
private final Map<K, V> disabled = new HashMap<>();

/**
* get
Expand All @@ -45,12 +42,7 @@ public class MapChain<K, V> {
* @return V
*/
public V get(Object key) {
V value = highPrioMap.get(key);
return value != null ? value : lowPrioMap.get(key);
}

public void setHighPrio(boolean value) {
isHighPrioEnabled = value;
return enabled.get(key);
}

/**
Expand All @@ -61,37 +53,29 @@ public void setHighPrio(boolean value) {
* @return V
*/
public V put(K key, V value) {
if (isHighPrioEnabled) {
return highPrioMap.put(key, value);
} else {
return lowPrioMap.put(key, value);
}
return enabled.put(key, value);
}

public void putAll(Map<K, V> m) {
if (isHighPrioEnabled) {
highPrioMap.putAll(m);
} else {
lowPrioMap.putAll(m);
}
enabled.putAll(m);
}

/**
* removeLowPrio
* remove
*
* @param key
* @return V
*/
public V removeLowPrio(K key) {
return lowPrioMap.remove(key);
public V remove(K key) {
return enabled.remove(key);
}

/**
* clearLowPrio
* clear
*/
public void clearLowPrio() {
lowPrioMap.clear();
lowPrioDisabled.clear();
public void clear() {
enabled.clear();
disabled.clear();
}

/**
Expand All @@ -100,8 +84,7 @@ public void clearLowPrio() {
* @param key
*/
public void disable(K key) {
move(key, lowPrioMap, lowPrioDisabled);
move(key, highPrioMap, highPrioDisabled);
move(key, enabled, disabled);
}

/**
Expand All @@ -110,12 +93,11 @@ public void disable(K key) {
* @param key
*/
public void enable(K key) {
move(key, lowPrioDisabled, lowPrioMap);
move(key, highPrioDisabled, highPrioMap);
move(key, disabled, enabled);
}

public Map<K, V> getHighPrioMap() {
return Collections.unmodifiableMap(highPrioMap);
public Map<K, V> getMap() {
return Collections.unmodifiableMap(enabled);
}

private void move(K key, Map<K, V> from, Map<K, V> to) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;

Expand All @@ -40,7 +42,13 @@
public class SourceCodeProvider {

private static final Logger LOG = Loggers.get(SourceCodeProvider.class);

private final List<Path> includeRoots = new LinkedList<>();
private final Deque<State> ppState = new LinkedList<>();

public SourceCodeProvider() {
pushFileState(null);
}

public void setIncludeRoots(List<String> roots, String baseDir) {
for (var root : roots) {
Expand All @@ -63,38 +71,92 @@ public void setIncludeRoots(List<String> roots, String baseDir) {
}
}

public void pushFileState(File currentFile) {
ppState.push(new State(currentFile));
}

public void popFileState() {
ppState.pop();
}

public void skipBlock(boolean state) {
ppState.peek().skipBlock = state;
}

public boolean doSkipBlock() {
return ppState.peek().skipBlock;
}

public boolean doNotSkipBlock() {
return !ppState.peek().skipBlock;
}

public void expressionWas(boolean state) {
ppState.peek().expression = state;
}

public boolean expressionWasTrue() {
return ppState.peek().expression;
}

public boolean expressionWasFalse() {
return !ppState.peek().expression;
}

public void nestedBlock(int dir) {
ppState.peek().nestedBlock += dir;
}

public boolean isNestedBlock() {
return ppState.peek().nestedBlock > 0;
}

public boolean isNotNestedBlock() {
return ppState.peek().nestedBlock <= 0;
}

public File getIncludeUnderAnalysis() {
return ppState.peek().includeUnderAnalysis;
}

@CheckForNull
public File getSourceCodeFile(String filename, String cwd, boolean quoted) {
File result = null;
var file = new File(filename);

// If the file name is fully specified for an include file that has a path that
// includes a colon (for example, F:\MSVC\SPECIAL\INCL\TEST.H), the preprocessor
// follows the path.
// If the file name is fully specified for an include file that has a path that includes a colon
// (for example F:\MSVC\SPECIAL\INCL\TEST.H) the preprocessor follows the path.
if (file.isAbsolute()) {
if (file.isFile()) {
result = file;
}
} else {
if (quoted) {

// Quoted form: The preprocessor searches for include files in this order:
// 1) In the same directory as the file that contains the #include statement.
// 2) In the directories of the currently opened include files, in the reverse
// order in which they were opened. The search begins in the directory of the parent
// include file and continues upward through the directories of any grandparent include files.
var abspath = new File(new File(cwd), file.getPath());
if (abspath.isFile()) {
// 1) In the same directory as the file that contains the #include statement.
result = abspath;
} else {
// fall back to use include paths instead of local folder
result = null;
result = null; // 3) fallback to use include paths instead of local folder

// 2) In the directories of the currently opened include files, in the reverse order in which they were opened.
// The search begins in the directory of the parent include file and continues upward through the
// directories of any grandparent include files.
for (var parent : ppState) {
if (parent.includeUnderAnalysis != null) {
abspath = new File(parent.includeUnderAnalysis.getParentFile(), file.getPath());
if (abspath.exists()) {
result = abspath;
break;
}
}
}
}
}

// Angle-bracket form: lookup relative to to the include roots.
// The quoted case falls back to this, if its special handling wasn't
// successful.
// The quoted case falls back to this, if its special handling wasn't successful.
if (result == null) {
for (var path : includeRoots) {
Path abspath = path.resolve(filename);
Expand Down Expand Up @@ -122,4 +184,20 @@ public String getSourceCode(File file, Charset charset) throws IOException {
return new String(encoded, charset);
}

private static class State {

private boolean skipBlock;
private boolean expression;
private int nestedBlock;
private File includeUnderAnalysis;

public State(@Nullable File includeUnderAnalysis) {
this.skipBlock = false;
this.expression = false;
this.nestedBlock = 0;
this.includeUnderAnalysis = includeUnderAnalysis;
}

}

}
Loading

0 comments on commit fd28034

Please sign in to comment.