3535import com .sonar .sslr .impl .Parser ;
3636import java .io .File ;
3737import java .io .IOException ;
38- import java .net .URI ;
39- import java .net .URISyntaxException ;
4038import java .nio .charset .Charset ;
41- import java .nio .file .FileSystemNotFoundException ;
42- import java .nio .file .Paths ;
4339import java .util .ArrayList ;
4440import java .util .Collection ;
4541import java .util .Collections ;
4945import java .util .LinkedList ;
5046import java .util .List ;
5147import java .util .Map ;
48+ import java .util .Objects ;
5249import java .util .Set ;
5350import java .util .stream .Collectors ;
5451import javax .annotation .Nullable ;
@@ -93,7 +90,6 @@ public class CxxPreprocessor extends Preprocessor {
9390
9491 private final CxxLanguage language ;
9592 private File currentContextFile ;
96- private String rootFilePath ;
9793
9894 private final Parser <Grammar > pplineParser ;
9995 private final MapChain <String , Macro > fixedMacros = new MapChain <>();
@@ -107,6 +103,7 @@ public class CxxPreprocessor extends Preprocessor {
107103 private CxxCompilationUnitSettings compilationUnitSettings ;
108104 private final Multimap <String , Include > includedFiles = HashMultimap .create ();
109105 private final Multimap <String , Include > missingIncludeFiles = HashMultimap .create ();
106+ private boolean ctorInProgress = true ;
110107
111108 private State currentFileState = new State (null );
112109 private final Deque <State > globalStateStack = new LinkedList <>();
@@ -159,6 +156,7 @@ public CxxPreprocessor(SquidAstVisitorContext<Grammar> context,
159156 }
160157 } finally {
161158 getMacros ().setHighPrio (false );
159+ ctorInProgress = false ;
162160 }
163161 }
164162
@@ -456,11 +454,19 @@ public PreprocessorAction process(List<Token> tokens) { //TODO: deprecated Prepr
456454 Token token = tokens .get (0 );
457455 TokenType ttype = token .getType ();
458456
459- File file = getFileUnderAnalysis ();
460- rootFilePath = file == null ? token .getURI ().toString () : file .getAbsolutePath ();
457+ final String rootFilePath = getFileUnderAnalysis ().getAbsolutePath ();
461458
462- if (context .getFile () != currentContextFile ) {
459+ // CxxPreprocessor::process() can be called a) while construction,
460+ // b) for a new "physical" file or c) for #include directive.
461+ // Make sure, that the following code is executed for a new "physical" file
462+ // only.
463+ final boolean processingNewSourceFile = !ctorInProgress && (context .getFile () != currentContextFile );
464+
465+ if (processingNewSourceFile ) {
463466 currentContextFile = context .getFile ();
467+ // In case "physical" file is preprocessed, SquidAstVisitorContext::getFile() cannot return null
468+ // Did you forget to setup the mock properly?
469+ Objects .requireNonNull (context .getFile (), "SquidAstVisitorContext::getFile() must be non-null" );
464470 compilationUnitSettings = conf .getCompilationUnitSettings (currentContextFile .getAbsolutePath ());
465471
466472 if (compilationUnitSettings != null ) {
@@ -640,9 +646,8 @@ public String expandFunctionLikeMacro(String macroName, List<Token> restTokens)
640646 }
641647
642648 public Boolean expandHasIncludeExpression (AstNode exprAst ) {
643- File file = getFileUnderAnalysis ();
644- String filePath = file == null ? rootFilePath : file .getAbsolutePath ();
645- return findIncludedFile (exprAst , exprAst .getToken (), filePath ) != null ;
649+ final File file = getFileUnderAnalysis ();
650+ return findIncludedFile (exprAst , exprAst .getToken (), file .getAbsolutePath ()) != null ;
646651 }
647652
648653 private boolean isCFile (String filePath ) {
@@ -857,7 +862,6 @@ private Macro parseMacroDefinition(String macroDef) {
857862
858863 private File findIncludedFile (AstNode ast , Token token , String currFileName ) {
859864 String includedFileName = null ;
860- File includedFile = null ;
861865 boolean quoted = false ;
862866
863867 AstNode node = ast .getFirstDescendant (CppGrammar .includeBodyQuoted );
@@ -908,28 +912,34 @@ private File findIncludedFile(AstNode ast, Token token, String currFileName) {
908912 }
909913
910914 if (includedFileName != null ) {
911- File file = getFileUnderAnalysis ();
912- String dir ;
913- if (file != null ) {
914- dir = file .getParent ();
915- } else {
916- try {
917- dir = Paths .get (new URI (currFileName )).getParent ().toString ();
918- } catch (IllegalArgumentException | FileSystemNotFoundException | SecurityException | URISyntaxException e ) {
919- dir = "" ;
920- }
921- }
922- includedFile = getCodeProvider ().getSourceCodeFile (includedFileName , dir , quoted );
915+ final File file = getFileUnderAnalysis ();
916+ final String dir = file .getParent ();
917+ return getCodeProvider ().getSourceCodeFile (includedFileName , dir , quoted );
923918 }
924919
925- return includedFile ;
920+ return null ;
926921 }
927922
928923 private File getFileUnderAnalysis () {
929- if (currentFileState .includeUnderAnalysis == null ) {
930- return context .getFile ();
924+ if (ctorInProgress ) {
925+ // a) CxxPreprocessor is parsing artificial #include and #define
926+ // directives in order to initialize preprocessor with default macros
927+ // and forced includes.
928+ // This code is running in constructor of CxxPreprocessor. There is no
929+ // information about the current file. Therefore return some artificial
930+ // path under the project base directory.
931+ return new File (conf .getBaseDir (), "CxxPreprocessorCtorInProgress.cpp" ).getAbsoluteFile ();
932+ } else if (currentFileState .includeUnderAnalysis != null ) {
933+ // b) CxxPreprocessor is called recursively in order to parse the #include
934+ // directive. Return path to the included file.
935+ return currentFileState .includeUnderAnalysis ;
931936 }
932- return currentFileState .includeUnderAnalysis ;
937+
938+ // c) CxxPreprocessor is called in the ordinary mode: it is preprocessing the
939+ // file, tracked in org.sonar.squidbridge.SquidAstVisitorContext. This file cannot
940+ // be null. If it is null - you forgot to setup the test mock.
941+ Objects .requireNonNull (context .getFile (), "SquidAstVisitorContext::getFile() must be non-null" );
942+ return context .getFile ();
933943 }
934944
935945 PreprocessorAction handleIfLine (AstNode ast , Token token , String filename ) {
@@ -1068,7 +1078,7 @@ PreprocessorAction handleIncludeLine(AstNode ast, Token token, String filename,
10681078 File includedFile = findIncludedFile (ast , token , filename );
10691079
10701080 File currentFile = this .getFileUnderAnalysis ();
1071- if (currentFile != null && includedFile != null ) {
1081+ if (includedFile != null ) {
10721082 includedFiles .put (currentFile .getPath (), new Include (token .getLine (), includedFile .getAbsolutePath ()));
10731083 }
10741084
@@ -1077,9 +1087,7 @@ PreprocessorAction handleIncludeLine(AstNode ast, Token token, String filename,
10771087 if (LOG .isDebugEnabled ()) {
10781088 LOG .debug ("[" + filename + ":" + token .getLine () + "]: cannot find include file '" + token .getValue () + "'" );
10791089 }
1080- if (currentFile != null ) {
1081- missingIncludeFiles .put (currentFile .getPath (), new Include (token .getLine (), token .getValue ()));
1082- }
1090+ missingIncludeFiles .put (currentFile .getPath (), new Include (token .getLine (), token .getValue ()));
10831091 } else if (analysedFiles .add (includedFile .getAbsoluteFile ())) {
10841092 if (LOG .isTraceEnabled ()) {
10851093 LOG .trace ("[{}:{}]: processing {}, resolved to file '{}'" ,
0 commit comments