public ILCompilerTestCaseResult(TestCase testCase, NPath inputAssemblyPath, NPath expectationsAssemblyPath, TestCaseSandbox sandbox, TestCaseMetadataProvider metadataProvider, ManagedCompilationResult compilationResult, TestLogWriter logWriter)
 {
     TestCase                 = testCase;
     InputAssemblyPath        = inputAssemblyPath;
     ExpectationsAssemblyPath = expectationsAssemblyPath;
     Sandbox           = sandbox;
     MetadataProvider  = metadataProvider;
     CompilationResult = compilationResult;
     LogWriter         = logWriter;
 }
Beispiel #2
0
        private ILCompilerTestCaseResult Link(TestCase testCase, TestCaseSandbox sandbox, ManagedCompilationResult compilationResult, TestCaseMetadataProvider metadataProvider)
        {
            var trimmer = _factory.CreateTrimmer();

            var builder = _factory.CreateTrimmerOptionsBuilder(metadataProvider);

            AddLinkOptions(sandbox, compilationResult, builder, metadataProvider);

            var logWriter = new TestLogWriter();

            trimmer.Trim(builder.Options, logWriter);

            return(new ILCompilerTestCaseResult(testCase, compilationResult.InputAssemblyPath, compilationResult.ExpectationsAssemblyPath, sandbox, metadataProvider, compilationResult, logWriter));
        }
