/// <summary> /// Checks whether macros in the given source file are matched /// </summary> /// <param name="SourceFile"></param> /// <param name="IdentifierToIndex">Map of macro identifier to bit index. The complement of an index is used to indicate the end of the pair.</param> /// <param name="LogLock">Object used to marshal access to the global log instance</param> void CheckSourceFile(FileReference SourceFile, Dictionary <string, int> IdentifierToIndex, object LogLock) { // Read the text string Text = FileReference.ReadAllText(SourceFile); // Scan through the file token by token. Each bit in the Flags array indicates an index into the MacroPairs array that is currently active. int Flags = 0; for (int Idx = 0; Idx < Text.Length;) { int StartIdx = Idx++; if ((Text[StartIdx] >= 'a' && Text[StartIdx] <= 'z') || (Text[StartIdx] >= 'A' && Text[StartIdx] <= 'Z') || Text[StartIdx] == '_') { // Identifier while (Idx < Text.Length && ((Text[Idx] >= 'a' && Text[Idx] <= 'z') || (Text[Idx] >= 'A' && Text[Idx] <= 'Z') || (Text[Idx] >= '0' && Text[Idx] <= '9') || Text[Idx] == '_')) { Idx++; } // Extract the identifier string Identifier = Text.Substring(StartIdx, Idx - StartIdx); // Find the matching flag int Index; if (IdentifierToIndex.TryGetValue(Identifier, out Index)) { if (Index >= 0) { // Set the flag (should not already be set) int Flag = 1 << Index; if ((Flags & Flag) != 0) { Tools.DotNETCommon.Log.TraceWarning(SourceFile, GetLineNumber(Text, StartIdx), "{0} macro appears a second time without matching {1} macro", Identifier, MacroPairs[Index, 1]); } Flags |= Flag; } else { // Clear the flag (should already be set) int Flag = 1 << ~Index; if ((Flags & Flag) == 0) { Tools.DotNETCommon.Log.TraceWarning(SourceFile, GetLineNumber(Text, StartIdx), "{0} macro appears without matching {1} macro", Identifier, MacroPairs[~Index, 0]); } Flags &= ~Flag; } } } else if (Text[StartIdx] == '/' && Idx < Text.Length) { if (Text[Idx] == '/') { // Single-line comment while (Idx < Text.Length && Text[Idx] != '\n') { Idx++; } } else if (Text[Idx] == '*') { // Multi-line comment Idx++; for (; Idx < Text.Length; Idx++) { if (Idx + 2 < Text.Length && Text[Idx] == '*' && Text[Idx + 1] == '/') { Idx += 2; break; } } } } else if (Text[StartIdx] == '"' || Text[StartIdx] == '\'') { // String for (; Idx < Text.Length; Idx++) { if (Text[Idx] == Text[StartIdx]) { Idx++; break; } if (Text[Idx] == '\\') { Idx++; } } } else if (Text[StartIdx] == '#') { // Preprocessor directive (eg. #define) for (; Idx < Text.Length && Text[Idx] != '\n'; Idx++) { if (Text[Idx] == '\\') { Idx++; } } } } // Check if there's anything left over if (Flags != 0) { for (int Idx = 0; Idx < MacroPairs.GetLength(0); Idx++) { if ((Flags & (1 << Idx)) != 0) { Tools.DotNETCommon.Log.TraceWarning(SourceFile, "{0} macro does not have matching {1} macro", MacroPairs[Idx, 0], MacroPairs[Idx, 1]); } } } }