Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1498b9a9 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Collect parsing errors to report them at the end" into main

parents a14b7f9f 6dc8ba48
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -24,5 +24,5 @@ interface ProtoLogCallProcessor {
        logCallVisitor: ProtoLogCallVisitor?,
        otherCallVisitor: MethodCallVisitor?,
        fileName: String
    ): CompilationUnit
    ): Collection<CodeProcessingException>
}
+42 −29
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@ class ProtoLogCallProcessorImpl(
    }

    fun process(code: CompilationUnit, logCallVisitor: ProtoLogCallVisitor?, fileName: String):
            CompilationUnit {
            Collection<CodeProcessingException> {
        return process(code, logCallVisitor, null, fileName)
    }

@@ -83,7 +83,7 @@ class ProtoLogCallProcessorImpl(
        logCallVisitor: ProtoLogCallVisitor?,
        otherCallVisitor: MethodCallVisitor?,
        fileName: String
    ): CompilationUnit {
    ): Collection<CodeProcessingException> {
        CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName)
        CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName)

@@ -93,50 +93,63 @@ class ProtoLogCallProcessorImpl(
                protoLogGroupClassName)
        val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName)

        val errors = mutableListOf<CodeProcessingException>()
        code.findAll(MethodCallExpr::class.java)
                .filter { call ->
                    isProtoCall(call, isLogClassImported, staticLogImports)
                }.forEach { call ->
                    val context = ParsingContext(fileName, call)

                    try {
                        val logMethods = LogLevel.entries.map { it.shortCode }
                        if (logMethods.contains(call.name.id)) {
                            // Process a log call
                            if (call.arguments.size < 2) {
                            throw InvalidProtoLogCallException("Method signature does not match " +
                                    "any ProtoLog method: $call", context)
                                errors.add(InvalidProtoLogCallException(
                                    "Method signature does not match " +
                                            "any ProtoLog method: $call", context
                                ))
                                return@forEach
                            }

                        val messageString = CodeUtils.concatMultilineString(call.getArgument(1),
                            context)
                            val messageString = CodeUtils.concatMultilineString(
                                call.getArgument(1),
                                context
                            )
                            val groupNameArg = call.getArgument(0)
                            val groupName =
                            getLogGroupName(groupNameArg, isGroupClassImported,
                                staticGroupImports, fileName)
                                getLogGroupName(
                                    groupNameArg, isGroupClassImported,
                                    staticGroupImports, fileName
                                )
                            if (groupName !in groupMap) {
                            throw InvalidProtoLogCallException("Unknown group argument " +
                                    "- not a ProtoLogGroup enum member: $call", context)
                                errors.add(InvalidProtoLogCallException(
                                    "Unknown group argument " +
                                            "- not a ProtoLogGroup enum member: $call", context
                                ))
                                return@forEach
                            }

                        try {
                            logCallVisitor?.processCall(
                                call, messageString, getLevelForMethodName(
                                    call.name.toString(), call, context
                                ), groupMap.getValue(groupName),
                                context.lineNumber
                            )
                        } catch (e: Throwable) {
                            throw InvalidProtoLogCallException("Error processing log call: $call",
                                context, e)
                        }
                        } else if (call.name.id == "init") {
                            // No processing
                        } else {
                            // Process non-log message calls
                            otherCallVisitor?.processCall(call)
                        }
                    } catch (e: Throwable) {
                        errors.add(InvalidProtoLogCallException(
                            "Error processing log call: $call",
                            context, e
                        ))
                    }
                }
        return code
        return errors
    }

    private fun getLevelForMethodName(
+19 −6
Original line number Diff line number Diff line
@@ -130,14 +130,16 @@ object ProtoLogTool {
                    val outSrc = try {
                        val code = tryParse(text, path)
                        if (containsProtoLogText(text, PROTOLOG_CLASS_NAME)) {
                            transformer.processClass(text, path, packagePath(file, code), code)
                            val (processedText, errors) = transformer.processClass(text, path, packagePath(file, code), code)
                            errors.forEach { injector.reportProcessingError(it) }
                            processedText
                        } else {
                            text
                        }
                    } catch (ex: ParsingException) {
                        // If we cannot parse this file, skip it (and log why). Compilation will
                        // fail in a subsequent build step.
                        injector.reportParseError(ex)
                        injector.reportProcessingError(ex)
                        text
                    }
                    path to outSrc
@@ -405,7 +407,7 @@ object ProtoLogTool {
                        } catch (ex: ParsingException) {
                            // If we cannot parse this file, skip it (and log why). Compilation will
                            // fail in a subsequent build step.
                            injector.reportParseError(ex)
                            injector.reportProcessingError(ex)
                            null
                        }
                    } else {
@@ -466,6 +468,14 @@ object ProtoLogTool {
        try {
            val command = CommandOptions(args)
            invoke(command)

            if (injector.processingErrors.isNotEmpty()) {
                injector.processingErrors.forEachIndexed { index, it ->
                    println("CodeProcessingException " +
                            "(${index + 1}/${injector.processingErrors.size}): \n${it.message}\n")
                }
                exitProcess(1)
            }
        } catch (ex: InvalidCommandException) {
            println("InvalidCommandException: \n${ex.message}\n")
            showHelpAndExit()
@@ -489,12 +499,14 @@ object ProtoLogTool {
    }

    var injector = object : Injector {
        override val processingErrors: MutableList<CodeProcessingException>
            get() = mutableListOf()
        override fun fileOutputStream(file: String) = FileOutputStream(file)
        override fun readText(file: File) = file.readText()
        override fun readLogGroups(jarPath: String, className: String) =
                ProtoLogGroupReader().loadFromJar(jarPath, className)
        override fun reportParseError(ex: ParsingException) {
            println("\n${ex.message}\n")
        override fun reportProcessingError(ex: CodeProcessingException) {
            processingErrors.add(ex)
        }
    }

@@ -502,7 +514,8 @@ object ProtoLogTool {
        fun fileOutputStream(file: String): OutputStream
        fun readText(file: File): String
        fun readLogGroups(jarPath: String, className: String): Map<String, LogGroup>
        fun reportParseError(ex: ParsingException)
        fun reportProcessingError(ex: CodeProcessingException)
        val processingErrors: Collection<CodeProcessingException>
    }
}

+3 −3
Original line number Diff line number Diff line
@@ -66,13 +66,13 @@ class SourceTransformer(
        packagePath: String,
        compilationUnit: CompilationUnit =
            StaticJavaParser.parse(code)
    ): String {
    ): Pair<String, Collection<CodeProcessingException>> {
        this.path = path
        this.packagePath = packagePath
        processedCode = code.split('\n').toMutableList()
        offsets = IntArray(processedCode.size)
        protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path)
        return processedCode.joinToString("\n")
        val processingErrors = protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path)
        return Pair(processedCode.joinToString("\n"), processingErrors)
    }

    private val protoLogImplClassNode =
+42 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.protolog.tool

import com.android.protolog.tool.ProtoLogTool.PROTOLOG_IMPL_SRC_PATH
import com.android.protolog.tool.ProtoLogTool.injector
import com.google.common.truth.Truth
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@@ -102,6 +103,42 @@ class EndToEndTest {
        """.trimIndent())
    }

    @Test
    fun e2e_transform_withErrors() {
        val srcs = mapOf(
            "frameworks/base/org/example/Example.java" to """
                    package org.example;
                    import com.android.internal.protolog.ProtoLog;
                    import static com.android.internal.protolog.ProtoLogGroup.GROUP;

                    class Example {
                        void method() {
                            String argString = "hello";
                            int argInt = 123;
                            ProtoLog.d(GROUP, "Invalid format: %s %d %9 %", argString, argInt);
                        }
                    }
                """.trimIndent())
        val output = run(
            srcs = srcs,
            logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
            commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
                "--protolog-class", "com.android.internal.protolog.ProtoLog",
                "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
                "--loggroups-jar", "not_required.jar",
                "--viewer-config-file-path", "not_required.pb",
                "--output-srcjar", "out.srcjar",
                "frameworks/base/org/example/Example.java"))
        )
        val outSrcJar = assertLoadSrcJar(output, "out.srcjar")
        // No change to source code on failure to process
        Truth.assertThat(outSrcJar["frameworks/base/org/example/Example.java"])
            .contains(srcs["frameworks/base/org/example/Example.java"])

        Truth.assertThat(injector.processingErrors).hasSize(1)
        Truth.assertThat(injector.processingErrors.first().message).contains("Invalid format")
    }

    private fun assertLoadSrcJar(
        outputs: Map<String, ByteArray>,
        path: String
@@ -172,7 +209,11 @@ class EndToEndTest {
            override fun readLogGroups(jarPath: String, className: String) = mapOf(
                    logGroup.name to logGroup)

            override fun reportParseError(ex: ParsingException) = throw AssertionError(ex)
            override fun reportProcessingError(ex: CodeProcessingException) {
                processingErrors.add(ex)
            }

            override val processingErrors: MutableList<CodeProcessingException> = mutableListOf()
        }

        ProtoLogTool.invoke(commandOptions)
Loading