Beispiel #3
0
        void VerifyLoggedMessages(AssemblyDefinition original, TestLogWriter logger, bool checkRemainingErrors)
        {
            List <MessageContainer> loggedMessages = logger.GetLoggedMessages();
            List <(IMemberDefinition, CustomAttribute)> expectedNoWarningsAttributes = new List <(IMemberDefinition, CustomAttribute)> ();

            foreach (var attrProvider in GetAttributeProviders(original))
            {
                if (attrProvider.ToString() is String mystring && mystring.Contains("RequiresInCompilerGeneratedCode/SuppressInLambda"))
                {
                    Debug.WriteLine("Print");
                }
                foreach (var attr in attrProvider.CustomAttributes)
                {
                    if (!IsProducedByNativeAOT(attr))
                    {
                        continue;
                    }

                    switch (attr.AttributeType.Name)
                    {
                    case nameof(LogContainsAttribute): {
                        var expectedMessage = (string)attr.ConstructorArguments[0].Value;

                        List <MessageContainer> matchedMessages;
                        if ((bool)attr.ConstructorArguments[1].Value)
                        {
                            matchedMessages = loggedMessages.Where(m => Regex.IsMatch(m.ToString(), expectedMessage)).ToList();
                        }
                        else
                        {
                            matchedMessages = loggedMessages.Where(m => MessageTextContains(m.ToString(), expectedMessage)).ToList();
                        };
                        Assert.True(
                            matchedMessages.Count > 0,
                            $"Expected to find logged message matching `{expectedMessage}`, but no such message was found.{Environment.NewLine}Logged messages:{Environment.NewLine}{string.Join (Environment.NewLine, loggedMessages)}");

                        foreach (var matchedMessage in matchedMessages)
                        {
                            loggedMessages.Remove(matchedMessage);
                        }
                    }
                    break;

                    case nameof(LogDoesNotContainAttribute): {
                        var unexpectedMessage = (string)attr.ConstructorArguments[0].Value;
                        foreach (var loggedMessage in loggedMessages)
                        {
                            var isLogged = () => {
                                if ((bool)attr.ConstructorArguments[1].Value)
                                {
                                    return(!Regex.IsMatch(loggedMessage.ToString(), unexpectedMessage));
                                }
                                return(!MessageTextContains(loggedMessage.ToString(), unexpectedMessage));
                            };

                            Assert.True(
                                isLogged(),
                                $"Expected to not find logged message matching `{unexpectedMessage}`, but found:{Environment.NewLine}{loggedMessage.ToString ()}{Environment.NewLine}Logged messages:{Environment.NewLine}{string.Join (Environment.NewLine, loggedMessages)}");
                        }
                    }
                    break;

                    case nameof(ExpectedWarningAttribute): {
                        var expectedWarningCode = (string)attr.GetConstructorArgumentValue(0);
                        if (!expectedWarningCode.StartsWith("IL"))
                        {
                            Assert.Fail($"The warning code specified in {nameof (ExpectedWarningAttribute)} must start with the 'IL' prefix. Specified value: '{expectedWarningCode}'.");
                        }
                        var    expectedMessageContains = ((CustomAttributeArgument[])attr.GetConstructorArgumentValue(1)).Select(a => (string)a.Value).ToArray();
                        string fileName                = (string)attr.GetPropertyValue("FileName") !;
                        int?   sourceLine              = (int?)attr.GetPropertyValue("SourceLine");
                        int?   sourceColumn            = (int?)attr.GetPropertyValue("SourceColumn");
                        bool?  isCompilerGeneratedCode = (bool?)attr.GetPropertyValue("CompilerGeneratedCode");

                        int    expectedWarningCodeNumber = int.Parse(expectedWarningCode.Substring(2));
                        string?expectedOrigin            = null;
                        bool   expectedWarningFound      = false;

                        foreach (var loggedMessage in loggedMessages)
                        {
                            if (loggedMessage.ToString().Contains("RequiresInCompilerGeneratedCode.SuppressInLambda"))
                            {
                                Debug.WriteLine("Print 2");
                            }

                            if (loggedMessage.Category != MessageCategory.Warning || loggedMessage.Code != expectedWarningCodeNumber)
                            {
                                continue;
                            }

                            bool messageNotFound = false;
                            foreach (var expectedMessage in expectedMessageContains)
                            {
                                if (!MessageTextContains(loggedMessage.Text, expectedMessage))
                                {
                                    messageNotFound = true;
                                    break;
                                }
                            }
                            if (messageNotFound)
                            {
                                continue;
                            }

                            if (fileName != null)
                            {
                                if (loggedMessage.Origin == null)
                                {
                                    continue;
                                }

                                var actualOrigin = loggedMessage.Origin.Value;
                                if (actualOrigin.FileName != null)
                                {
                                    // Note: string.Compare(string, StringComparison) doesn't exist in .NET Framework API set
                                    if (actualOrigin.FileName.IndexOf(fileName, StringComparison.OrdinalIgnoreCase) < 0)
                                    {
                                        continue;
                                    }

                                    if (sourceLine != null && loggedMessage.Origin?.SourceLine != sourceLine.Value)
                                    {
                                        continue;
                                    }

                                    if (sourceColumn != null && loggedMessage.Origin?.SourceColumn != sourceColumn.Value)
                                    {
                                        continue;
                                    }
                                }
                                else
                                {
                                    // The warning was logged with member/ILoffset, so it didn't have line/column info filled
                                    // but it will be computed from PDBs, so instead compare it in a string representation
                                    if (expectedOrigin == null)
                                    {
                                        expectedOrigin = fileName;
                                        if (sourceLine.HasValue)
                                        {
                                            expectedOrigin += "(" + sourceLine.Value;
                                            if (sourceColumn.HasValue)
                                            {
                                                expectedOrigin += "," + sourceColumn.Value;
                                            }
                                            expectedOrigin += ")";
                                        }
                                    }

                                    string actualOriginString = actualOrigin.ToString() ?? "";
                                    if (!actualOriginString.EndsWith(expectedOrigin, StringComparison.OrdinalIgnoreCase))
                                    {
                                        continue;
                                    }
                                }
                            }
                            else if (isCompilerGeneratedCode == true)
                            {
                                if (loggedMessage.Origin?.MemberDefinition is MethodDesc methodDesc)
                                {
                                    if (attrProvider is not IMemberDefinition expectedMember)
                                    {
                                        continue;
                                    }

                                    string actualName = methodDesc.OwningType.ToString().Replace("+", ".") + "." + methodDesc.Name;
                                    if (actualName.Contains(expectedMember.DeclaringType.FullName.Replace("/", ".")) &&
                                        actualName.Contains("<" + expectedMember.Name + ">"))
                                    {
                                        expectedWarningFound = true;
                                        loggedMessages.Remove(loggedMessage);
                                        break;
                                    }
                                    if (actualName.StartsWith(expectedMember.DeclaringType.FullName) &&
                                        actualName.Contains(".cctor") && (expectedMember is FieldDefinition || expectedMember is PropertyDefinition))
                                    {
                                        expectedWarningFound = true;
                                        loggedMessages.Remove(loggedMessage);
                                        break;
                                    }
                                    if (methodDesc.Name == ".ctor" &&
                                        methodDesc.OwningType.ToString() == expectedMember.FullName)
                                    {
                                        expectedWarningFound = true;
                                        loggedMessages.Remove(loggedMessage);
                                        break;
                                    }
                                }
                                continue;
                            }
                            else
                            {
                                if (LogMessageHasSameOriginMember(loggedMessage, attrProvider))
                                {
                                    expectedWarningFound = true;
                                    loggedMessages.Remove(loggedMessage);
                                    break;
                                }
                                continue;
                            }

                            expectedWarningFound = true;
                            loggedMessages.Remove(loggedMessage);
                            break;
                        }

                        var expectedOriginString = fileName == null
                                                                ? GetExpectedOriginDisplayName(attrProvider) + ": "
                                                                : "";

                        Assert.True(expectedWarningFound,
                                    $"Expected to find warning: {(fileName != null ? fileName + (sourceLine != null ? $"({sourceLine},{sourceColumn})" : "") + ": " : "")}" +
                                    $"warning {expectedWarningCode}: {expectedOriginString}" +
                                    $"and message containing {string.Join (" ", expectedMessageContains.Select (m => "'" + m + "'"))}, " +
                                    $"but no such message was found.{Environment.NewLine}Logged messages:{Environment.NewLine}{string.Join (Environment.NewLine, loggedMessages)}");
                    }
                    break;

                    case nameof(ExpectedNoWarningsAttribute):
                        // Postpone processing of negative checks, to make it possible to mark some warnings as expected (will be removed from the list above)
                        // and then do the negative check on the rest.
                        var memberDefinition = attrProvider as IMemberDefinition;
                        Assert.NotNull(memberDefinition);
                        expectedNoWarningsAttributes.Add((memberDefinition, attr));
                        break;
                    }
                }
            }

            foreach ((var attrProvider, var attr) in expectedNoWarningsAttributes)
            {
                var unexpectedWarningCode = attr.ConstructorArguments.Count == 0 ? null : (string)attr.GetConstructorArgumentValue(0);
                if (unexpectedWarningCode != null && !unexpectedWarningCode.StartsWith("IL"))
                {
                    Assert.Fail($"The warning code specified in ExpectedNoWarnings attribute must start with the 'IL' prefix. Specified value: '{unexpectedWarningCode}'.");
                }

                int?unexpectedWarningCodeNumber = unexpectedWarningCode == null ? null : int.Parse(unexpectedWarningCode.Substring(2));

                MessageContainer?unexpectedWarningMessage = null;
                foreach (var mc in logger.GetLoggedMessages())
                {
                    if (mc.Category != MessageCategory.Warning)
                    {
                        continue;
                    }

                    if (unexpectedWarningCodeNumber != null && unexpectedWarningCodeNumber.Value != mc.Code)
                    {
                        continue;
                    }

                    // This is a hacky way to say anything in the "subtree" of the attrProvider
                    if ((mc.Origin?.MemberDefinition is TypeSystemEntity member) && member.ToString()?.Contains(attrProvider.FullName) != true)
                    {
                        continue;
                    }

                    unexpectedWarningMessage = mc;
                    break;
                }

                Assert.False(unexpectedWarningMessage.HasValue,
                             $"Unexpected warning found: {unexpectedWarningMessage}");
            }

            if (checkRemainingErrors)
            {
                var remainingErrors = loggedMessages.Where(m => Regex.IsMatch(m.ToString(), @".*(error | warning): \d{4}.*"));
                Assert.False(remainingErrors.Any(), $"Found unexpected errors:{Environment.NewLine}{string.Join (Environment.NewLine, remainingErrors)}");
            }

            bool LogMessageHasSameOriginMember(MessageContainer mc, ICustomAttributeProvider expectedOriginProvider)
            {
                var origin = mc.Origin;

                Debug.Assert(origin != null);
                if (GetActualOriginDisplayName(origin?.MemberDefinition) == ConvertSignatureToIlcFormat(GetExpectedOriginDisplayName(expectedOriginProvider)))
                {
                    return(true);
                }

                var actualMember = origin !.Value.MemberDefinition;

                // Compensate for cases where for some reason the OM doesn't preserve the declaring types
                // on certain things after trimming.
                if (actualMember != null && GetOwningType(actualMember) == null &&
                    GetMemberName(actualMember) == (expectedOriginProvider as IMemberDefinition)?.Name)
                {
                    return(true);
                }

                return(false);
            }